diff --git a/bob/learn/em/__init__.py b/bob/learn/em/__init__.py
index 44e6b51988b903eccb68875662a682f84bb59233..99d4c77a77bedb426a1ff588e3bd2c7b24edd79e 100644
--- a/bob/learn/em/__init__.py
+++ b/bob/learn/em/__init__.py
@@ -1,215 +1,13 @@
-# import Libraries of other lib packages
-import bob.io.base
-import bob.math
-import bob.learn.linear
-import bob.sp
+from . import cluster
+from . import mixture
 
-# import our own Library
 import bob.extension
 
-bob.extension.load_bob_library("bob.learn.em", __file__)
-
-from ._library import *
-from ._library import GMMMachine as _GMMMachine_C
-from ._library import ISVBase as _ISVBase_C
-from ._library import ISVMachine as _ISVMachine_C
-from ._library import KMeansMachine as _KMeansMachine_C
-from ._library import GMMStats as _GMMStats_C
-from ._library import IVectorMachine as _IVectorMachine_C
-
-from . import version
-from .version import module as __version__
-from .version import api as __api_version__
-from .train import *
-
-
 def get_config():
-    """Returns a string containing the configuration information.
-    """
-    return bob.extension.get_config(__name__, version.externals, version.api)
+    """Returns a string containing the configuration information."""
+    return bob.extension.get_config(__name__)
 
 
 # gets sphinx autodoc done right - don't remove it
 __all__ = [_ for _ in dir() if not _.startswith("_")]
 
-
-class GMMMachine(_GMMMachine_C):
-    __doc__ = _GMMMachine_C.__doc__
-
-    def update_dict(self, d):
-        self.means = d["means"]
-        self.variances = d["variances"]
-        self.weights = d["weights"]
-
-    @staticmethod
-    def gmm_shape_from_dict(d):
-        return d["means"].shape
-
-    @classmethod
-    def create_from_dict(cls, d):
-        shape = GMMMachine.gmm_shape_from_dict(d)
-        gmm_machine = cls(shape[0], shape[1])
-        gmm_machine.update_dict(d)
-        return gmm_machine
-
-    @staticmethod
-    def to_dict(gmm_machine):
-        gmm_data = dict()
-        gmm_data["means"] = gmm_machine.means
-        gmm_data["variances"] = gmm_machine.variances
-        gmm_data["weights"] = gmm_machine.weights
-        return gmm_data
-
-    def __getstate__(self):
-        d = dict(self.__dict__)
-        d.update(self.__class__.to_dict(self))
-        return d
-
-    def __setstate__(self, d):
-        self.__dict__ = d
-        shape = self.gmm_shape_from_dict(d)
-        self.__init__(shape[0], shape[1])
-        self.update_dict(d)
-
-
-class ISVBase(_ISVBase_C):
-    __doc__ = _ISVBase_C.__doc__
-
-    @staticmethod
-    def to_dict(isv_base):
-        isv_data = dict()
-        isv_data["gmm"] = GMMMachine.to_dict(isv_base.ubm)
-        isv_data["u"] = isv_base.u
-        isv_data["d"] = isv_base.d
-
-        return isv_data
-
-    def update_dict(self, d):
-        ubm = GMMMachine.create_from_dict(d["gmm"])
-        u = d["u"]
-        self.__init__(ubm, u.shape[1])
-        self.u = u
-        self.d = d["d"]
-
-    @classmethod
-    def create_from_dict(cls, d):
-        ubm = GMMMachine.create_from_dict(d["gmm"])
-        ru = d["u"].shape[1]
-        isv_base = ISVBase(ubm, ru)
-        isv_base.u = d["u"]
-        isv_base.d = d["d"]
-        return isv_base
-
-    def __getstate__(self):
-        d = dict(self.__dict__)
-        d.update(self.__class__.to_dict(self))
-        return d
-
-    def __setstate__(self, d):
-        self.__dict__ = d
-        self.update_dict(d)
-
-
-class ISVMachine(_ISVMachine_C):
-    __doc__ = _ISVMachine_C.__doc__
-
-    @staticmethod
-    def to_dict(isv_machine):
-        isv_data = dict()
-        isv_data["x"] = isv_machine.x
-        isv_data["z"] = isv_machine.z
-        isv_data["isv_base"] = ISVBase.to_dict(isv_machine.isv_base)
-
-        return isv_data
-
-    def update_dict(self, d):
-        isv_base = ISVBase.create_from_dict(d["isv_base"])
-        self.__init__(isv_base)
-        self.x = d["x"]
-        self.z = d["z"]
-
-    def __getstate__(self):
-        d = dict(self.__dict__)
-        d.update(self.__class__.to_dict(self))
-        return d
-
-    def __setstate__(self, d):
-        self.__dict__ = d
-        self.update_dict(d)
-
-
-class KMeansMachine(_KMeansMachine_C):
-    __doc__ = _KMeansMachine_C.__doc__
-
-    @staticmethod
-    def to_dict(kmeans_machine):
-        kmeans_data = dict()
-        kmeans_data["means"] = kmeans_machine.means
-        return kmeans_data
-
-    def __getstate__(self):
-        d = dict(self.__dict__)
-        d.update(self.__class__.to_dict(self))
-        return d
-
-    def __setstate__(self, d):
-        means = d["means"]
-        self.__init__(means.shape[0], means.shape[1])
-        self.means = means
-
-
-class GMMStats(_GMMStats_C):
-    __doc__ = _GMMStats_C.__doc__
-
-    @staticmethod
-    def to_dict(gmm_stats):
-        gmm_stats_data = dict()
-        gmm_stats_data["log_likelihood"] = gmm_stats.log_likelihood
-        gmm_stats_data["t"] = gmm_stats.t
-        gmm_stats_data["n"] = gmm_stats.n
-        gmm_stats_data["sum_px"] = gmm_stats.sum_px
-        gmm_stats_data["sum_pxx"] = gmm_stats.sum_pxx
-        return gmm_stats_data
-
-    def __getstate__(self):
-        d = dict(self.__dict__)
-        d.update(self.__class__.to_dict(self))
-        return d
-
-    def __setstate__(self, d):
-        shape = d["sum_pxx"].shape
-        self.__init__(shape[0], shape[1])
-        self.t = d["t"]
-        self.n = d["n"]
-        self.log_likelihood = d["log_likelihood"]
-        self.sum_px = d["sum_px"]
-        self.sum_pxx = d["sum_pxx"]
-
-
-class IVectorMachine(_IVectorMachine_C):
-    __doc__ = _IVectorMachine_C.__doc__
-
-    @staticmethod
-    def to_dict(ivector_machine):
-        ivector_data = dict()
-        ivector_data["gmm"] = GMMMachine.to_dict(ivector_machine.ubm)
-        ivector_data["sigma"] = ivector_machine.sigma
-        ivector_data["t"] = ivector_machine.t
-
-        return ivector_data
-
-    def update_dict(self, d):
-        ubm = GMMMachine.create_from_dict(d["gmm"])
-        t = d["t"]
-        self.__init__(ubm, t.shape[1])
-        self.sigma = d["sigma"]
-        self.t = t
-
-    def __getstate__(self):
-        d = dict(self.__dict__)
-        d.update(self.__class__.to_dict(self))
-        return d
-
-    def __setstate__(self, d):
-        self.__dict__ = d
-        self.update_dict(d)
diff --git a/bob/learn/em/cpp/EMPCATrainer.cpp b/bob/learn/em/cpp/EMPCATrainer.cpp
deleted file mode 100644
index 1217be261cb83818373990f97312ce8670f8d47f..0000000000000000000000000000000000000000
--- a/bob/learn/em/cpp/EMPCATrainer.cpp
+++ /dev/null
@@ -1,420 +0,0 @@
-/**
- * @date Tue Oct 11 12:18:23 2011 +0200
- * @author Laurent El Shafey <Laurent.El-Shafey@idiap.ch>
- *
- * Copyright (C) Idiap Research Institute, Martigny, Switzerland
- */
-
-#include <vector>
-#include <algorithm>
-#include <boost/random.hpp>
-#include <cmath>
-
-#include <bob.learn.em/EMPCATrainer.h>
-#include <bob.core/array_copy.h>
-#include <bob.core/check.h>
-#include <bob.math/linear.h>
-#include <bob.math/det.h>
-#include <bob.math/inv.h>
-#include <bob.math/pinv.h>
-#include <bob.math/stats.h>
-
-bob::learn::em::EMPCATrainer::EMPCATrainer(bool compute_likelihood):
-  m_compute_likelihood(compute_likelihood),
-  m_rng(new boost::mt19937()),
-  m_S(0,0),
-  m_z_first_order(0,0), m_z_second_order(0,0,0),
-  m_inW(0,0), m_invM(0,0), m_sigma2(0), m_f_log2pi(0),
-  m_tmp_dxf(0,0), m_tmp_d(0), m_tmp_f(0),
-  m_tmp_dxd_1(0,0), m_tmp_dxd_2(0,0),
-  m_tmp_fxd_1(0,0), m_tmp_fxd_2(0,0),
-  m_tmp_fxf_1(0,0), m_tmp_fxf_2(0,0)
-{
-}
-
-bob::learn::em::EMPCATrainer::EMPCATrainer(const bob::learn::em::EMPCATrainer& other):
-  m_compute_likelihood(other.m_compute_likelihood),
-  m_rng(other.m_rng),
-  m_S(bob::core::array::ccopy(other.m_S)),
-  m_z_first_order(bob::core::array::ccopy(other.m_z_first_order)),
-  m_z_second_order(bob::core::array::ccopy(other.m_z_second_order)),
-  m_inW(bob::core::array::ccopy(other.m_inW)),
-  m_invM(bob::core::array::ccopy(other.m_invM)),
-  m_sigma2(other.m_sigma2), m_f_log2pi(other.m_f_log2pi),
-  m_tmp_dxf(bob::core::array::ccopy(other.m_tmp_dxf)),
-  m_tmp_d(bob::core::array::ccopy(other.m_tmp_d)),
-  m_tmp_f(bob::core::array::ccopy(other.m_tmp_f)),
-  m_tmp_dxd_1(bob::core::array::ccopy(other.m_tmp_dxd_1)),
-  m_tmp_dxd_2(bob::core::array::ccopy(other.m_tmp_dxd_2)),
-  m_tmp_fxd_1(bob::core::array::ccopy(other.m_tmp_fxd_1)),
-  m_tmp_fxd_2(bob::core::array::ccopy(other.m_tmp_fxd_2)),
-  m_tmp_fxf_1(bob::core::array::ccopy(other.m_tmp_fxf_1)),
-  m_tmp_fxf_2(bob::core::array::ccopy(other.m_tmp_fxf_2))
-{
-}
-
-bob::learn::em::EMPCATrainer::~EMPCATrainer()
-{
-}
-
-bob::learn::em::EMPCATrainer& bob::learn::em::EMPCATrainer::operator=
-  (const bob::learn::em::EMPCATrainer& other)
-{
-  if (this != &other)
-  {
-    m_rng                   = other.m_rng;
-	m_compute_likelihood    = other.m_compute_likelihood;
-    m_S = bob::core::array::ccopy(other.m_S);
-    m_z_first_order = bob::core::array::ccopy(other.m_z_first_order);
-    m_z_second_order = bob::core::array::ccopy(other.m_z_second_order);
-    m_inW = bob::core::array::ccopy(other.m_inW);
-    m_invM = bob::core::array::ccopy(other.m_invM);
-    m_sigma2 = other.m_sigma2;
-    m_f_log2pi = other.m_f_log2pi;
-    m_tmp_dxf = bob::core::array::ccopy(other.m_tmp_dxf);
-    m_tmp_d = bob::core::array::ccopy(other.m_tmp_d);
-    m_tmp_f = bob::core::array::ccopy(other.m_tmp_f);
-    m_tmp_dxd_1 = bob::core::array::ccopy(other.m_tmp_dxd_1);
-    m_tmp_dxd_2 = bob::core::array::ccopy(other.m_tmp_dxd_2);
-    m_tmp_fxd_1 = bob::core::array::ccopy(other.m_tmp_fxd_1);
-    m_tmp_fxd_2 = bob::core::array::ccopy(other.m_tmp_fxd_2);
-    m_tmp_fxf_1 = bob::core::array::ccopy(other.m_tmp_fxf_1);
-    m_tmp_fxf_2 = bob::core::array::ccopy(other.m_tmp_fxf_2);
-  }
-  return *this;
-}
-
-bool bob::learn::em::EMPCATrainer::operator==
-  (const bob::learn::em::EMPCATrainer &other) const
-{
-  return m_compute_likelihood == other.m_compute_likelihood &&
-        m_rng                   == other.m_rng &&
-        bob::core::array::isEqual(m_S, other.m_S) &&
-        bob::core::array::isEqual(m_z_first_order, other.m_z_first_order) &&
-        bob::core::array::isEqual(m_z_second_order, other.m_z_second_order) &&
-        bob::core::array::isEqual(m_inW, other.m_inW) &&
-        bob::core::array::isEqual(m_invM, other.m_invM) &&
-        m_sigma2 == other.m_sigma2 &&
-        m_f_log2pi == other.m_f_log2pi;
-}
-
-bool bob::learn::em::EMPCATrainer::operator!=
-  (const bob::learn::em::EMPCATrainer &other) const
-{
-  return !(this->operator==(other));
-}
-
-bool bob::learn::em::EMPCATrainer::is_similar_to
-  (const bob::learn::em::EMPCATrainer &other, const double r_epsilon,
-   const double a_epsilon) const
-{
-  return m_compute_likelihood == other.m_compute_likelihood &&
-         m_rng                == other.m_rng &&
-         bob::core::array::isClose(m_S, other.m_S, r_epsilon, a_epsilon) &&
-         bob::core::array::isClose(m_z_first_order, other.m_z_first_order, r_epsilon, a_epsilon) &&
-         bob::core::array::isClose(m_z_second_order, other.m_z_second_order, r_epsilon, a_epsilon) &&
-         bob::core::array::isClose(m_inW, other.m_inW, r_epsilon, a_epsilon) &&
-         bob::core::array::isClose(m_invM, other.m_invM, r_epsilon, a_epsilon) &&
-         bob::core::isClose(m_sigma2, other.m_sigma2, r_epsilon, a_epsilon) &&
-         bob::core::isClose(m_f_log2pi, other.m_f_log2pi, r_epsilon, a_epsilon);
-}
-
-void bob::learn::em::EMPCATrainer::initialize(bob::learn::linear::Machine& machine,
-  const blitz::Array<double,2>& ar)
-{
-  // reinitializes array members and checks dimensionality
-  initMembers(machine, ar);
-
-  // computes the mean and the covariance if required
-  computeMeanVariance(machine, ar);
-
-  // Random initialization of W and sigma2
-  initRandomWSigma2(machine);
-
-  // Computes the product m_inW = W^T.W
-  computeWtW(machine);
-  // Computes inverse(M), where M = Wt * W + sigma2 * Id
-  computeInvM();
-}
-
-
-void bob::learn::em::EMPCATrainer::initMembers(
-  const bob::learn::linear::Machine& machine,
-  const blitz::Array<double,2>& ar)
-{
-  // Gets dimensions
-  const size_t n_samples = ar.extent(0);
-  const size_t n_features = ar.extent(1);
-
-  // Checks that the dimensions are matching
-  const size_t n_inputs = machine.inputSize();
-  const size_t n_outputs = machine.outputSize();
-
-  // Checks that the dimensions are matching
-  if (n_inputs != n_features) {
-    boost::format m("number of inputs (%u) does not match the number of features (%u)");
-    m % n_inputs % n_features;
-    throw std::runtime_error(m.str());
-  }
-
-  // Covariance matrix S is only required to compute the log likelihood
-  if (m_compute_likelihood)
-    m_S.resize(n_features,n_features);
-  else
-    m_S.resize(0,0);
-  m_z_first_order.resize(n_samples, n_outputs);
-  m_z_second_order.resize(n_samples, n_outputs, n_outputs);
-  m_inW.resize(n_outputs, n_outputs);
-  m_invM.resize(n_outputs, n_outputs);
-  m_sigma2 = 0.;
-  m_f_log2pi = n_features * log(2*M_PI);
-
-  // Cache
-  m_tmp_dxf.resize(n_outputs, n_features);
-  m_tmp_d.resize(n_outputs);
-  m_tmp_f.resize(n_features);
-  
-  m_tmp_dxd_1.resize(n_outputs, n_outputs);
-  m_tmp_dxd_2.resize(n_outputs, n_outputs);
-  m_tmp_fxd_1.resize(n_features, n_outputs);
-  m_tmp_fxd_2.resize(n_features, n_outputs);
-  // The following large cache matrices are only required to compute the
-  // log likelihood.
-  if (m_compute_likelihood)
-  {
-    m_tmp_fxf_1.resize(n_features, n_features);
-    m_tmp_fxf_2.resize(n_features, n_features);
-  }
-  else
-  {
-    m_tmp_fxf_1.resize(0,0);
-    m_tmp_fxf_2.resize(0,0);
-  }
-}
-
-void bob::learn::em::EMPCATrainer::computeMeanVariance(bob::learn::linear::Machine& machine,
-  const blitz::Array<double,2>& ar)
-{
-  size_t n_samples = ar.extent(0);
-  blitz::Array<double,1> mu = machine.updateInputSubtraction();
-  blitz::Range all = blitz::Range::all();
-  if (m_compute_likelihood)
-  {
-    // Mean and scatter computation
-    bob::math::scatter(ar, m_S, mu);
-    // divides scatter by N-1
-    m_S /= static_cast<double>(n_samples-1);
-  }
-  else
-  {
-    // computes the mean and updates mu
-    mu = 0.;
-    for (size_t i=0; i<n_samples; ++i)
-      mu += ar(i,all);
-    mu /= static_cast<double>(n_samples);
-  }
-}
-
-void bob::learn::em::EMPCATrainer::initRandomWSigma2(bob::learn::linear::Machine& machine)
-{
-  // Initializes the random number generator
-  boost::uniform_01<> range01;
-  boost::variate_generator<boost::mt19937&, boost::uniform_01<> > die(*m_rng, range01);
-
-  // W initialization (TODO: add method in core)
-  blitz::Array<double,2> W = machine.updateWeights();
-  double ratio = 2.; /// Follows matlab implementation using a ratio of 2
-  for (int i=0; i<W.extent(0); ++i)
-    for (int j=0; j<W.extent(1); ++j)
-      W(i,j) = die() * ratio;
-  // sigma2 initialization
-  m_sigma2 = die() * ratio;
-}
-
-void bob::learn::em::EMPCATrainer::computeWtW(bob::learn::linear::Machine& machine)
-{
-  const blitz::Array<double,2> W = machine.getWeights();
-  const blitz::Array<double,2> Wt = W.transpose(1,0);
-  bob::math::prod(Wt, W, m_inW);
-}
-
-void bob::learn::em::EMPCATrainer::computeInvM()
-{
-  // Compute inverse(M), where M = W^T * W + sigma2 * Id
-  bob::math::eye(m_tmp_dxd_1); // m_tmp_dxd_1 = Id
-  m_tmp_dxd_1 *= m_sigma2; // m_tmp_dxd_1 = sigma2 * Id  
-  m_tmp_dxd_1 += m_inW; // m_tmp_dxd_1 = M = W^T * W + sigma2 * Id  
-  bob::math::inv(m_tmp_dxd_1, m_invM); // m_invM = inv(M)
-}
-
-
-
-void bob::learn::em::EMPCATrainer::eStep(bob::learn::linear::Machine& machine, const blitz::Array<double,2>& ar)
-{
-  // Gets mu and W from the machine
-  const blitz::Array<double,1>& mu = machine.getInputSubtraction();
-  const blitz::Array<double,2>& W = machine.getWeights();
-  const blitz::Array<double,2> Wt = W.transpose(1,0); // W^T
-
-  // Computes the statistics
-  blitz::Range a = blitz::Range::all();
-  for(int i=0; i<ar.extent(0); ++i)
-  {
-    /// 1/ First order statistics: \f$z_first_order_i = inv(M) W^T (t - \mu)\f$
-    // m_tmp_f = t (sample) - mu (normalized sample)
-    m_tmp_f = ar(i,a) - mu;
-    // m_tmp_dxf = inv(M) * W^T
-    bob::math::prod(m_invM, Wt, m_tmp_dxf);
-    blitz::Array<double,1> z_first_order_i = m_z_first_order(i,blitz::Range::all());
-    // z_first_order_i = inv(M) * W^T * (t - mu)
-    bob::math::prod(m_tmp_dxf, m_tmp_f, z_first_order_i);
-
-    /// 2/ Second order statistics:
-    ///     z_second_order_i = sigma2 * inv(M) + z_first_order_i * z_first_order_i^T
-    blitz::Array<double,2> z_second_order_i = m_z_second_order(i,blitz::Range::all(),blitz::Range::all());
-    // m_tmp_dxd = z_first_order_i * z_first_order_i^T
-    bob::math::prod(z_first_order_i, z_first_order_i, m_tmp_dxd_1); // outer product
-    // z_second_order_i = sigma2 * inv(M)
-    z_second_order_i = m_invM;
-    z_second_order_i *= m_sigma2;
-    // z_second_order_i = sigma2 * inv(M) + z_first_order_i * z_first_order_i^T
-    z_second_order_i += m_tmp_dxd_1;
-  }
-}
-
-void bob::learn::em::EMPCATrainer::mStep(bob::learn::linear::Machine& machine, const blitz::Array<double,2>& ar)
-{
-  // 1/ New estimate of W
-  updateW(machine, ar);
-
-  // 2/ New estimate of sigma2
-  updateSigma2(machine, ar);
-
-  // Computes the new value of inverse(M), where M = Wt * W + sigma2 * Id
-  computeInvM();
-}
-
-void bob::learn::em::EMPCATrainer::updateW(bob::learn::linear::Machine& machine, const blitz::Array<double,2>& ar) {
-  // Get the mean mu and the projection matrix W
-  const blitz::Array<double,1>& mu = machine.getInputSubtraction();
-  blitz::Array<double,2>& W = machine.updateWeights();
-  const blitz::Array<double,2> Wt = W.transpose(1,0); // W^T
-
-  // Compute W = sum{ (t_{i} - mu) z_first_order_i^T} * inv( sum{z_second_order_i} )
-  m_tmp_fxd_1 = 0.;
-  m_tmp_dxd_1 = 0.;
-  blitz::Range a = blitz::Range::all();
-  for(int i=0; i<ar.extent(0); ++i)
-  {
-    // m_tmp_f = t (sample) - mu (normalized sample)
-    m_tmp_f = ar(i,a) - mu;
-    // first order statistics of sample i
-    blitz::Array<double,1> z_first_order_i = m_z_first_order(i,blitz::Range::all());
-    // m_tmp_fxd_2 = (t - mu)*z_first_order_i
-    bob::math::prod(m_tmp_f, z_first_order_i, m_tmp_fxd_2);
-    m_tmp_fxd_1 += m_tmp_fxd_2;
-
-    // second order statistics of sample i
-    blitz::Array<double,2> z_second_order_i = m_z_second_order(i,blitz::Range::all(),blitz::Range::all());
-    m_tmp_dxd_1 += z_second_order_i;
-  }
-
-  // m_tmp_dxd_2 = inv( sum(E(x_i.x_i^T)) )
-  bob::math::inv(m_tmp_dxd_1, m_tmp_dxd_2);
-  // New estimates of W
-  bob::math::prod(m_tmp_fxd_1, m_tmp_dxd_2, W);
-  // Updates W'*W as well
-  bob::math::prod(Wt, W, m_inW);
-}
-
-void bob::learn::em::EMPCATrainer::updateSigma2(bob::learn::linear::Machine& machine, const blitz::Array<double,2>& ar) {
-  // Get the mean mu and the projection matrix W
-  const blitz::Array<double,1>& mu = machine.getInputSubtraction();
-  const blitz::Array<double,2>& W = machine.getWeights();
-  const blitz::Array<double,2> Wt = W.transpose(1,0); // W^T
-
-  m_sigma2 = 0.;
-  blitz::Range a = blitz::Range::all();
-  for(int i=0; i<ar.extent(0); ++i)
-  {
-    // a. sigma2 += || t - mu ||^2
-    // m_tmp_f = t (sample) - mu (normalized sample)
-    m_tmp_f = ar(i,a) - mu;
-    // sigma2 += || t - mu ||^2
-    m_sigma2 += blitz::sum(blitz::pow2(m_tmp_f));
-
-    // b. sigma2 -= 2*E(x_i)^T*W^T*(t - mu)
-    // m_tmp_d = W^T*(t - mu)
-    bob::math::prod(Wt, m_tmp_f, m_tmp_d);
-    // first order of i
-    blitz::Array<double,1> z_first_order_i = m_z_first_order(i,blitz::Range::all());
-    // sigma2 -= 2*E(x_i)^T*W^T*(t - mu)
-    m_sigma2 -= 2*bob::math::dot(z_first_order_i, m_tmp_d);
-
-    // c. sigma2 += trace( E(x_i.x_i^T)*W^T*W )
-    // second order of i
-    blitz::Array<double,2> z_second_order_i = m_z_second_order(i,blitz::Range::all(),blitz::Range::all());
-    // m_tmp_dxd_1 = E(x_i.x_i^T)*W^T*W
-    bob::math::prod(z_second_order_i, m_inW, m_tmp_dxd_1);
-    // sigma2 += trace( E(x_i.x_i^T)*W^T*W )
-    m_sigma2 += bob::math::trace(m_tmp_dxd_1);
-  }
-  // Normalization factor
-  m_sigma2 /= (static_cast<double>(ar.extent(0)) * mu.extent(0));
-}
-
-double bob::learn::em::EMPCATrainer::computeLikelihood(bob::learn::linear::Machine& machine)
-{
-  // Get W projection matrix
-  const blitz::Array<double,2>& W = machine.getWeights();
-  const blitz::Array<double,2> Wt = W.transpose(1,0); // W^T
-  const size_t n_features = m_S.extent(0);
-
-  // 1/ Compute det(C), where C = sigma2.I + W.W^T
-  //            det(C) = det(sigma2 * C / sigma2) = det(sigma2 * Id) * det(C / sigma2)
-  //    We are using Sylvester's determinant theorem to compute a dxd
-  //    determinant rather than a fxf one: det(I + A.B) = det(I + B.A)
-  //            det(C) = sigma2^n_features * det(I + W.W^T/sigma2)
-  //                   = sigma2^n_features * det(I + W^T.W/sigma2) (cf. Bishop Appendix C)
-  // detC = det( eye(n_features) * sigma2 )
-
-  // detC = sigma2^n_features
-  double detC = pow(m_sigma2, n_features);
-  // m_tmp_dxd_1 = Id
-  bob::math::eye(m_tmp_dxd_1);
-  // m_tmp_dxd_2 = W^T.W
-  bob::math::prod(Wt, W, m_tmp_dxd_2);
-  // m_tmp_dxd_2 = W^T.W / sigma2
-  m_tmp_dxd_2 /= m_sigma2;
-  // m_tmp_dxd_1 = Id + W^T.W / sigma2
-  m_tmp_dxd_1 += m_tmp_dxd_2;
-  // detC = sigma2^n_features * det(I + W^T.W/sigma2)
-  detC *= bob::math::det(m_tmp_dxd_1);
-
-  // 2/ Compute inv(C), where C = sigma2.I + W.W^T
-  //    We are using the following identity (Property C.7 of Bishop's book)
-  //      (A + B.D^-1.C)^-1 = A^-1 - A^-1.B(D+C.A^-1.B)^-1.C.A^-1
-  //    Hence, inv(C) = sigma2^-1 .(I - W.M^-1.W^T)
-
-  // Compute inverse(M), where M = Wt * W + sigma2 * Id
-  computeInvM();
-  // m_tmp_fxf_1 = I = eye(n_features)
-  bob::math::eye(m_tmp_fxf_1);
-  // m_tmp_fxd_1 = W * inv(M)
-  bob::math::prod(W, m_invM, m_tmp_fxd_1);
-  // m_tmp_fxf_2 = (W * inv(M) * Wt)
-  bob::math::prod(m_tmp_fxd_1, Wt, m_tmp_fxf_2);
-  // m_tmp_fxd_1 = inv(C) = (I - W.M^-1.W^T) / sigma2
-  m_tmp_fxf_1 -= m_tmp_fxf_2;
-  m_tmp_fxf_1 /= m_sigma2;
-
-  // 3/ Compute inv(C).S
-  bob::math::prod(m_tmp_fxf_1, m_S, m_tmp_fxf_2);
-
-  // 4/ Use previous values to compute the log likelihood:
-  // Log likelihood =  - N/2*{ d*ln(2*PI) + ln |detC| + tr(C^-1.S) }
-  double llh = - static_cast<double>(m_z_first_order.extent(0)) / 2. *
-    ( m_f_log2pi + log(fabs(detC)) + bob::math::trace(m_tmp_fxf_2) );
-
-  return llh;
-}
diff --git a/bob/learn/em/cpp/FABase.cpp b/bob/learn/em/cpp/FABase.cpp
deleted file mode 100644
index 317a368d696668824f6be9a533c5ab6d4da8ff50..0000000000000000000000000000000000000000
--- a/bob/learn/em/cpp/FABase.cpp
+++ /dev/null
@@ -1,307 +0,0 @@
-/**
- * @date Tue Jan 27 15:51:15 2015 +0200
- * @author Laurent El Shafey <Laurent.El-Shafey@idiap.ch>
- * @author Tiago de Freitas Pereira <tiago.pereira@idiap.ch>
- *
- * Copyright (C) Idiap Research Institute, Martigny, Switzerland
- */
-
-
-#include <bob.learn.em/FABase.h>
-#include <bob.core/array_copy.h>
-#include <bob.math/linear.h>
-#include <bob.math/inv.h>
-#include <limits>
-
-
-//////////////////// FABase ////////////////////
-bob::learn::em::FABase::FABase():
-  m_ubm(boost::shared_ptr<bob::learn::em::GMMMachine>()), m_ru(1), m_rv(1),
-  m_U(0,1), m_V(0,1), m_d(0)
-{}
-
-bob::learn::em::FABase::FABase(const boost::shared_ptr<bob::learn::em::GMMMachine> ubm,
-    const size_t ru, const size_t rv):
-  m_ubm(ubm), m_ru(ru), m_rv(rv),
-  m_U(getSupervectorLength(),ru), m_V(getSupervectorLength(),rv), m_d(getSupervectorLength())
-{
-  if (ru < 1) {
-    boost::format m("value for parameter `ru' (%lu) cannot be smaller than 1");
-    m % ru;
-    throw std::runtime_error(m.str());
-  }
-  if (rv < 1) {
-    boost::format m("value for parameter `rv' (%lu) cannot be smaller than 1");
-    m % ru;
-    throw std::runtime_error(m.str());
-  }
-  updateCache();
-}
-
-bob::learn::em::FABase::FABase(const bob::learn::em::FABase& other):
-  m_ubm(other.m_ubm), m_ru(other.m_ru), m_rv(other.m_rv),
-  m_U(bob::core::array::ccopy(other.m_U)),
-  m_V(bob::core::array::ccopy(other.m_V)),
-  m_d(bob::core::array::ccopy(other.m_d))
-{
-  updateCache();
-}
-
-bob::learn::em::FABase::~FABase() {
-}
-
-bob::learn::em::FABase& bob::learn::em::FABase::operator=
-(const bob::learn::em::FABase& other)
-{
-  if (this != &other)
-  {
-    m_ubm = other.m_ubm;
-    m_ru = other.m_ru;
-    m_rv = other.m_rv;
-    m_U.reference(bob::core::array::ccopy(other.m_U));
-    m_V.reference(bob::core::array::ccopy(other.m_V));
-    m_d.reference(bob::core::array::ccopy(other.m_d));
-
-    updateCache();
-  }
-  return *this;
-}
-
-bool bob::learn::em::FABase::operator==(const bob::learn::em::FABase& b) const
-{
-  return ( (((m_ubm && b.m_ubm) && *m_ubm == *(b.m_ubm)) || (!m_ubm && !b.m_ubm)) &&
-          m_ru == b.m_ru && m_rv == b.m_rv &&
-          bob::core::array::isEqual(m_U, b.m_U) &&
-          bob::core::array::isEqual(m_V, b.m_V) &&
-          bob::core::array::isEqual(m_d, b.m_d));
-}
-
-bool bob::learn::em::FABase::operator!=(const bob::learn::em::FABase& b) const
-{
-  return !(this->operator==(b));
-}
-
-bool bob::learn::em::FABase::is_similar_to(const bob::learn::em::FABase& b,
-    const double r_epsilon, const double a_epsilon) const
-{
-  // TODO: update is_similar_to of the GMMMachine with the 2 epsilon's
-  return (( ((m_ubm && b.m_ubm) && m_ubm->is_similar_to(*(b.m_ubm), a_epsilon)) ||
-            (!m_ubm && !b.m_ubm) ) &&
-          m_ru == b.m_ru && m_rv == b.m_rv &&
-          bob::core::array::isClose(m_U, b.m_U, r_epsilon, a_epsilon) &&
-          bob::core::array::isClose(m_V, b.m_V, r_epsilon, a_epsilon) &&
-          bob::core::array::isClose(m_d, b.m_d, r_epsilon, a_epsilon));
-}
-
-void bob::learn::em::FABase::resize(const size_t ru, const size_t rv)
-{
-  if (ru < 1) {
-    boost::format m("value for parameter `ru' (%lu) cannot be smaller than 1");
-    m % ru;
-    throw std::runtime_error(m.str());
-  }
-  if (rv < 1) {
-    boost::format m("value for parameter `rv' (%lu) cannot be smaller than 1");
-    m % ru;
-    throw std::runtime_error(m.str());
-  }
-
-  m_ru = ru;
-  m_rv = rv;
-  m_U.resizeAndPreserve(m_U.extent(0), ru);
-  m_V.resizeAndPreserve(m_V.extent(0), rv);
-
-  updateCacheUbmUVD();
-}
-
-void bob::learn::em::FABase::resize(const size_t ru, const size_t rv, const size_t cd)
-{
-  if (ru < 1) {
-    boost::format m("value for parameter `ru' (%lu) cannot be smaller than 1");
-    m % ru;
-    throw std::runtime_error(m.str());
-  }
-  if (rv < 1) {
-    boost::format m("value for parameter `rv' (%lu) cannot be smaller than 1");
-    m % ru;
-    throw std::runtime_error(m.str());
-  }
-
-  if (!m_ubm || (m_ubm && getSupervectorLength() == cd))
-  {
-    m_ru = ru;
-    m_rv = rv;
-    m_U.resizeAndPreserve(cd, ru);
-    m_V.resizeAndPreserve(cd, rv);
-    m_d.resizeAndPreserve(cd);
-
-    updateCacheUbmUVD();
-  }
-  else {
-    boost::format m("value for parameter `cd' (%lu) is not set to %lu");
-    m % cd % getSupervectorLength();
-    throw std::runtime_error(m.str());
-  }
-}
-
-void bob::learn::em::FABase::setUbm(const boost::shared_ptr<bob::learn::em::GMMMachine> ubm)
-{
-  m_ubm = ubm;
-  m_U.resizeAndPreserve(getSupervectorLength(), m_ru);
-  m_V.resizeAndPreserve(getSupervectorLength(), m_rv);
-  m_d.resizeAndPreserve(getSupervectorLength());
-
-  updateCache();
-}
-
-void bob::learn::em::FABase::setU(const blitz::Array<double,2>& U)
-{
-  if(U.extent(0) != m_U.extent(0)) { //checks dimension
-    boost::format m("number of rows in parameter `U' (%d) does not match the expected size (%d)");
-    m % U.extent(0) % m_U.extent(0);
-    throw std::runtime_error(m.str());
-  }
-  if(U.extent(1) != m_U.extent(1)) { //checks dimension
-    boost::format m("number of columns in parameter `U' (%d) does not match the expected size (%d)");
-    m % U.extent(1) % m_U.extent(1);
-    throw std::runtime_error(m.str());
-  }
-  m_U.reference(bob::core::array::ccopy(U));
-
-  // update cache
-  updateCacheUbmUVD();
-}
-
-void bob::learn::em::FABase::setV(const blitz::Array<double,2>& V)
-{
-  if(V.extent(0) != m_V.extent(0)) { //checks dimension
-    boost::format m("number of rows in parameter `V' (%d) does not match the expected size (%d)");
-    m % V.extent(0) % m_V.extent(0);
-    throw std::runtime_error(m.str());
-  }
-  if(V.extent(1) != m_V.extent(1)) { //checks dimension
-    boost::format m("number of columns in parameter `V' (%d) does not match the expected size (%d)");
-    m % V.extent(1) % m_V.extent(1);
-    throw std::runtime_error(m.str());
-  }
-  m_V.reference(bob::core::array::ccopy(V));
-}
-
-void bob::learn::em::FABase::setD(const blitz::Array<double,1>& d)
-{
-  if(d.extent(0) != m_d.extent(0)) { //checks dimension
-    boost::format m("size of input vector `d' (%d) does not match the expected size (%d)");
-    m % d.extent(0) % m_d.extent(0);
-    throw std::runtime_error(m.str());
-  }
-  m_d.reference(bob::core::array::ccopy(d));
-}
-
-
-void bob::learn::em::FABase::updateCache()
-{
-  updateCacheUbm();
-  updateCacheUbmUVD();
-  resizeTmp();
-}
-
-void bob::learn::em::FABase::resizeTmp()
-{
-  m_tmp_IdPlusUSProdInv.resize(getDimRu(),getDimRu());
-  m_tmp_Fn_x.resize(getSupervectorLength());
-  m_tmp_ru.resize(getDimRu());
-  m_tmp_ruD.resize(getDimRu(), getNInputs());
-  m_tmp_ruru.resize(getDimRu(), getDimRu());
-}
-
-void bob::learn::em::FABase::updateCacheUbm()
-{
-  // Put supervectors in cache
-  if (m_ubm)
-  {
-    m_cache_mean.resize(getSupervectorLength());
-    m_cache_sigma.resize(getSupervectorLength());
-    m_cache_mean  = m_ubm->getMeanSupervector();
-    m_cache_sigma = m_ubm->getVarianceSupervector();
-  }
-}
-
-void bob::learn::em::FABase::updateCacheUbmUVD()
-{
-  // Compute and put  U^{T}.diag(sigma)^{-1} in cache
-  if (m_ubm)
-  {
-    blitz::firstIndex i;
-    blitz::secondIndex j;
-    m_cache_UtSigmaInv.resize(getDimRu(), getSupervectorLength());
-    m_cache_UtSigmaInv = m_U(j,i) / m_cache_sigma(j); // Ut * diag(sigma)^-1
-  }
-}
-
-void bob::learn::em::FABase::computeIdPlusUSProdInv(const bob::learn::em::GMMStats& gmm_stats,
-  blitz::Array<double,2>& output) const
-{
-  // Computes (Id + U^T.Sigma^-1.U.N_{i,h}.U)^-1 =
-  // (Id + sum_{c=1..C} N_{i,h}.U_{c}^T.Sigma_{c}^-1.U_{c})^-1
-
-  // Blitz compatibility: ugly fix (const_cast, as old blitz version does not
-  // provide a non-const version of transpose())
-  blitz::Array<double,2> Ut = const_cast<blitz::Array<double,2>&>(m_U).transpose(1,0);
-
-  blitz::firstIndex i;
-  blitz::secondIndex j;
-  blitz::Range rall = blitz::Range::all();
-
-  bob::math::eye(m_tmp_ruru); // m_tmp_ruru = Id
-  // Loop and add N_{i,h}.U_{c}^T.Sigma_{c}^-1.U_{c} to m_tmp_ruru at each iteration
-  const size_t dim_c = getNGaussians();
-  const size_t dim_d = getNInputs();
-  for(size_t c=0; c<dim_c; ++c) {
-    blitz::Range rc(c*dim_d,(c+1)*dim_d-1);
-    blitz::Array<double,2> Ut_c = Ut(rall,rc);
-    blitz::Array<double,1> sigma_c = m_cache_sigma(rc);
-    m_tmp_ruD = Ut_c(i,j) / sigma_c(j); // U_{c}^T.Sigma_{c}^-1
-    blitz::Array<double,2> U_c = m_U(rc,rall);
-    // Use m_cache_IdPlusUSProdInv as an intermediate array
-    bob::math::prod(m_tmp_ruD, U_c, output); // U_{c}^T.Sigma_{c}^-1.U_{c}
-    // Finally, add N_{i,h}.U_{c}^T.Sigma_{c}^-1.U_{c} to m_tmp_ruru
-    m_tmp_ruru += output * gmm_stats.n(c);
-  }
-  // Computes the inverse
-  bob::math::inv(m_tmp_ruru, output);
-}
-
-
-void bob::learn::em::FABase::computeFn_x(const bob::learn::em::GMMStats& gmm_stats,
-  blitz::Array<double,1>& output) const
-{
-  // Compute Fn_x = sum_{sessions h}(N*(o - m) (Normalised first order statistics)
-  blitz::Range rall = blitz::Range::all();
-  const size_t dim_c = getNGaussians();
-  const size_t dim_d = getNInputs();
-  for(size_t c=0; c<dim_c; ++c) {
-    blitz::Range rc(c*dim_d,(c+1)*dim_d-1);
-    blitz::Array<double,1> Fn_x_c = output(rc);
-    blitz::Array<double,1> mean_c = m_cache_mean(rc);
-    Fn_x_c = gmm_stats.sumPx(c,rall) - mean_c*gmm_stats.n(c);
-  }
-}
-
-void bob::learn::em::FABase::estimateX(const blitz::Array<double,2>& IdPlusUSProdInv,
-  const blitz::Array<double,1>& Fn_x, blitz::Array<double,1>& x) const
-{
-  // m_tmp_ru = UtSigmaInv * Fn_x = Ut*diag(sigma)^-1 * N*(o - m)
-  bob::math::prod(m_cache_UtSigmaInv, Fn_x, m_tmp_ru);
-  // x = IdPlusUSProdInv * m_cache_UtSigmaInv * Fn_x
-  bob::math::prod(IdPlusUSProdInv, m_tmp_ru, x);
-}
-
-
-void bob::learn::em::FABase::estimateX(const bob::learn::em::GMMStats& gmm_stats, blitz::Array<double,1>& x) const
-{
-  if (!m_ubm) throw std::runtime_error("No UBM was set in the JFA machine.");
-  computeIdPlusUSProdInv(gmm_stats, m_tmp_IdPlusUSProdInv); // Computes first term
-  computeFn_x(gmm_stats, m_tmp_Fn_x); // Computes last term
-  estimateX(m_tmp_IdPlusUSProdInv, m_tmp_Fn_x, x); // Estimates the value of x
-}
-
diff --git a/bob/learn/em/cpp/FABaseTrainer.cpp b/bob/learn/em/cpp/FABaseTrainer.cpp
deleted file mode 100644
index 09e4a4b69e262775b8ef529697f04ed2d2804621..0000000000000000000000000000000000000000
--- a/bob/learn/em/cpp/FABaseTrainer.cpp
+++ /dev/null
@@ -1,547 +0,0 @@
-/**
- * @date Sat Jan 31 17:16:17 2015 +0200
- * @author Laurent El Shafey <Laurent.El-Shafey@idiap.ch>
- * @author Tiago de Freitas Pereira <tiago.pereira@idiap.ch>
- *
- * @brief FABaseTrainer functions
- *
- * Copyright (C) Idiap Research Institute, Martigny, Switzerland
- */
-
-#include <bob.learn.em/FABaseTrainer.h>
-#include <bob.core/check.h>
-#include <bob.core/array_copy.h>
-#include <bob.core/array_random.h>
-#include <bob.math/inv.h>
-#include <bob.math/linear.h>
-#include <bob.core/check.h>
-#include <bob.core/array_repmat.h>
-#include <algorithm>
-
-
-bob::learn::em::FABaseTrainer::FABaseTrainer():
-  m_Nid(0), m_dim_C(0), m_dim_D(0), m_dim_ru(0), m_dim_rv(0),
-  m_x(0), m_y(0), m_z(0), m_Nacc(0), m_Facc(0)
-{
-}
-
-bob::learn::em::FABaseTrainer::FABaseTrainer(const bob::learn::em::FABaseTrainer& other)
-{
-}
-
-bob::learn::em::FABaseTrainer::~FABaseTrainer()
-{
-}
-
-void bob::learn::em::FABaseTrainer::checkStatistics(
-  const bob::learn::em::FABase& m,
-  const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& stats)
-{
-  for (size_t id=0; id<stats.size(); ++id) {
-    for (size_t s=0; s<stats[id].size(); ++s) {
-      if (stats[id][s]->sumPx.extent(0) != (int)m_dim_C) {
-        boost::format m("GMMStats C dimension parameter = %d is different than the expected value of %d");
-        m % stats[id][s]->sumPx.extent(0) % (int)m_dim_C;
-        throw std::runtime_error(m.str());
-      }
-      if (stats[id][s]->sumPx.extent(1) != (int)m_dim_D) {
-        boost::format m("GMMStats D dimension parameter = %d is different than the expected value of %d");
-        m % stats[id][s]->sumPx.extent(1) % (int)m_dim_D;
-        throw std::runtime_error(m.str());
-      }
-    }
-  }
-}
-
-
-void bob::learn::em::FABaseTrainer::initUbmNidSumStatistics(
-  const bob::learn::em::FABase& m,
-  const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& stats)
-{
-  m_Nid = stats.size();
-  boost::shared_ptr<bob::learn::em::GMMMachine> ubm = m.getUbm();
-  // Put UBM in cache
-  m_dim_C = ubm->getNGaussians();
-  m_dim_D = ubm->getNInputs();
-  m_dim_ru = m.getDimRu();
-  m_dim_rv = m.getDimRv();
-  // Check statistics dimensionality
-  checkStatistics(m, stats);
-  // Precomputes the sum of the statistics for each client/identity
-  precomputeSumStatisticsN(stats);
-  precomputeSumStatisticsF(stats);
-  // Cache and working arrays
-  initCache();
-}
-
-void bob::learn::em::FABaseTrainer::precomputeSumStatisticsN(
-  const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& stats)
-{
-  m_Nacc.clear();
-  blitz::Array<double,1> Nsum(m_dim_C);
-  for (size_t id=0; id<stats.size(); ++id) {
-    Nsum = 0.;
-    for (size_t s=0; s<stats[id].size(); ++s) {
-      Nsum += stats[id][s]->n;
-    }
-    m_Nacc.push_back(bob::core::array::ccopy(Nsum));
-  }
-}
-
-void bob::learn::em::FABaseTrainer::precomputeSumStatisticsF(
-  const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& stats)
-{
-  m_Facc.clear();
-  blitz::Array<double,1> Fsum(m_dim_C*m_dim_D);
-  for (size_t id=0; id<stats.size(); ++id) {
-    Fsum = 0.;
-    for (size_t s=0; s<stats[id].size(); ++s) {
-      for (size_t c=0; c<m_dim_C; ++c) {
-        blitz::Array<double,1> Fsum_c = Fsum(blitz::Range(c*m_dim_D,(c+1)*m_dim_D-1));
-        Fsum_c += stats[id][s]->sumPx(c,blitz::Range::all());
-      }
-    }
-    m_Facc.push_back(bob::core::array::ccopy(Fsum));
-  }
-}
-
-void bob::learn::em::FABaseTrainer::initializeXYZ(const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& vec)
-{
-  m_x.clear();
-  m_y.clear();
-  m_z.clear();
-
-  blitz::Array<double,1> z0(m_dim_C*m_dim_D);
-  z0 = 0;
-  blitz::Array<double,1> y0(m_dim_rv);
-  y0 = 0;
-  blitz::Array<double,2> x0(m_dim_ru,0);
-  x0 = 0;
-  for (size_t i=0; i<vec.size(); ++i)
-  {
-    m_z.push_back(bob::core::array::ccopy(z0));
-    m_y.push_back(bob::core::array::ccopy(y0));
-    x0.resize(m_dim_ru, vec[i].size());
-    x0 = 0;
-    m_x.push_back(bob::core::array::ccopy(x0));
-  }
-}
-
-void bob::learn::em::FABaseTrainer::resetXYZ()
-{
-  for (size_t i=0; i<m_x.size(); ++i)
-  {
-    m_x[i] = 0.;
-    m_y[i] = 0.;
-    m_z[i] = 0.;
-  }
-}
-
-void bob::learn::em::FABaseTrainer::initCache()
-{
-  const size_t dim_CD = m_dim_C*m_dim_D;
-  // U
-  m_cache_UtSigmaInv.resize(m_dim_ru, dim_CD);
-  m_cache_UProd.resize(m_dim_C, m_dim_ru, m_dim_ru);
-  m_cache_IdPlusUProd_ih.resize(m_dim_ru, m_dim_ru);
-  m_cache_Fn_x_ih.resize(dim_CD);
-  m_acc_U_A1.resize(m_dim_C, m_dim_ru, m_dim_ru);
-  m_acc_U_A2.resize(dim_CD, m_dim_ru);
-  // V
-  m_cache_VtSigmaInv.resize(m_dim_rv, dim_CD);
-  m_cache_VProd.resize(m_dim_C, m_dim_rv, m_dim_rv);
-  m_cache_IdPlusVProd_i.resize(m_dim_rv, m_dim_rv);
-  m_cache_Fn_y_i.resize(dim_CD);
-  m_acc_V_A1.resize(m_dim_C, m_dim_rv, m_dim_rv);
-  m_acc_V_A2.resize(dim_CD, m_dim_rv);
-  // D
-  m_cache_DtSigmaInv.resize(dim_CD);
-  m_cache_DProd.resize(dim_CD);
-  m_cache_IdPlusDProd_i.resize(dim_CD);
-  m_cache_Fn_z_i.resize(dim_CD);
-  m_acc_D_A1.resize(dim_CD);
-  m_acc_D_A2.resize(dim_CD);
-
-  // tmp
-  m_tmp_CD.resize(dim_CD);
-  m_tmp_CD_b.resize(dim_CD);
-
-  m_tmp_ru.resize(m_dim_ru);
-  m_tmp_ruD.resize(m_dim_ru, m_dim_D);
-  m_tmp_ruru.resize(m_dim_ru, m_dim_ru);
-
-  m_tmp_rv.resize(m_dim_rv);
-  m_tmp_rvD.resize(m_dim_rv, m_dim_D);
-  m_tmp_rvrv.resize(m_dim_rv, m_dim_rv);
-}
-
-
-
-//////////////////////////// V ///////////////////////////
-void bob::learn::em::FABaseTrainer::computeVtSigmaInv(const bob::learn::em::FABase& m)
-{
-  const blitz::Array<double,2>& V = m.getV();
-  // Blitz compatibility: ugly fix (const_cast, as old blitz version does not
-  // provide a non-const version of transpose())
-  const blitz::Array<double,2> Vt = const_cast<blitz::Array<double,2>&>(V).transpose(1,0);
-  const blitz::Array<double,1>& sigma = m.getUbmVariance();
-  blitz::firstIndex i;
-  blitz::secondIndex j;
-  m_cache_VtSigmaInv = Vt(i,j) / sigma(j); // Vt * diag(sigma)^-1
-}
-
-void bob::learn::em::FABaseTrainer::computeVProd(const bob::learn::em::FABase& m)
-{
-  const blitz::Array<double,2>& V = m.getV();
-  blitz::firstIndex i;
-  blitz::secondIndex j;
-  const blitz::Array<double,1>& sigma = m.getUbmVariance();
-  blitz::Range rall = blitz::Range::all();
-  for (size_t c=0; c<m_dim_C; ++c)
-  {
-    blitz::Array<double,2> VProd_c = m_cache_VProd(c, rall, rall);
-    blitz::Array<double,2> Vv_c = V(blitz::Range(c*m_dim_D,(c+1)*m_dim_D-1), rall);
-    blitz::Array<double,2> Vt_c = Vv_c.transpose(1,0);
-    blitz::Array<double,1> sigma_c = sigma(blitz::Range(c*m_dim_D,(c+1)*m_dim_D-1));
-    m_tmp_rvD = Vt_c(i,j) / sigma_c(j); // Vt_c * diag(sigma)^-1
-    bob::math::prod(m_tmp_rvD, Vv_c, VProd_c);
-  }
-}
-
-void bob::learn::em::FABaseTrainer::computeIdPlusVProd_i(const size_t id)
-{
-  const blitz::Array<double,1>& Ni = m_Nacc[id];
-  bob::math::eye(m_tmp_rvrv); // m_tmp_rvrv = I
-  blitz::Range rall = blitz::Range::all();
-  for (size_t c=0; c<m_dim_C; ++c) {
-    blitz::Array<double,2> VProd_c = m_cache_VProd(c, rall, rall);
-    m_tmp_rvrv += VProd_c * Ni(c);
-  }
-  bob::math::inv(m_tmp_rvrv, m_cache_IdPlusVProd_i); // m_cache_IdPlusVProd_i = ( I+Vt*diag(sigma)^-1*Ni*V)^-1
-}
-
-void bob::learn::em::FABaseTrainer::computeFn_y_i(const bob::learn::em::FABase& mb,
-  const std::vector<boost::shared_ptr<bob::learn::em::GMMStats> >& stats, const size_t id)
-{
-  const blitz::Array<double,2>& U = mb.getU();
-  const blitz::Array<double,1>& d = mb.getD();
-  // Compute Fn_yi = sum_{sessions h}(N_{i,h}*(o_{i,h} - m - D*z_{i} - U*x_{i,h}) (Normalised first order statistics)
-  const blitz::Array<double,1>& Fi = m_Facc[id];
-  const blitz::Array<double,1>& m = mb.getUbmMean();
-  const blitz::Array<double,1>& z = m_z[id];
-  bob::core::array::repelem(m_Nacc[id], m_tmp_CD);
-  m_cache_Fn_y_i = Fi - m_tmp_CD * (m + d * z); // Fn_yi = sum_{sessions h}(N_{i,h}*(o_{i,h} - m - D*z_{i})
-  const blitz::Array<double,2>& X = m_x[id];
-  blitz::Range rall = blitz::Range::all();
-  for (int h=0; h<X.extent(1); ++h) // Loops over the sessions
-  {
-    blitz::Array<double,1> Xh = X(rall, h); // Xh = x_{i,h} (length: ru)
-    bob::math::prod(U, Xh, m_tmp_CD_b); // m_tmp_CD_b = U*x_{i,h}
-    const blitz::Array<double,1>& Nih = stats[h]->n;
-    bob::core::array::repelem(Nih, m_tmp_CD);
-    m_cache_Fn_y_i -= m_tmp_CD * m_tmp_CD_b; // N_{i,h} * U * x_{i,h}
-  }
-  // Fn_yi = sum_{sessions h}(N_{i,h}*(o_{i,h} - m - D*z_{i} - U*x_{i,h})
-}
-
-void bob::learn::em::FABaseTrainer::updateY_i(const size_t id)
-{
-  // Computes yi = Ayi * Cvs * Fn_yi
-  blitz::Array<double,1>& y = m_y[id];
-  // m_tmp_rv = m_cache_VtSigmaInv * m_cache_Fn_y_i = Vt*diag(sigma)^-1 * sum_{sessions h}(N_{i,h}*(o_{i,h} - m - D*z_{i} - U*x_{i,h})
-  bob::math::prod(m_cache_VtSigmaInv, m_cache_Fn_y_i, m_tmp_rv);
-  bob::math::prod(m_cache_IdPlusVProd_i, m_tmp_rv, y);
-}
-
-void bob::learn::em::FABaseTrainer::updateY(const bob::learn::em::FABase& m,
-  const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& stats)
-{
-  // Precomputation
-  computeVtSigmaInv(m);
-  computeVProd(m);
-  // Loops over all people
-  for (size_t id=0; id<stats.size(); ++id) {
-    computeIdPlusVProd_i(id);
-    computeFn_y_i(m, stats[id], id);
-    updateY_i(id);
-  }
-}
-
-void bob::learn::em::FABaseTrainer::computeAccumulatorsV(
-  const bob::learn::em::FABase& m,
-  const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& stats)
-{
-  // Initializes the cache accumulator
-  m_acc_V_A1 = 0.;
-  m_acc_V_A2 = 0.;
-  // Loops over all people
-  blitz::firstIndex i;
-  blitz::secondIndex j;
-  blitz::Range rall = blitz::Range::all();
-  for (size_t id=0; id<stats.size(); ++id) {
-    computeIdPlusVProd_i(id);
-    computeFn_y_i(m, stats[id], id);
-
-    // Needs to return values to be accumulated for estimating V
-    const blitz::Array<double,1>& y = m_y[id];
-    m_tmp_rvrv = m_cache_IdPlusVProd_i;
-    m_tmp_rvrv += y(i) * y(j);
-    for (size_t c=0; c<m_dim_C; ++c)
-    {
-      blitz::Array<double,2> A1_y_c = m_acc_V_A1(c, rall, rall);
-      A1_y_c += m_tmp_rvrv * m_Nacc[id](c);
-    }
-    m_acc_V_A2 += m_cache_Fn_y_i(i) * y(j);
-  }
-}
-
-void bob::learn::em::FABaseTrainer::updateV(blitz::Array<double,2>& V)
-{
-  blitz::Range rall = blitz::Range::all();
-  for (size_t c=0; c<m_dim_C; ++c)
-  {
-    const blitz::Array<double,2> A1 = m_acc_V_A1(c, rall, rall);
-    bob::math::inv(A1, m_tmp_rvrv);
-    const blitz::Array<double,2> A2 = m_acc_V_A2(blitz::Range(c*m_dim_D,(c+1)*m_dim_D-1), rall);
-    blitz::Array<double,2> V_c = V(blitz::Range(c*m_dim_D,(c+1)*m_dim_D-1), rall);
-    bob::math::prod(A2, m_tmp_rvrv, V_c);
-  }
-}
-
-
-//////////////////////////// U ///////////////////////////
-void bob::learn::em::FABaseTrainer::computeUtSigmaInv(const bob::learn::em::FABase& m)
-{
-  const blitz::Array<double,2>& U = m.getU();
-  // Blitz compatibility: ugly fix (const_cast, as old blitz version does not
-  // provide a non-const version of transpose())
-  const blitz::Array<double,2> Ut = const_cast<blitz::Array<double,2>&>(U).transpose(1,0);
-  const blitz::Array<double,1>& sigma = m.getUbmVariance();
-  blitz::firstIndex i;
-  blitz::secondIndex j;
-  m_cache_UtSigmaInv = Ut(i,j) / sigma(j); // Ut * diag(sigma)^-1
-}
-
-void bob::learn::em::FABaseTrainer::computeUProd(const bob::learn::em::FABase& m)
-{
-  const blitz::Array<double,2>& U = m.getU();
-  blitz::firstIndex i;
-  blitz::secondIndex j;
-  const blitz::Array<double,1>& sigma = m.getUbmVariance();
-  for (size_t c=0; c<m_dim_C; ++c)
-  {
-    blitz::Array<double,2> UProd_c = m_cache_UProd(c, blitz::Range::all(), blitz::Range::all());
-    blitz::Array<double,2> Uu_c = U(blitz::Range(c*m_dim_D,(c+1)*m_dim_D-1), blitz::Range::all());
-    blitz::Array<double,2> Ut_c = Uu_c.transpose(1,0);
-    blitz::Array<double,1> sigma_c = sigma(blitz::Range(c*m_dim_D,(c+1)*m_dim_D-1));
-    m_tmp_ruD = Ut_c(i,j) / sigma_c(j); // Ut_c * diag(sigma)^-1
-    bob::math::prod(m_tmp_ruD, Uu_c, UProd_c);
-  }
-}
-
-void bob::learn::em::FABaseTrainer::computeIdPlusUProd_ih(
-  const boost::shared_ptr<bob::learn::em::GMMStats>& stats)
-{
-  const blitz::Array<double,1>& Nih = stats->n;
-  bob::math::eye(m_tmp_ruru); // m_tmp_ruru = I
-  for (size_t c=0; c<m_dim_C; ++c) {
-    blitz::Array<double,2> UProd_c = m_cache_UProd(c,blitz::Range::all(),blitz::Range::all());
-    m_tmp_ruru += UProd_c * Nih(c);
-  }
-  bob::math::inv(m_tmp_ruru, m_cache_IdPlusUProd_ih); // m_cache_IdPlusUProd_ih = ( I+Ut*diag(sigma)^-1*Ni*U)^-1
-}
-
-void bob::learn::em::FABaseTrainer::computeFn_x_ih(const bob::learn::em::FABase& mb,
-  const boost::shared_ptr<bob::learn::em::GMMStats>& stats, const size_t id)
-{
-  const blitz::Array<double,2>& V = mb.getV();
-  const blitz::Array<double,1>& d =  mb.getD();
-  // Compute Fn_x_ih = sum_{sessions h}(N_{i,h}*(o_{i,h} - m - D*z_{i} - V*y_{i}) (Normalised first order statistics)
-  const blitz::Array<double,2>& Fih = stats->sumPx;
-  const blitz::Array<double,1>& m = mb.getUbmMean();
-  const blitz::Array<double,1>& z = m_z[id];
-  const blitz::Array<double,1>& Nih = stats->n;
-  bob::core::array::repelem(Nih, m_tmp_CD);
-  for (size_t c=0; c<m_dim_C; ++c) {
-    blitz::Array<double,1> Fn_x_ih_c = m_cache_Fn_x_ih(blitz::Range(c*m_dim_D,(c+1)*m_dim_D-1));
-    Fn_x_ih_c = Fih(c,blitz::Range::all());
-  }
-  m_cache_Fn_x_ih -= m_tmp_CD * (m + d * z); // Fn_x_ih = N_{i,h}*(o_{i,h} - m - D*z_{i})
-
-  const blitz::Array<double,1>& y = m_y[id];
-  bob::math::prod(V, y, m_tmp_CD_b);
-  m_cache_Fn_x_ih -= m_tmp_CD * m_tmp_CD_b;
-  // Fn_x_ih = N_{i,h}*(o_{i,h} - m - D*z_{i} - V*y_{i})
-}
-
-void bob::learn::em::FABaseTrainer::updateX_ih(const size_t id, const size_t h)
-{
-  // Computes xih = Axih * Cus * Fn_x_ih
-  blitz::Array<double,1> x = m_x[id](blitz::Range::all(), h);
-  // m_tmp_ru = m_cache_UtSigmaInv * m_cache_Fn_x_ih = Ut*diag(sigma)^-1 * N_{i,h}*(o_{i,h} - m - D*z_{i} - V*y_{i})
-  bob::math::prod(m_cache_UtSigmaInv, m_cache_Fn_x_ih, m_tmp_ru);
-  bob::math::prod(m_cache_IdPlusUProd_ih, m_tmp_ru, x);
-}
-
-void bob::learn::em::FABaseTrainer::updateX(const bob::learn::em::FABase& m,
-  const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& stats)
-{
-  // Precomputation
-  computeUtSigmaInv(m);
-  computeUProd(m);
-  // Loops over all people
-  for (size_t id=0; id<stats.size(); ++id) {
-    int n_session_i = stats[id].size();
-    for (int s=0; s<n_session_i; ++s) {
-      computeIdPlusUProd_ih(stats[id][s]);
-      computeFn_x_ih(m, stats[id][s], id);
-      updateX_ih(id, s);
-    }
-  }
-}
-
-void bob::learn::em::FABaseTrainer::computeAccumulatorsU(
-  const bob::learn::em::FABase& m,
-  const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& stats)
-{
-  // Initializes the cache accumulator
-  m_acc_U_A1 = 0.;
-  m_acc_U_A2 = 0.;
-  // Loops over all people
-  blitz::firstIndex i;
-  blitz::secondIndex j;
-  blitz::Range rall = blitz::Range::all();
-  for (size_t id=0; id<stats.size(); ++id) {
-    int n_session_i = stats[id].size();
-    for (int h=0; h<n_session_i; ++h) {
-      computeIdPlusUProd_ih(stats[id][h]);
-      computeFn_x_ih(m, stats[id][h], id);
-
-      // Needs to return values to be accumulated for estimating U
-      blitz::Array<double,1> x = m_x[id](rall, h);
-      m_tmp_ruru = m_cache_IdPlusUProd_ih;
-      m_tmp_ruru += x(i) * x(j);
-      for (int c=0; c<(int)m_dim_C; ++c)
-      {
-        blitz::Array<double,2> A1_x_c = m_acc_U_A1(c,rall,rall);
-        A1_x_c += m_tmp_ruru * stats[id][h]->n(c);
-      }
-      m_acc_U_A2 += m_cache_Fn_x_ih(i) * x(j);
-    }
-  }
-}
-
-void bob::learn::em::FABaseTrainer::updateU(blitz::Array<double,2>& U)
-{
-  for (size_t c=0; c<m_dim_C; ++c)
-  {
-    const blitz::Array<double,2> A1 = m_acc_U_A1(c,blitz::Range::all(),blitz::Range::all());
-    bob::math::inv(A1, m_tmp_ruru);
-    const blitz::Array<double,2> A2 = m_acc_U_A2(blitz::Range(c*m_dim_D,(c+1)*m_dim_D-1),blitz::Range::all());
-    blitz::Array<double,2> U_c = U(blitz::Range(c*m_dim_D,(c+1)*m_dim_D-1),blitz::Range::all());
-    bob::math::prod(A2, m_tmp_ruru, U_c);
-  }
-}
-
-
-//////////////////////////// D ///////////////////////////
-void bob::learn::em::FABaseTrainer::computeDtSigmaInv(const bob::learn::em::FABase& m)
-{
-  const blitz::Array<double,1>& d = m.getD();
-  const blitz::Array<double,1>& sigma = m.getUbmVariance();
-  m_cache_DtSigmaInv = d / sigma; // Dt * diag(sigma)^-1
-}
-
-void bob::learn::em::FABaseTrainer::computeDProd(const bob::learn::em::FABase& m)
-{
-  const blitz::Array<double,1>& d = m.getD();
-  const blitz::Array<double,1>& sigma = m.getUbmVariance();
-  m_cache_DProd = d / sigma * d; // Dt * diag(sigma)^-1 * D
-}
-
-void bob::learn::em::FABaseTrainer::computeIdPlusDProd_i(const size_t id)
-{
-  const blitz::Array<double,1>& Ni = m_Nacc[id];
-  bob::core::array::repelem(Ni, m_tmp_CD); // m_tmp_CD = Ni 'repmat'
-  m_cache_IdPlusDProd_i = 1.; // m_cache_IdPlusDProd_i = Id
-  m_cache_IdPlusDProd_i += m_cache_DProd * m_tmp_CD; // m_cache_IdPlusDProd_i = I+Dt*diag(sigma)^-1*Ni*D
-  m_cache_IdPlusDProd_i = 1 / m_cache_IdPlusDProd_i; // m_cache_IdPlusVProd_i = (I+Dt*diag(sigma)^-1*Ni*D)^-1
-}
-
-void bob::learn::em::FABaseTrainer::computeFn_z_i(
-  const bob::learn::em::FABase& mb,
-  const std::vector<boost::shared_ptr<bob::learn::em::GMMStats> >& stats, const size_t id)
-{
-  const blitz::Array<double,2>& U = mb.getU();
-  const blitz::Array<double,2>& V = mb.getV();
-  // Compute Fn_z_i = sum_{sessions h}(N_{i,h}*(o_{i,h} - m - V*y_{i} - U*x_{i,h}) (Normalised first order statistics)
-  const blitz::Array<double,1>& Fi = m_Facc[id];
-  const blitz::Array<double,1>& m = mb.getUbmMean();
-  const blitz::Array<double,1>& y = m_y[id];
-  bob::core::array::repelem(m_Nacc[id], m_tmp_CD);
-  bob::math::prod(V, y, m_tmp_CD_b); // m_tmp_CD_b = V * y
-  m_cache_Fn_z_i = Fi - m_tmp_CD * (m + m_tmp_CD_b); // Fn_yi = sum_{sessions h}(N_{i,h}*(o_{i,h} - m - V*y_{i})
-
-  const blitz::Array<double,2>& X = m_x[id];
-  blitz::Range rall = blitz::Range::all();
-  for (int h=0; h<X.extent(1); ++h) // Loops over the sessions
-  {
-    const blitz::Array<double,1>& Nh = stats[h]->n; // Nh = N_{i,h} (length: C)
-    bob::core::array::repelem(Nh, m_tmp_CD);
-    blitz::Array<double,1> Xh = X(rall, h); // Xh = x_{i,h} (length: ru)
-    bob::math::prod(U, Xh, m_tmp_CD_b);
-    m_cache_Fn_z_i -= m_tmp_CD * m_tmp_CD_b;
-  }
-  // Fn_z_i = sum_{sessions h}(N_{i,h}*(o_{i,h} - m - V*y_{i} - U*x_{i,h})
-}
-
-void bob::learn::em::FABaseTrainer::updateZ_i(const size_t id)
-{
-  // Computes zi = Azi * D^T.Sigma^-1 * Fn_zi
-  blitz::Array<double,1>& z = m_z[id];
-  // m_tmp_CD = m_cache_DtSigmaInv * m_cache_Fn_z_i = Dt*diag(sigma)^-1 * sum_{sessions h}(N_{i,h}*(o_{i,h} - m - V*y_{i} - U*x_{i,h})
-  z = m_cache_IdPlusDProd_i * m_cache_DtSigmaInv * m_cache_Fn_z_i;
-}
-
-void bob::learn::em::FABaseTrainer::updateZ(const bob::learn::em::FABase& m,
-  const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& stats)
-{
-  // Precomputation
-  computeDtSigmaInv(m);
-  computeDProd(m);
-  // Loops over all people
-  for (size_t id=0; id<m_Nid; ++id) {
-    computeIdPlusDProd_i(id);
-    computeFn_z_i(m, stats[id], id);
-    updateZ_i(id);
-  }
-}
-
-void bob::learn::em::FABaseTrainer::computeAccumulatorsD(
-  const bob::learn::em::FABase& m,
-  const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& stats)
-{
-  // Initializes the cache accumulator
-  m_acc_D_A1 = 0.;
-  m_acc_D_A2 = 0.;
-  // Loops over all people
-  blitz::firstIndex i;
-  blitz::secondIndex j;
-  for (size_t id=0; id<stats.size(); ++id) {
-    computeIdPlusDProd_i(id);
-    computeFn_z_i(m, stats[id], id);
-
-    // Needs to return values to be accumulated for estimating D
-    blitz::Array<double,1> z = m_z[id];
-    bob::core::array::repelem(m_Nacc[id], m_tmp_CD);
-    m_acc_D_A1 += (m_cache_IdPlusDProd_i + z * z) * m_tmp_CD;
-    m_acc_D_A2 += m_cache_Fn_z_i * z;
-  }
-}
-
-void bob::learn::em::FABaseTrainer::updateD(blitz::Array<double,1>& d)
-{
-  d = m_acc_D_A2 / m_acc_D_A1;
-}
-
-
diff --git a/bob/learn/em/cpp/GMMBaseTrainer.cpp b/bob/learn/em/cpp/GMMBaseTrainer.cpp
deleted file mode 100644
index f4697bd527e018866f6dfa10232e4b36546b5169..0000000000000000000000000000000000000000
--- a/bob/learn/em/cpp/GMMBaseTrainer.cpp
+++ /dev/null
@@ -1,96 +0,0 @@
-/**
- * @date Tue May 10 11:35:58 2011 +0200
- * @author Francois Moulin <Francois.Moulin@idiap.ch>
- *
- * Copyright (C) Idiap Research Institute, Martigny, Switzerland
- */
-
-#include <bob.learn.em/GMMBaseTrainer.h>
-#include <bob.core/assert.h>
-#include <bob.core/check.h>
-
-bob::learn::em::GMMBaseTrainer::GMMBaseTrainer(const bool update_means,
-    const bool update_variances, const bool update_weights,
-    const double mean_var_update_responsibilities_threshold):
-  m_ss(new bob::learn::em::GMMStats()),
-  m_update_means(update_means), m_update_variances(update_variances),
-  m_update_weights(update_weights),
-  m_mean_var_update_responsibilities_threshold(mean_var_update_responsibilities_threshold)
-{}
-
-bob::learn::em::GMMBaseTrainer::GMMBaseTrainer(const bob::learn::em::GMMBaseTrainer& b):
-  m_ss(new bob::learn::em::GMMStats(  *b.getGMMStats()  )),
-  m_update_means(b.m_update_means), m_update_variances(b.m_update_variances),
-  m_mean_var_update_responsibilities_threshold(b.m_mean_var_update_responsibilities_threshold)
-{}
-
-bob::learn::em::GMMBaseTrainer::~GMMBaseTrainer()
-{}
-
-void bob::learn::em::GMMBaseTrainer::initialize(bob::learn::em::GMMMachine& gmm)
-{
-  // Allocate memory for the sufficient statistics and initialise
-  m_ss->resize(gmm.getNGaussians(),gmm.getNInputs());
-}
-
-void bob::learn::em::GMMBaseTrainer::eStep(bob::learn::em::GMMMachine& gmm,
-  const blitz::Array<double,2>& data)
-{
-  m_ss->init();
-  // Calculate the sufficient statistics and save in m_ss
-  gmm.accStatistics(data, *m_ss);
-}
-
-double bob::learn::em::GMMBaseTrainer::computeLikelihood(bob::learn::em::GMMMachine& gmm)
-{
-  return m_ss->log_likelihood / m_ss->T;
-}
-
-
-bob::learn::em::GMMBaseTrainer& bob::learn::em::GMMBaseTrainer::operator=
-  (const bob::learn::em::GMMBaseTrainer &other)
-{
-  if (this != &other)
-  {
-    *m_ss = *other.m_ss;
-    m_update_means = other.m_update_means;
-    m_update_variances = other.m_update_variances;
-    m_update_weights = other.m_update_weights;
-    m_mean_var_update_responsibilities_threshold = other.m_mean_var_update_responsibilities_threshold;
-  }
-  return *this;
-}
-
-bool bob::learn::em::GMMBaseTrainer::operator==
-  (const bob::learn::em::GMMBaseTrainer &other) const
-{
-  return *m_ss == *other.m_ss &&
-         m_update_means == other.m_update_means &&
-         m_update_variances == other.m_update_variances &&
-         m_update_weights == other.m_update_weights &&
-         m_mean_var_update_responsibilities_threshold == other.m_mean_var_update_responsibilities_threshold;
-}
-
-bool bob::learn::em::GMMBaseTrainer::operator!=
-  (const bob::learn::em::GMMBaseTrainer &other) const
-{
-  return !(this->operator==(other));
-}
-
-bool bob::learn::em::GMMBaseTrainer::is_similar_to
-  (const bob::learn::em::GMMBaseTrainer &other, const double r_epsilon,
-   const double a_epsilon) const
-{
-  return *m_ss == *other.m_ss &&
-         m_update_means == other.m_update_means &&
-         m_update_variances == other.m_update_variances &&
-         m_update_weights == other.m_update_weights &&
-         bob::core::isClose(m_mean_var_update_responsibilities_threshold,
-          other.m_mean_var_update_responsibilities_threshold, r_epsilon, a_epsilon);
-}
-
-void bob::learn::em::GMMBaseTrainer::setGMMStats(boost::shared_ptr<bob::learn::em::GMMStats> stats)
-{
-  bob::core::array::assertSameShape(m_ss->sumPx, stats->sumPx);
-  m_ss = stats;
-}
diff --git a/bob/learn/em/cpp/GMMMachine.cpp b/bob/learn/em/cpp/GMMMachine.cpp
deleted file mode 100644
index d2512b7085f09d9938fbed063a092251d974b5c9..0000000000000000000000000000000000000000
--- a/bob/learn/em/cpp/GMMMachine.cpp
+++ /dev/null
@@ -1,458 +0,0 @@
-/**
- * @date Tue May 10 11:35:58 2011 +0200
- * @author Francois Moulin <Francois.Moulin@idiap.ch>
- * @author Laurent El Shafey <Laurent.El-Shafey@idiap.ch>
- *
- * Copyright (C) Idiap Research Institute, Martigny, Switzerland
- */
-
-#include <bob.learn.em/GMMMachine.h>
-#include <bob.core/assert.h>
-#include <bob.math/log.h>
-
-bob::learn::em::GMMMachine::GMMMachine(): m_gaussians(0) {
-  resize(0,0);
-}
-
-bob::learn::em::GMMMachine::GMMMachine(const size_t n_gaussians, const size_t n_inputs):
-  m_gaussians(0)
-{
-  resize(n_gaussians,n_inputs);
-}
-
-bob::learn::em::GMMMachine::GMMMachine(bob::io::base::HDF5File& config):
-  m_gaussians(0)
-{
-  load(config);
-}
-
-bob::learn::em::GMMMachine::GMMMachine(const GMMMachine& other)  
-{
-  copy(other);
-}
-
-
-bob::learn::em::GMMMachine& bob::learn::em::GMMMachine::operator=(const bob::learn::em::GMMMachine &other) {
-  // protect against invalid self-assignment
-  if (this != &other)
-    copy(other);
-
-  // by convention, always return *this
-  return *this;
-}
-
-bool bob::learn::em::GMMMachine::operator==(const bob::learn::em::GMMMachine& b) const
-{
-  if (m_n_gaussians != b.m_n_gaussians || m_n_inputs != b.m_n_inputs ||
-      !bob::core::array::isEqual(m_weights, b.m_weights))
-    return false;
-
-  for(size_t i=0; i<m_n_gaussians; ++i) {
-    if(!(*(m_gaussians[i]) == *(b.m_gaussians[i])))
-      return false;
-  }
-
-  return true;
-}
-
-bool bob::learn::em::GMMMachine::operator!=(const bob::learn::em::GMMMachine& b) const {
-  return !(this->operator==(b));
-}
-
-bool bob::learn::em::GMMMachine::is_similar_to(const bob::learn::em::GMMMachine& b,
-  const double r_epsilon, const double a_epsilon) const
-{
-  if (m_n_gaussians != b.m_n_gaussians || m_n_inputs != b.m_n_inputs ||
-      !bob::core::array::isClose(m_weights, b.m_weights, r_epsilon, a_epsilon))
-    return false;
-
-  for (size_t i = 0; i < m_n_gaussians; ++i)
-    if (!m_gaussians[i]->is_similar_to(*b.m_gaussians[i], r_epsilon, a_epsilon))
-      return false;
-
-  return true;
-}
-
-void bob::learn::em::GMMMachine::copy(const GMMMachine& other) {
-  m_n_gaussians = other.m_n_gaussians;
-  m_n_inputs = other.m_n_inputs;
-
-  // Initialise weights
-  m_weights.resize(m_n_gaussians);
-  m_weights = other.m_weights;
-
-  // Initialise Gaussians
-  m_gaussians.clear();
-  for(size_t i=0; i<m_n_gaussians; ++i) {
-    boost::shared_ptr<bob::learn::em::Gaussian> g(new bob::learn::em::Gaussian(*(other.m_gaussians[i])));
-    m_gaussians.push_back(g);
-  }
-
-  // Initialise cache
-  initCache();
-}
-
-
-bob::learn::em::GMMMachine::~GMMMachine() { 
-}
-
-
-/////////////////////
-// Setters 
-////////////////////
-
-void bob::learn::em::GMMMachine::setWeights(const blitz::Array<double,1> &weights) {
-  bob::core::array::assertSameShape(weights, m_weights);
-  m_weights = weights;
-  recomputeLogWeights();
-}
-
-void bob::learn::em::GMMMachine::recomputeLogWeights() const
-{
-  m_cache_log_weights = blitz::log(m_weights);
-}
-
-void bob::learn::em::GMMMachine::setMeans(const blitz::Array<double,2> &means) {
-  bob::core::array::assertSameDimensionLength(means.extent(0), m_n_gaussians);
-  bob::core::array::assertSameDimensionLength(means.extent(1), m_n_inputs);
-  for(size_t i=0; i<m_n_gaussians; ++i)
-    m_gaussians[i]->updateMean() = means(i,blitz::Range::all());
-  m_cache_supervector = false;
-}
-
-void bob::learn::em::GMMMachine::setMeanSupervector(const blitz::Array<double,1> &mean_supervector) {
-  bob::core::array::assertSameDimensionLength(mean_supervector.extent(0), m_n_gaussians*m_n_inputs);
-  for(size_t i=0; i<m_n_gaussians; ++i)
-    m_gaussians[i]->updateMean() = mean_supervector(blitz::Range(i*m_n_inputs, (i+1)*m_n_inputs-1));
-  m_cache_supervector = false;
-}
-
-
-void bob::learn::em::GMMMachine::setVariances(const blitz::Array<double, 2 >& variances) {
-  bob::core::array::assertSameDimensionLength(variances.extent(0), m_n_gaussians);
-  bob::core::array::assertSameDimensionLength(variances.extent(1), m_n_inputs);
-  for(size_t i=0; i<m_n_gaussians; ++i) {
-    m_gaussians[i]->updateVariance() = variances(i,blitz::Range::all());
-    m_gaussians[i]->applyVarianceThresholds();
-  }
-  m_cache_supervector = false;
-}
-
-void bob::learn::em::GMMMachine::setVarianceSupervector(const blitz::Array<double,1> &variance_supervector) {
-  bob::core::array::assertSameDimensionLength(variance_supervector.extent(0), m_n_gaussians*m_n_inputs);
-  for(size_t i=0; i<m_n_gaussians; ++i) {
-    m_gaussians[i]->updateVariance() = variance_supervector(blitz::Range(i*m_n_inputs, (i+1)*m_n_inputs-1));
-    m_gaussians[i]->applyVarianceThresholds();
-  }
-  m_cache_supervector = false;
-}
-
-void bob::learn::em::GMMMachine::setVarianceThresholds(const double value) {
-  for(size_t i=0; i<m_n_gaussians; ++i)
-    m_gaussians[i]->setVarianceThresholds(value);
-  m_cache_supervector = false;
-}
-
-void bob::learn::em::GMMMachine::setVarianceThresholds(blitz::Array<double, 1> variance_thresholds) {
-  bob::core::array::assertSameDimensionLength(variance_thresholds.extent(0), m_n_inputs);
-  for(size_t i=0; i<m_n_gaussians; ++i)
-    m_gaussians[i]->setVarianceThresholds(variance_thresholds);
-  m_cache_supervector = false;
-}
-
-void bob::learn::em::GMMMachine::setVarianceThresholds(const blitz::Array<double, 2>& variance_thresholds) {
-  bob::core::array::assertSameDimensionLength(variance_thresholds.extent(0), m_n_gaussians);
-  bob::core::array::assertSameDimensionLength(variance_thresholds.extent(1), m_n_inputs);
-  for(size_t i=0; i<m_n_gaussians; ++i)
-    m_gaussians[i]->setVarianceThresholds(variance_thresholds(i,blitz::Range::all()));
-  m_cache_supervector = false;
-}
-
-/////////////////////
-// Getters 
-////////////////////
-
-const blitz::Array<double,2> bob::learn::em::GMMMachine::getMeans() const {
-
-  blitz::Array<double,2> means(m_n_gaussians,m_n_inputs);  
-  for(size_t i=0; i<m_n_gaussians; ++i)
-    means(i,blitz::Range::all()) = m_gaussians[i]->getMean();
-    
-  return means;
-}
-
-const blitz::Array<double,2> bob::learn::em::GMMMachine::getVariances() const{
-  
-  blitz::Array<double,2> variances(m_n_gaussians,m_n_inputs);
-  for(size_t i=0; i<m_n_gaussians; ++i)
-    variances(i,blitz::Range::all()) = m_gaussians[i]->getVariance();
-
-  return variances;
-}
-
-
-const blitz::Array<double,2>  bob::learn::em::GMMMachine::getVarianceThresholds() const {
-  //bob::core::array::assertSameDimensionLength(variance_thresholds.extent(0), m_n_gaussians);
-  //bob::core::array::assertSameDimensionLength(variance_thresholds.extent(1), m_n_inputs);
-  blitz::Array<double, 2> variance_thresholds(m_n_gaussians, m_n_inputs);
-  for(size_t i=0; i<m_n_gaussians; ++i)
-    variance_thresholds(i,blitz::Range::all()) = m_gaussians[i]->getVarianceThresholds();
-
-  return variance_thresholds;
-}
-
-
-/////////////////////
-// Methods
-////////////////////
-
-
-void bob::learn::em::GMMMachine::resize(const size_t n_gaussians, const size_t n_inputs) {
-  m_n_gaussians = n_gaussians;
-  m_n_inputs = n_inputs;
-
-  // Initialise weights
-  m_weights.resize(m_n_gaussians);
-  m_weights = 1.0 / m_n_gaussians;
-
-  // Initialise Gaussians
-  m_gaussians.clear();
-  for(size_t i=0; i<m_n_gaussians; ++i)
-    m_gaussians.push_back(boost::shared_ptr<bob::learn::em::Gaussian>(new bob::learn::em::Gaussian(n_inputs)));
-
-  // Initialise cache arrays
-  initCache();
-}
-
-double bob::learn::em::GMMMachine::logLikelihood(const blitz::Array<double, 1> &x,
-  blitz::Array<double,1> &log_weighted_gaussian_likelihoods) const
-{
-  // Check dimension
-  bob::core::array::assertSameDimensionLength(log_weighted_gaussian_likelihoods.extent(0), m_n_gaussians);
-  bob::core::array::assertSameDimensionLength(x.extent(0), m_n_inputs);
-  return logLikelihood_(x,log_weighted_gaussian_likelihoods);
-}
-
-
-double bob::learn::em::GMMMachine::logLikelihood_(const blitz::Array<double, 1> &x,
-  blitz::Array<double,1> &log_weighted_gaussian_likelihoods) const
-{
-  // Initialise variables
-  double log_likelihood = bob::math::Log::LogZero;
-
-  // Accumulate the weighted log likelihoods from each Gaussian
-  for(size_t i=0; i<m_n_gaussians; ++i) {
-    double l = m_cache_log_weights(i) + m_gaussians[i]->logLikelihood_(x);
-    log_weighted_gaussian_likelihoods(i) = l;
-    log_likelihood = bob::math::Log::logAdd(log_likelihood, l);
-  }
-
-  // Return log(p(x|GMMMachine))
-  return log_likelihood;
-}
-
-
-double bob::learn::em::GMMMachine::logLikelihood(const blitz::Array<double, 2> &x) const {
-  // Check dimension
-  bob::core::array::assertSameDimensionLength(x.extent(1), m_n_inputs);
-  // Call the other logLikelihood_ (overloaded) function
-
-
-  double sum_ll = 0;
-  for (int i=0; i<x.extent(0); i++)
-    sum_ll+= logLikelihood_(x(i,blitz::Range::all()));
-
-  return sum_ll/x.extent(0);  
-}
-
-
-
-double bob::learn::em::GMMMachine::logLikelihood(const blitz::Array<double, 1> &x) const {
-  // Check dimension
-  bob::core::array::assertSameDimensionLength(x.extent(0), m_n_inputs);
-  // Call the other logLikelihood_ (overloaded) function
-  // (log_weighted_gaussian_likelihoods will be discarded)
-  return logLikelihood_(x,m_cache_log_weighted_gaussian_likelihoods);
-}
-
-
-
-double bob::learn::em::GMMMachine::logLikelihood_(const blitz::Array<double, 1> &x) const {
-  // Call the other logLikelihood (overloaded) function
-  // (log_weighted_gaussian_likelihoods will be discarded)
-  return logLikelihood_(x,m_cache_log_weighted_gaussian_likelihoods);
-}
-
-void bob::learn::em::GMMMachine::accStatistics(const blitz::Array<double,2>& input,
-    bob::learn::em::GMMStats& stats) const {
-  // iterate over data
-  blitz::Range a = blitz::Range::all();
-  for(int i=0; i<input.extent(0); ++i) {
-    // Get example
-    blitz::Array<double,1> x(input(i,a));
-    // Accumulate statistics
-    accStatistics(x,stats);
-  }
-}
-
-void bob::learn::em::GMMMachine::accStatistics_(const blitz::Array<double,2>& input, bob::learn::em::GMMStats& stats) const {
-  // iterate over data
-  blitz::Range a = blitz::Range::all();
-  for(int i=0; i<input.extent(0); ++i) {
-    // Get example
-    blitz::Array<double,1> x(input(i, a));
-    // Accumulate statistics
-    accStatistics_(x,stats);
-  }
-}
-
-void bob::learn::em::GMMMachine::accStatistics(const blitz::Array<double, 1>& x, bob::learn::em::GMMStats& stats) const {
-  // check GMMStats size
-  bob::core::array::assertSameDimensionLength(stats.sumPx.extent(0), m_n_gaussians);
-  bob::core::array::assertSameDimensionLength(stats.sumPx.extent(1), m_n_inputs);
-
-  // Calculate Gaussian and GMM likelihoods
-  // - m_cache_log_weighted_gaussian_likelihoods(i) = log(weight_i*p(x|gaussian_i))
-  // - log_likelihood = log(sum_i(weight_i*p(x|gaussian_i)))
-  double log_likelihood = logLikelihood(x, m_cache_log_weighted_gaussian_likelihoods);
-
-  accStatisticsInternal(x, stats, log_likelihood);
-}
-
-void bob::learn::em::GMMMachine::accStatistics_(const blitz::Array<double, 1>& x, bob::learn::em::GMMStats& stats) const {
-  // Calculate Gaussian and GMM likelihoods
-  // - m_cache_log_weighted_gaussian_likelihoods(i) = log(weight_i*p(x|gaussian_i))
-  // - log_likelihood = log(sum_i(weight_i*p(x|gaussian_i)))
-  double log_likelihood = logLikelihood_(x, m_cache_log_weighted_gaussian_likelihoods);
-
-  accStatisticsInternal(x, stats, log_likelihood);
-}
-
-void bob::learn::em::GMMMachine::accStatisticsInternal(const blitz::Array<double, 1>& x,
-  bob::learn::em::GMMStats& stats, const double log_likelihood) const
-{
-  // Calculate responsibilities
-  m_cache_P = blitz::exp(m_cache_log_weighted_gaussian_likelihoods - log_likelihood);
-
-  // Accumulate statistics
-  // - total likelihood
-  stats.log_likelihood += log_likelihood;
-
-  // - number of samples
-  stats.T++;
-
-  // - responsibilities
-  stats.n += m_cache_P;
-
-  // - first order stats
-  blitz::firstIndex i;
-  blitz::secondIndex j;
-
-  m_cache_Px = m_cache_P(i) * x(j);
-
-  stats.sumPx += m_cache_Px;
-
-  // - second order stats
-  stats.sumPxx += (m_cache_Px(i,j) * x(j));
-}
-
-boost::shared_ptr<bob::learn::em::Gaussian> bob::learn::em::GMMMachine::getGaussian(const size_t i) {
-  if (i>=m_n_gaussians) {
-    throw std::runtime_error("getGaussian(): index out of bounds");
-  }
-  return m_gaussians[i];
-}
-
-void bob::learn::em::GMMMachine::save(bob::io::base::HDF5File& config) const {
-  int64_t v = static_cast<int64_t>(m_n_gaussians);
-  config.set("m_n_gaussians", v);
-  v = static_cast<int64_t>(m_n_inputs);
-  config.set("m_n_inputs", v);
-
-  for(size_t i=0; i<m_n_gaussians; ++i) {
-    std::ostringstream oss;
-    oss << "m_gaussians" << i;
-
-    if (!config.hasGroup(oss.str())) config.createGroup(oss.str());
-    config.cd(oss.str());
-    m_gaussians[i]->save(config);
-    config.cd("..");
-  }
-
-  config.setArray("m_weights", m_weights);
-}
-
-void bob::learn::em::GMMMachine::load(bob::io::base::HDF5File& config) {
-  int64_t v;
-  v = config.read<int64_t>("m_n_gaussians");
-  m_n_gaussians = static_cast<size_t>(v);
-  v = config.read<int64_t>("m_n_inputs");
-  m_n_inputs = static_cast<size_t>(v);
-
-  m_gaussians.clear();
-  for(size_t i=0; i<m_n_gaussians; ++i) {
-    m_gaussians.push_back(boost::shared_ptr<bob::learn::em::Gaussian>(new bob::learn::em::Gaussian(m_n_inputs)));
-    std::ostringstream oss;
-    oss << "m_gaussians" << i;
-    config.cd(oss.str());
-    m_gaussians[i]->load(config);
-    config.cd("..");
-  }
-
-  m_weights.resize(m_n_gaussians);
-  config.readArray("m_weights", m_weights);
-
-  // Initialise cache
-  initCache();
-}
-
-void bob::learn::em::GMMMachine::updateCacheSupervectors() const
-{
-  m_cache_mean_supervector.resize(m_n_gaussians*m_n_inputs);
-  m_cache_variance_supervector.resize(m_n_gaussians*m_n_inputs);
-
-  for(size_t i=0; i<m_n_gaussians; ++i) {
-    blitz::Range range(i*m_n_inputs, (i+1)*m_n_inputs-1);
-    m_cache_mean_supervector(range) = m_gaussians[i]->getMean();
-    m_cache_variance_supervector(range) = m_gaussians[i]->getVariance();
-  }
-  m_cache_supervector = true;
-}
-
-void bob::learn::em::GMMMachine::initCache() const {
-  // Initialise cache arrays
-  m_cache_log_weights.resize(m_n_gaussians);
-  recomputeLogWeights();
-  m_cache_log_weighted_gaussian_likelihoods.resize(m_n_gaussians);
-  m_cache_P.resize(m_n_gaussians);
-  m_cache_Px.resize(m_n_gaussians,m_n_inputs);
-  m_cache_supervector = false;
-}
-
-void bob::learn::em::GMMMachine::reloadCacheSupervectors() const {
-  if(!m_cache_supervector)
-    updateCacheSupervectors();
-}
-
-const blitz::Array<double,1>& bob::learn::em::GMMMachine::getMeanSupervector() const {
-  if(!m_cache_supervector)
-    updateCacheSupervectors();
-  return m_cache_mean_supervector;
-}
-
-const blitz::Array<double,1>& bob::learn::em::GMMMachine::getVarianceSupervector() const {
-  if(!m_cache_supervector)
-    updateCacheSupervectors();
-  return m_cache_variance_supervector;
-}
-
-namespace bob { namespace learn { namespace em {
-  std::ostream& operator<<(std::ostream& os, const GMMMachine& machine) {
-    os << "Weights = " << machine.m_weights << std::endl;
-    for(size_t i=0; i < machine.m_n_gaussians; ++i) {
-      os << "Gaussian " << i << ": " << std::endl << *(machine.m_gaussians[i]);
-    }
-
-    return os;
-  }
-} } }
diff --git a/bob/learn/em/cpp/GMMStats.cpp b/bob/learn/em/cpp/GMMStats.cpp
deleted file mode 100644
index 8a9f783c58068b2b67f9a7ac0e735447ff265143..0000000000000000000000000000000000000000
--- a/bob/learn/em/cpp/GMMStats.cpp
+++ /dev/null
@@ -1,151 +0,0 @@
-/**
- * @date Tue May 10 11:35:58 2011 +0200
- * @author Francois Moulin <Francois.Moulin@idiap.ch>
- * @author Laurent El Shafey <Laurent.El-Shafey@idiap.ch>
- *
- * Copyright (C) Idiap Research Institute, Martigny, Switzerland
- */
-
-#include <bob.learn.em/GMMStats.h>
-#include <bob.core/logging.h>
-#include <bob.core/check.h>
-
-bob::learn::em::GMMStats::GMMStats() {
-  resize(0,0);
-}
-
-bob::learn::em::GMMStats::GMMStats(const size_t n_gaussians, const size_t n_inputs) {
-  resize(n_gaussians,n_inputs);
-}
-
-bob::learn::em::GMMStats::GMMStats(bob::io::base::HDF5File& config) {
-  load(config);
-}
-
-bob::learn::em::GMMStats::GMMStats(const bob::learn::em::GMMStats& other) {
-  copy(other);
-}
-
-bob::learn::em::GMMStats::~GMMStats() {
-}
-
-bob::learn::em::GMMStats&
-bob::learn::em::GMMStats::operator=(const bob::learn::em::GMMStats& other) {
-  // protect against invalid self-assignment
-  if (this != &other)
-    copy(other);
-
-  // by convention, always return *this
-  return *this;
-}
-
-bool bob::learn::em::GMMStats::operator==(const bob::learn::em::GMMStats& b) const
-{
-  return (T == b.T && log_likelihood == b.log_likelihood &&
-          bob::core::array::isEqual(n, b.n) &&
-          bob::core::array::isEqual(sumPx, b.sumPx) &&
-          bob::core::array::isEqual(sumPxx, b.sumPxx));
-}
-
-bool
-bob::learn::em::GMMStats::operator!=(const bob::learn::em::GMMStats& b) const
-{
-  return !(this->operator==(b));
-}
-
-bool bob::learn::em::GMMStats::is_similar_to(const bob::learn::em::GMMStats& b,
-  const double r_epsilon, const double a_epsilon) const
-{
-  return (T == b.T &&
-          bob::core::isClose(log_likelihood, b.log_likelihood, r_epsilon, a_epsilon) &&
-          bob::core::array::isClose(n, b.n, r_epsilon, a_epsilon) &&
-          bob::core::array::isClose(sumPx, b.sumPx, r_epsilon, a_epsilon) &&
-          bob::core::array::isClose(sumPxx, b.sumPxx, r_epsilon, a_epsilon));
-}
-
-
-void bob::learn::em::GMMStats::operator+=(const bob::learn::em::GMMStats& b) {
-  // Check dimensions
-  if(n.extent(0) != b.n.extent(0) ||
-      sumPx.extent(0) != b.sumPx.extent(0) || sumPx.extent(1) != b.sumPx.extent(1) ||
-      sumPxx.extent(0) != b.sumPxx.extent(0) || sumPxx.extent(1) != b.sumPxx.extent(1))
-    // TODO: add a specialized exception
-    throw std::runtime_error("if you see this exception, fill a bug report");
-
-  // Update GMMStats object with the content of the other one
-  T += b.T;
-  log_likelihood += b.log_likelihood;
-  n += b.n;
-  sumPx += b.sumPx;
-  sumPxx += b.sumPxx;
-}
-
-void bob::learn::em::GMMStats::copy(const GMMStats& other) {
-  // Resize arrays
-  resize(other.sumPx.extent(0),other.sumPx.extent(1));
-  // Copy content
-  T = other.T;
-  log_likelihood = other.log_likelihood;
-  n = other.n;
-  sumPx = other.sumPx;
-  sumPxx = other.sumPxx;
-}
-
-void bob::learn::em::GMMStats::resize(const size_t n_gaussians, const size_t n_inputs) {
-  n.resize(n_gaussians);
-  sumPx.resize(n_gaussians, n_inputs);
-  sumPxx.resize(n_gaussians, n_inputs);
-  init();
-}
-
-void bob::learn::em::GMMStats::init() {
-  log_likelihood = 0;
-  T = 0;
-  n = 0.0;
-  sumPx = 0.0;
-  sumPxx = 0.0;
-}
-
-void bob::learn::em::GMMStats::save(bob::io::base::HDF5File& config) const {
-  //please note we fix the output values to be of a precise type so they can be
-  //retrieved at any platform with the exact same precision.
-  // TODO: add versioning, replace int64_t by uint64_t and log_liklihood by log_likelihood
-  int64_t sumpx_shape_0 = sumPx.shape()[0];
-  int64_t sumpx_shape_1 = sumPx.shape()[1];
-  config.set("n_gaussians", sumpx_shape_0);
-  config.set("n_inputs", sumpx_shape_1);
-  config.set("log_liklihood", log_likelihood); //double
-  config.set("T", static_cast<int64_t>(T));
-  config.setArray("n", n); //Array1d
-  config.setArray("sumPx", sumPx); //Array2d
-  config.setArray("sumPxx", sumPxx); //Array2d
-}
-
-void bob::learn::em::GMMStats::load(bob::io::base::HDF5File& config) {
-  log_likelihood = config.read<double>("log_liklihood");
-  int64_t n_gaussians = config.read<int64_t>("n_gaussians");
-  int64_t n_inputs = config.read<int64_t>("n_inputs");
-  T = static_cast<size_t>(config.read<int64_t>("T"));
-
-  //resize arrays to prepare for HDF5 readout
-  n.resize(n_gaussians);
-  sumPx.resize(n_gaussians, n_inputs);
-  sumPxx.resize(n_gaussians, n_inputs);
-
-  //load data
-  config.readArray("n", n);
-  config.readArray("sumPx", sumPx);
-  config.readArray("sumPxx", sumPxx);
-}
-
-namespace bob { namespace learn { namespace em {
-  std::ostream& operator<<(std::ostream& os, const GMMStats& g) {
-    os << "log_likelihood = " << g.log_likelihood << std::endl;
-    os << "T = " << g.T << std::endl;
-    os << "n = " << g.n;
-    os << "sumPx = " << g.sumPx;
-    os << "sumPxx = " << g.sumPxx;
-
-    return os;
-  }
-} } }
diff --git a/bob/learn/em/cpp/Gaussian.cpp b/bob/learn/em/cpp/Gaussian.cpp
deleted file mode 100644
index cd45f60f4248605bc48a934fbb7e7df5bf12b766..0000000000000000000000000000000000000000
--- a/bob/learn/em/cpp/Gaussian.cpp
+++ /dev/null
@@ -1,184 +0,0 @@
-/**
- * @date Tue May 10 11:35:58 2011 +0200
- * @author Francois Moulin <Francois.Moulin@idiap.ch>
- * @author Laurent El Shafey <Laurent.El-Shafey@idiap.ch>
- *
- * Copyright (C) Idiap Research Institute, Martigny, Switzerland
- */
-
-#include <bob.learn.em/Gaussian.h>
-
-#include <bob.core/assert.h>
-#include <bob.math/log.h>
-
-bob::learn::em::Gaussian::Gaussian() {
-  resize(0);
-}
-
-bob::learn::em::Gaussian::Gaussian(const size_t n_inputs) {
-  resize(n_inputs);
-}
-
-bob::learn::em::Gaussian::Gaussian(const bob::learn::em::Gaussian& other) {
-  copy(other);
-}
-
-bob::learn::em::Gaussian::Gaussian(bob::io::base::HDF5File& config) {
-  load(config);
-}
-
-bob::learn::em::Gaussian::~Gaussian() {
-}
-
-bob::learn::em::Gaussian& bob::learn::em::Gaussian::operator=(const bob::learn::em::Gaussian &other) {
-  if(this != &other)
-    copy(other);
-
-  return *this;
-}
-
-bool bob::learn::em::Gaussian::operator==(const bob::learn::em::Gaussian& b) const
-{
-  return (bob::core::array::isEqual(m_mean, b.m_mean) &&
-          bob::core::array::isEqual(m_variance, b.m_variance) &&
-          bob::core::array::isEqual(m_variance_thresholds, b.m_variance_thresholds));
-}
-
-bool bob::learn::em::Gaussian::operator!=(const bob::learn::em::Gaussian& b) const {
-  return !(this->operator==(b));
-}
-
-bool bob::learn::em::Gaussian::is_similar_to(const bob::learn::em::Gaussian& b,
-  const double r_epsilon, const double a_epsilon) const
-{
-  return (bob::core::array::isClose(m_mean, b.m_mean, r_epsilon, a_epsilon) &&
-          bob::core::array::isClose(m_variance, b.m_variance, r_epsilon, a_epsilon) &&
-          bob::core::array::isClose(m_variance_thresholds, b.m_variance_thresholds, r_epsilon, a_epsilon));
-}
-
-void bob::learn::em::Gaussian::copy(const bob::learn::em::Gaussian& other) {
-  m_n_inputs = other.m_n_inputs;
-
-  m_mean.resize(m_n_inputs);
-  m_mean = other.m_mean;
-
-  m_variance.resize(m_n_inputs);
-  m_variance = other.m_variance;
-
-  m_variance_thresholds.resize(m_n_inputs);
-  m_variance_thresholds = other.m_variance_thresholds;
-
-  m_n_log2pi = other.m_n_log2pi;
-  m_g_norm = other.m_g_norm;
-}
-
-
-void bob::learn::em::Gaussian::setNInputs(const size_t n_inputs) {
-  resize(n_inputs);
-}
-
-void bob::learn::em::Gaussian::resize(const size_t n_inputs) {
-  m_n_inputs = n_inputs;
-  m_mean.resize(m_n_inputs);
-  m_mean = 0;
-  m_variance.resize(m_n_inputs);
-  m_variance = 1;
-  m_variance_thresholds.resize(m_n_inputs);
-  m_variance_thresholds = std::numeric_limits<double>::epsilon();
-
-  // Re-compute g_norm, because m_n_inputs and m_variance
-  // have changed
-  preComputeNLog2Pi();
-  preComputeConstants();
-}
-
-void bob::learn::em::Gaussian::setMean(const blitz::Array<double,1> &mean) {
-  // Check and set
-  bob::core::array::assertSameShape(m_mean, mean);
-  m_mean = mean;
-}
-
-void bob::learn::em::Gaussian::setVariance(const blitz::Array<double,1> &variance) {
-  // Check and set
-  bob::core::array::assertSameShape(m_variance, variance);
-  m_variance = variance;
-
-  // Variance flooring
-  applyVarianceThresholds();
-}
-
-void bob::learn::em::Gaussian::setVarianceThresholds(const blitz::Array<double,1> &variance_thresholds) {
-  // Check and set
-  bob::core::array::assertSameShape(m_variance_thresholds, variance_thresholds);
-  m_variance_thresholds = variance_thresholds;
-
-  // Variance flooring
-  applyVarianceThresholds();
-}
-
-void bob::learn::em::Gaussian::setVarianceThresholds(const double value) {
-  blitz::Array<double,1> variance_thresholds(m_n_inputs);
-  variance_thresholds = value;
-  setVarianceThresholds(variance_thresholds);
-}
-
-void bob::learn::em::Gaussian::applyVarianceThresholds() {
-   // Apply variance flooring threshold
-  m_variance = blitz::where( m_variance < m_variance_thresholds, m_variance_thresholds, m_variance);
-
-  // Re-compute g_norm, because m_variance has changed
-  preComputeConstants();
-}
-
-double bob::learn::em::Gaussian::logLikelihood(const blitz::Array<double,1> &x) const {
-  // Check
-  bob::core::array::assertSameShape(x, m_mean);
-  return logLikelihood_(x);
-}
-
-double bob::learn::em::Gaussian::logLikelihood_(const blitz::Array<double,1> &x) const {
-  double z = blitz::sum(blitz::pow2(x - m_mean) / m_variance);
-  // Log Likelihood
-  return (-0.5 * (m_g_norm + z));
-}
-
-void bob::learn::em::Gaussian::preComputeNLog2Pi() {
-  m_n_log2pi = m_n_inputs * bob::math::Log::Log2Pi;
-}
-
-void bob::learn::em::Gaussian::preComputeConstants() {
-  m_g_norm = m_n_log2pi + blitz::sum(blitz::log(m_variance));
-}
-
-void bob::learn::em::Gaussian::save(bob::io::base::HDF5File& config) const {
-  config.setArray("m_mean", m_mean);
-  config.setArray("m_variance", m_variance);
-  config.setArray("m_variance_thresholds", m_variance_thresholds);
-  config.set("g_norm", m_g_norm);
-  int64_t v = static_cast<int64_t>(m_n_inputs);
-  config.set("m_n_inputs", v);
-}
-
-void bob::learn::em::Gaussian::load(bob::io::base::HDF5File& config) {
-  int64_t v = config.read<int64_t>("m_n_inputs");
-  m_n_inputs = static_cast<size_t>(v);
-
-  m_mean.resize(m_n_inputs);
-  m_variance.resize(m_n_inputs);
-  m_variance_thresholds.resize(m_n_inputs);
-
-  config.readArray("m_mean", m_mean);
-  config.readArray("m_variance", m_variance);
-  config.readArray("m_variance_thresholds", m_variance_thresholds);
-
-  preComputeNLog2Pi();
-  m_g_norm = config.read<double>("g_norm");
-}
-
-namespace bob { namespace learn { namespace em {
-  std::ostream& operator<<(std::ostream& os, const Gaussian& g) {
-    os << "Mean = " << g.m_mean << std::endl;
-    os << "Variance = " << g.m_variance << std::endl;
-    return os;
-  }
-} } }
diff --git a/bob/learn/em/cpp/ISVBase.cpp b/bob/learn/em/cpp/ISVBase.cpp
deleted file mode 100644
index c299013dc143b2b34b50a7802797b6728059e076..0000000000000000000000000000000000000000
--- a/bob/learn/em/cpp/ISVBase.cpp
+++ /dev/null
@@ -1,76 +0,0 @@
-/**
- * @date Tue Jan 27 16:02:00 2015 +0200
- * @author Laurent El Shafey <Laurent.El-Shafey@idiap.ch>
- * @author Tiago de Freitas Pereira <tiago.pereira@idiap.ch>
- *
- * Copyright (C) Idiap Research Institute, Martigny, Switzerland
- */
-
-
-#include <bob.learn.em/ISVBase.h>
-#include <bob.core/array_copy.h>
-#include <bob.math/linear.h>
-#include <bob.math/inv.h>
-#include <bob.learn.em/LinearScoring.h>
-#include <limits>
-
-
-//////////////////// ISVBase ////////////////////
-bob::learn::em::ISVBase::ISVBase()
-{
-}
-
-bob::learn::em::ISVBase::ISVBase(const boost::shared_ptr<bob::learn::em::GMMMachine> ubm,
-    const size_t ru):
-  m_base(ubm, ru, 1)
-{
-  blitz::Array<double,2>& V = m_base.updateV();
-  V = 0;
-}
-
-bob::learn::em::ISVBase::ISVBase(const bob::learn::em::ISVBase& other):
-  m_base(other.m_base)
-{
-}
-
-
-bob::learn::em::ISVBase::ISVBase(bob::io::base::HDF5File& config)
-{
-  load(config);
-}
-
-bob::learn::em::ISVBase::~ISVBase() {
-}
-
-void bob::learn::em::ISVBase::save(bob::io::base::HDF5File& config) const
-{
-  config.setArray("U", m_base.getU());
-  config.setArray("d", m_base.getD());
-}
-
-void bob::learn::em::ISVBase::load(bob::io::base::HDF5File& config)
-{
-  //reads all data directly into the member variables
-  blitz::Array<double,2> U = config.readArray<double,2>("U");
-  blitz::Array<double,1> d = config.readArray<double,1>("d");
-  const int ru = U.extent(1);
-  if (!m_base.getUbm())
-    m_base.resize(ru, 1, U.extent(0));
-  else
-    m_base.resize(ru, 1);
-  m_base.setU(U);
-  m_base.setD(d);
-  blitz::Array<double,2>& V = m_base.updateV();
-  V = 0;
-}
-
-bob::learn::em::ISVBase&
-bob::learn::em::ISVBase::operator=(const bob::learn::em::ISVBase& other)
-{
-  if (this != &other)
-  {
-    m_base = other.m_base;
-  }
-  return *this;
-}
-
diff --git a/bob/learn/em/cpp/ISVMachine.cpp b/bob/learn/em/cpp/ISVMachine.cpp
deleted file mode 100644
index 578e5d29f61f04af197c0b27aa27f32bb70e72b7..0000000000000000000000000000000000000000
--- a/bob/learn/em/cpp/ISVMachine.cpp
+++ /dev/null
@@ -1,195 +0,0 @@
-/**
- * @date Tue Jan 27 16:06:00 2015 +0200
- * @author Laurent El Shafey <Laurent.El-Shafey@idiap.ch>
- * @author Tiago de Freitas Pereira <tiago.pereira@idiap.ch>
- *
- * Copyright (C) Idiap Research Institute, Martigny, Switzerland
- */
-
-
-#include <bob.learn.em/ISVMachine.h>
-#include <bob.core/array_copy.h>
-#include <bob.math/linear.h>
-#include <bob.math/inv.h>
-#include <bob.learn.em/LinearScoring.h>
-#include <limits>
-
-
-//////////////////// ISVMachine ////////////////////
-bob::learn::em::ISVMachine::ISVMachine():
-  m_z(1)
-{
-  resizeTmp();
-}
-
-bob::learn::em::ISVMachine::ISVMachine(const boost::shared_ptr<bob::learn::em::ISVBase> isv_base):
-  m_isv_base(isv_base),
-  m_z(isv_base->getSupervectorLength())
-{
-  if (!m_isv_base->getUbm())
-    throw std::runtime_error("No UBM was set in the JFA machine.");
-  updateCache();
-  resizeTmp();
-}
-
-
-bob::learn::em::ISVMachine::ISVMachine(const bob::learn::em::ISVMachine& other):
-  m_isv_base(other.m_isv_base),
-  m_z(bob::core::array::ccopy(other.m_z))
-{
-  updateCache();
-  resizeTmp();
-}
-
-bob::learn::em::ISVMachine::ISVMachine(bob::io::base::HDF5File& config)
-{
-  load(config);
-}
-
-bob::learn::em::ISVMachine::~ISVMachine() {
-}
-
-bob::learn::em::ISVMachine&
-bob::learn::em::ISVMachine::operator=(const bob::learn::em::ISVMachine& other)
-{
-  if (this != &other)
-  {
-    m_isv_base = other.m_isv_base;
-    m_z.reference(bob::core::array::ccopy(other.m_z));
-  }
-  return *this;
-}
-
-bool bob::learn::em::ISVMachine::operator==(const bob::learn::em::ISVMachine& other) const
-{
-  return (*m_isv_base == *(other.m_isv_base) &&
-          bob::core::array::isEqual(m_z, other.m_z));
-}
-
-bool bob::learn::em::ISVMachine::operator!=(const bob::learn::em::ISVMachine& b) const
-{
-  return !(this->operator==(b));
-}
-
-
-bool bob::learn::em::ISVMachine::is_similar_to(const bob::learn::em::ISVMachine& b,
-    const double r_epsilon, const double a_epsilon) const
-{
-  return (m_isv_base->is_similar_to(*(b.m_isv_base), r_epsilon, a_epsilon) &&
-          bob::core::array::isClose(m_z, b.m_z, r_epsilon, a_epsilon));
-}
-
-void bob::learn::em::ISVMachine::save(bob::io::base::HDF5File& config) const
-{
-  config.setArray("z", m_z);
-}
-
-void bob::learn::em::ISVMachine::load(bob::io::base::HDF5File& config)
-{
-  //reads all data directly into the member variables
-  blitz::Array<double,1> z = config.readArray<double,1>("z");
-  if (!m_isv_base)
-    m_z.resize(z.extent(0));
-  setZ(z);
-  // update cache
-  updateCache();
-  resizeTmp();
-}
-
-void bob::learn::em::ISVMachine::setZ(const blitz::Array<double,1>& z)
-{
-  if(z.extent(0) != m_z.extent(0)) { //checks dimension
-    boost::format m("size of input vector `z' (%d) does not match the expected size (%d)");
-    m % z.extent(0) % m_z.extent(0);
-    throw std::runtime_error(m.str());
-  }
-  m_z.reference(bob::core::array::ccopy(z));
-  // update cache
-  updateCache();
-}
-
-
-void bob::learn::em::ISVMachine::setX(const blitz::Array<double,1>& x)
-{
-  if(x.extent(0) != m_cache_x.extent(0)) { //checks dimension
-    boost::format m("size of input vector `x' (%d) does not match the expected size (%d)");
-    m % x.extent(0) % m_cache_x.extent(0);
-    throw std::runtime_error(m.str());
-  }
-  m_cache_x.reference(bob::core::array::ccopy(x));
-  // update cache
-  updateCache();
-}
-
-void bob::learn::em::ISVMachine::setISVBase(const boost::shared_ptr<bob::learn::em::ISVBase> isv_base)
-{
-  if (!isv_base->getUbm())
-    throw std::runtime_error("No UBM was set in the JFA machine.");
-  m_isv_base = isv_base;
-  // Resize variables
-  resize();
-}
-
-void bob::learn::em::ISVMachine::resize()
-{
-  m_z.resizeAndPreserve(getSupervectorLength());
-  updateCache();
-  resizeTmp();
-}
-
-void bob::learn::em::ISVMachine::resizeTmp()
-{
-  if (m_isv_base)
-  {
-    m_tmp_Ux.resize(getSupervectorLength());
-  }
-}
-
-void bob::learn::em::ISVMachine::updateCache()
-{
-  if (m_isv_base)
-  {
-    // m + Dz
-    m_cache_mDz.resize(getSupervectorLength());
-    m_cache_mDz = m_isv_base->getD()*m_z + m_isv_base->getUbm()->getMeanSupervector();
-    m_cache_x.resize(getDimRu());
-  }
-}
-
-void bob::learn::em::ISVMachine::estimateUx(const bob::learn::em::GMMStats& gmm_stats,
-  blitz::Array<double,1>& Ux)
-{
-  estimateX(gmm_stats, m_cache_x);
-  bob::math::prod(m_isv_base->getU(), m_cache_x, Ux);
-}
-
-double bob::learn::em::ISVMachine::forward(const bob::learn::em::GMMStats& input)
-{
-  return forward_(input);
-}
-
-double bob::learn::em::ISVMachine::forward(const bob::learn::em::GMMStats& gmm_stats,
-  const blitz::Array<double,1>& Ux)
-{
-  // Checks that a Base machine has been set
-  if (!m_isv_base) throw std::runtime_error("No UBM was set in the JFA machine.");
-
-  return bob::learn::em::linearScoring(m_cache_mDz,
-            m_isv_base->getUbm()->getMeanSupervector(), m_isv_base->getUbm()->getVarianceSupervector(),
-            gmm_stats, Ux, true);
-}
-
-double bob::learn::em::ISVMachine::forward_(const bob::learn::em::GMMStats& input)
-{
-  // Checks that a Base machine has been set
-  if(!m_isv_base) throw std::runtime_error("No UBM was set in the JFA machine.");
-
-  // Ux and GMMStats
-  estimateX(input, m_cache_x);
-  bob::math::prod(m_isv_base->getU(), m_cache_x, m_tmp_Ux);
-
-  return bob::learn::em::linearScoring(m_cache_mDz,
-            m_isv_base->getUbm()->getMeanSupervector(), m_isv_base->getUbm()->getVarianceSupervector(),
-            input, m_tmp_Ux, true);
-}
-
diff --git a/bob/learn/em/cpp/ISVTrainer.cpp b/bob/learn/em/cpp/ISVTrainer.cpp
deleted file mode 100644
index cb00321a7e293a76469483b6e9e6863651b7eb66..0000000000000000000000000000000000000000
--- a/bob/learn/em/cpp/ISVTrainer.cpp
+++ /dev/null
@@ -1,127 +0,0 @@
-/**
- * @date Tue Jul 19 12:16:17 2011 +0200
- * @author Laurent El Shafey <Laurent.El-Shafey@idiap.ch>
- *
- * @brief Joint Factor Analysis Trainer
- *
- * Copyright (C) Idiap Research Institute, Martigny, Switzerland
- */
-
-#include <bob.learn.em/ISVTrainer.h>
-#include <bob.core/check.h>
-#include <bob.core/array_copy.h>
-#include <bob.core/array_random.h>
-#include <bob.math/inv.h>
-#include <bob.math/linear.h>
-#include <bob.core/check.h>
-#include <bob.core/array_repmat.h>
-#include <algorithm>
-
-
-//////////////////////////// ISVTrainer ///////////////////////////
-bob::learn::em::ISVTrainer::ISVTrainer(const double relevance_factor):
-  m_relevance_factor(relevance_factor),
-  m_rng(new boost::mt19937())
-{}
-
-bob::learn::em::ISVTrainer::ISVTrainer(const bob::learn::em::ISVTrainer& other):
-  m_rng(other.m_rng)
-{
-  m_relevance_factor      = other.m_relevance_factor;
-}
-
-bob::learn::em::ISVTrainer::~ISVTrainer()
-{}
-
-bob::learn::em::ISVTrainer& bob::learn::em::ISVTrainer::operator=
-(const bob::learn::em::ISVTrainer& other)
-{
-  if (this != &other)
-  {
-    m_rng                   = other.m_rng;
-    m_relevance_factor      = other.m_relevance_factor;
-  }
-  return *this;
-}
-
-bool bob::learn::em::ISVTrainer::operator==(const bob::learn::em::ISVTrainer& b) const
-{
-  return m_rng == b.m_rng &&
-         m_relevance_factor == b.m_relevance_factor;
-}
-
-bool bob::learn::em::ISVTrainer::operator!=(const bob::learn::em::ISVTrainer& b) const
-{
-  return !(this->operator==(b));
-}
-
-bool bob::learn::em::ISVTrainer::is_similar_to(const bob::learn::em::ISVTrainer& b,
-  const double r_epsilon, const double a_epsilon) const
-{
-  return  m_rng == b.m_rng &&
-          m_relevance_factor == b.m_relevance_factor;
-}
-
-void bob::learn::em::ISVTrainer::initialize(bob::learn::em::ISVBase& machine,
-  const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& ar)
-{
-  m_base_trainer.initUbmNidSumStatistics(machine.getBase(), ar);
-  m_base_trainer.initializeXYZ(ar);
-
-  blitz::Array<double,2>& U = machine.updateU();
-  bob::core::array::randn(*m_rng, U);
-  initializeD(machine);
-  machine.precompute();
-}
-
-void bob::learn::em::ISVTrainer::initializeD(bob::learn::em::ISVBase& machine) const
-{
-  // D = sqrt(variance(UBM) / relevance_factor)
-  blitz::Array<double,1> d = machine.updateD();
-  d = sqrt(machine.getBase().getUbmVariance() / m_relevance_factor);
-}
-
-void bob::learn::em::ISVTrainer::eStep(bob::learn::em::ISVBase& machine,
-  const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& ar)
-{
-  m_base_trainer.resetXYZ();
-
-  const bob::learn::em::FABase& base = machine.getBase();
-  m_base_trainer.updateX(base, ar);
-  m_base_trainer.updateZ(base, ar);
-  m_base_trainer.computeAccumulatorsU(base, ar);
-}
-
-void bob::learn::em::ISVTrainer::mStep(bob::learn::em::ISVBase& machine)
-{
-  blitz::Array<double,2>& U = machine.updateU();
-  m_base_trainer.updateU(U);
-  machine.precompute();
-}
-
-double bob::learn::em::ISVTrainer::computeLikelihood(bob::learn::em::ISVBase& machine)
-{
-  // TODO
-  return 0;
-}
-
-void bob::learn::em::ISVTrainer::enroll(bob::learn::em::ISVMachine& machine,
-  const std::vector<boost::shared_ptr<bob::learn::em::GMMStats> >& ar,
-  const size_t n_iter)
-{
-  std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > > vvec;
-  vvec.push_back(ar);
-
-  const bob::learn::em::FABase& fb = machine.getISVBase()->getBase();
-
-  m_base_trainer.initUbmNidSumStatistics(fb, vvec);
-  m_base_trainer.initializeXYZ(vvec);
-
-  for (size_t i=0; i<n_iter; ++i) {
-    m_base_trainer.updateX(fb, vvec);
-    m_base_trainer.updateZ(fb, vvec);
-  }
-
-  const blitz::Array<double,1> z(m_base_trainer.getZ()[0]);
-  machine.setZ(z);
-}
diff --git a/bob/learn/em/cpp/IVectorMachine.cpp b/bob/learn/em/cpp/IVectorMachine.cpp
deleted file mode 100644
index 912ba7d556f372563061b23d623670f49f1100b1..0000000000000000000000000000000000000000
--- a/bob/learn/em/cpp/IVectorMachine.cpp
+++ /dev/null
@@ -1,250 +0,0 @@
-/**
- * @date Sat Mar 30 21:00:00 2013 +0200
- * @author Laurent El Shafey <Laurent.El-Shafey@idiap.ch>
- *
- * Copyright (C) Idiap Research Institute, Martigny, Switzerland
- */
-
-#include <bob.learn.em/IVectorMachine.h>
-#include <bob.core/array_copy.h>
-#include <bob.core/check.h>
-#include <bob.math/linear.h>
-#include <bob.math/linsolve.h>
-
-bob::learn::em::IVectorMachine::IVectorMachine()
-{
-}
-
-bob::learn::em::IVectorMachine::IVectorMachine(const boost::shared_ptr<bob::learn::em::GMMMachine> ubm,
-    const size_t rt, const double variance_threshold):
-  m_ubm(ubm), m_rt(rt),
-  m_T(getSupervectorLength(),rt),  m_sigma(getSupervectorLength()),
-  m_variance_threshold(variance_threshold)
-{
-  m_sigma = 0.0;
-  resizePrecompute();
-}
-
-bob::learn::em::IVectorMachine::IVectorMachine(const bob::learn::em::IVectorMachine& other):
-  m_ubm(other.m_ubm), m_rt(other.m_rt),
-  m_T(bob::core::array::ccopy(other.m_T)),
-  m_sigma(bob::core::array::ccopy(other.m_sigma)),
-  m_variance_threshold(other.m_variance_threshold)
-{
-  resizePrecompute();
-}
-
-bob::learn::em::IVectorMachine::IVectorMachine(bob::io::base::HDF5File& config)
-{
-  load(config);
-}
-
-bob::learn::em::IVectorMachine::~IVectorMachine() {
-}
-
-void bob::learn::em::IVectorMachine::save(bob::io::base::HDF5File& config) const
-{
-  config.setArray("m_T", m_T);
-  config.setArray("m_sigma", m_sigma);
-  config.set("m_variance_threshold", m_variance_threshold);
-}
-
-void bob::learn::em::IVectorMachine::load(bob::io::base::HDF5File& config)
-{
-  //reads all data directly into the member variables
-  m_T.reference(config.readArray<double,2>("m_T"));
-  m_rt = m_T.extent(1);
-  m_sigma.reference(config.readArray<double,1>("m_sigma"));
-  m_variance_threshold = config.read<double>("m_variance_threshold");
-  resizePrecompute();
-}
-
-void bob::learn::em::IVectorMachine::resize(const size_t rt)
-{
-  m_rt = rt;
-  m_T.resizeAndPreserve(m_T.extent(0), rt);
-  resizePrecompute();
-}
-
-bob::learn::em::IVectorMachine&
-bob::learn::em::IVectorMachine::operator=(const bob::learn::em::IVectorMachine& other)
-{
-  if (this != &other)
-  {
-    m_ubm = other.m_ubm;
-    m_rt = other.m_rt;
-    m_T.reference(bob::core::array::ccopy(other.m_T));
-    m_sigma.reference(bob::core::array::ccopy(other.m_sigma));
-    m_variance_threshold = other.m_variance_threshold;
-    resizePrecompute();
-  }
-  return *this;
-}
-
-bool bob::learn::em::IVectorMachine::operator==(const IVectorMachine& b) const
-{
-  return (((m_ubm && b.m_ubm) && *m_ubm == *(b.m_ubm)) || (!m_ubm && !b.m_ubm)) &&
-         m_rt == b.m_rt &&
-         bob::core::array::isEqual(m_T, b.m_T) &&
-         bob::core::array::isEqual(m_sigma, b.m_sigma) &&
-         m_variance_threshold == b.m_variance_threshold;
-}
-
-bool bob::learn::em::IVectorMachine::operator!=(const bob::learn::em::IVectorMachine& b) const
-{
-  return !(this->operator==(b));
-}
-
-bool bob::learn::em::IVectorMachine::is_similar_to(const IVectorMachine& b,
-  const double r_epsilon, const double a_epsilon) const
-{
-  // TODO: update with new is_similar_to method
-  return (((m_ubm && b.m_ubm) && m_ubm->is_similar_to(*(b.m_ubm), r_epsilon)) || (!m_ubm && !b.m_ubm)) &&
-          m_rt == b.m_rt &&
-          bob::core::array::isClose(m_T, b.m_T, r_epsilon, a_epsilon) &&
-          bob::core::array::isClose(m_sigma, b.m_sigma, r_epsilon, a_epsilon) &&
-          bob::core::isClose(m_variance_threshold, b.m_variance_threshold, r_epsilon, a_epsilon);
-}
-
-void bob::learn::em::IVectorMachine::setUbm(const boost::shared_ptr<bob::learn::em::GMMMachine> ubm)
-{
-  m_ubm = ubm;
-  resizePrecompute();
-}
-
-void bob::learn::em::IVectorMachine::setT(const blitz::Array<double,2>& T)
-{
-  bob::core::array::assertSameShape(m_T, T);
-  m_T = T;
-  // Update cache
-  precompute();
-}
-
-void bob::learn::em::IVectorMachine::setSigma(const blitz::Array<double,1>& sigma)
-{
-  bob::core::array::assertSameShape(m_sigma, sigma);
-  m_sigma = sigma;
-  // Update cache
-  precompute();
-}
-
-
-void bob::learn::em::IVectorMachine::setVarianceThreshold(const double thd)
-{
-  m_variance_threshold = thd;
-  // Update cache
-  precompute();
-}
-
-void bob::learn::em::IVectorMachine::applyVarianceThreshold()
-{
-  // Apply variance flooring threshold
-  m_sigma = blitz::where(m_sigma < m_variance_threshold, m_variance_threshold, m_sigma);
-}
-
-void bob::learn::em::IVectorMachine::precompute()
-{
-  if (m_ubm)
-  {
-    // Apply variance threshold
-    applyVarianceThreshold();
-
-    blitz::firstIndex i;
-    blitz::secondIndex j;
-    blitz::Range rall = blitz::Range::all();
-    const int C = (int)m_ubm->getNGaussians();
-    const int D = (int)m_ubm->getNInputs();
-
-    // T_{c}^{T}.sigma_{c}^{-1}
-    for (int c=0; c<C; ++c)
-    {
-      blitz::Array<double,2> Tct_sigmacInv = m_cache_Tct_sigmacInv(c, rall, rall);
-      blitz::Array<double,2> Tc = m_T(blitz::Range(c*D,(c+1)*D-1), rall);
-      blitz::Array<double,2> Tct = Tc.transpose(1,0);
-      blitz::Array<double,1> sigma_c = m_sigma(blitz::Range(c*D,(c+1)*D-1));
-      Tct_sigmacInv = Tct(i,j) / sigma_c(j);
-    }
-
-    // T_{c}^{T}.sigma_{c}^{-1}.T_{c}
-    for (int c=0; c<C; ++c)
-    {
-      blitz::Array<double,2> Tc = m_T(blitz::Range(c*D,(c+1)*D-1), rall);
-      blitz::Array<double,2> Tct_sigmacInv = m_cache_Tct_sigmacInv(c, rall, rall);
-      blitz::Array<double,2> Tct_sigmacInv_Tc = m_cache_Tct_sigmacInv_Tc(c, rall, rall);
-      bob::math::prod(Tct_sigmacInv, Tc, Tct_sigmacInv_Tc);
-    }
-  }
-}
-
-void bob::learn::em::IVectorMachine::resizePrecompute()
-{
-  resizeCache();
-  resizeTmp();
-  precompute();
-}
-
-void bob::learn::em::IVectorMachine::resizeCache()
-{
-  if (m_ubm)
-  {
-    const int C = (int)m_ubm->getNGaussians();
-    const int D = (int)m_ubm->getNInputs();
-    m_cache_Tct_sigmacInv.resize(C, (int)m_rt, D);
-    m_cache_Tct_sigmacInv_Tc.resize(C, (int)m_rt, (int)m_rt);
-  }
-}
-
-void bob::learn::em::IVectorMachine::resizeTmp()
-{
-  if (m_ubm)
-    m_tmp_d.resize(m_ubm->getNInputs());
-  m_tmp_t1.resize(m_rt);
-  m_tmp_t2.resize(m_rt);
-  m_tmp_tt.resize(m_rt, m_rt);
-}
-
-void bob::learn::em::IVectorMachine::forward(const bob::learn::em::GMMStats& gs,
-  blitz::Array<double,1>& ivector) const
-{
-  bob::core::array::assertSameDimensionLength(ivector.extent(0), (int)m_rt);
-  forward_(gs, ivector);
-}
-
-void bob::learn::em::IVectorMachine::computeIdTtSigmaInvT(
-  const bob::learn::em::GMMStats& gs, blitz::Array<double,2>& output) const
-{
-  // Computes \f$(Id + \sum_{c=1}^{C} N_{i,j,c} T^{T} \Sigma_{c}^{-1} T)\f$
-  blitz::Range rall = blitz::Range::all();
-  bob::math::eye(output);
-  for (int c=0; c<(int)getNGaussians(); ++c)
-    output += gs.n(c) * m_cache_Tct_sigmacInv_Tc(c, rall, rall);
-}
-
-void bob::learn::em::IVectorMachine::computeTtSigmaInvFnorm(
-  const bob::learn::em::GMMStats& gs, blitz::Array<double,1>& output) const
-{
-  // Computes \f$T^{T} \Sigma^{-1} \sum_{c=1}^{C} (F_c - N_c ubmmean_{c})\f$
-  blitz::Range rall = blitz::Range::all();
-  output = 0;
-  for (int c=0; c<(int)getNGaussians(); ++c)
-  {
-    m_tmp_d = gs.sumPx(c,rall) - gs.n(c) * m_ubm->getGaussian(c)->getMean();
-    blitz::Array<double,2> Tct_sigmacInv = m_cache_Tct_sigmacInv(c, rall, rall);
-    bob::math::prod(Tct_sigmacInv, m_tmp_d, m_tmp_t2);
-
-    output += m_tmp_t2;
-  }
-}
-
-void bob::learn::em::IVectorMachine::forward_(const bob::learn::em::GMMStats& gs,
-  blitz::Array<double,1>& ivector) const
-{
-  // Computes \f$(Id + \sum_{c=1}^{C} N_{i,j,c} T^{T} \Sigma_{c}^{-1} T)\f$
-  computeIdTtSigmaInvT(gs, m_tmp_tt);
-
-  // Computes \f$T^{T} \Sigma^{-1} \sum_{c=1}^{C} (F_c - N_c ubmmean_{c})\f$
-  computeTtSigmaInvFnorm(gs, m_tmp_t1);
-
-  // Solves m_tmp_tt.ivector = m_tmp_t1
-  bob::math::linsolve(m_tmp_tt, m_tmp_t1, ivector);
-}
diff --git a/bob/learn/em/cpp/IVectorTrainer.cpp b/bob/learn/em/cpp/IVectorTrainer.cpp
deleted file mode 100644
index 591de601cf195b9be555a8873dc7814512d3a64f..0000000000000000000000000000000000000000
--- a/bob/learn/em/cpp/IVectorTrainer.cpp
+++ /dev/null
@@ -1,235 +0,0 @@
-/**
- * @date Sun Mar 31 20:15:00 2013 +0200
- * @author Laurent El Shafey <Laurent.El-Shafey@idiap.ch>
- *
- * Copyright (C) Idiap Research Institute, Martigny, Switzerland
- */
-
-
-#include <bob.learn.em/IVectorTrainer.h>
-#include <bob.core/check.h>
-#include <bob.core/array_copy.h>
-#include <bob.core/array_random.h>
-#include <bob.math/inv.h>
-#include <bob.core/check.h>
-#include <bob.core/array_repmat.h>
-#include <algorithm>
-
-#include <bob.math/linear.h>
-#include <bob.math/linsolve.h>
-
-bob::learn::em::IVectorTrainer::IVectorTrainer(const bool update_sigma):
-  m_update_sigma(update_sigma),
-  m_rng(new boost::mt19937())
-{}
-
-bob::learn::em::IVectorTrainer::IVectorTrainer(const bob::learn::em::IVectorTrainer& other):
-  m_update_sigma(other.m_update_sigma)
-{
-  m_rng                   = other.m_rng;
-  m_acc_Nij_wij2.reference(bob::core::array::ccopy(other.m_acc_Nij_wij2));
-  m_acc_Fnormij_wij.reference(bob::core::array::ccopy(other.m_acc_Fnormij_wij));
-  m_acc_Nij.reference(bob::core::array::ccopy(other.m_acc_Nij));
-  m_acc_Snormij.reference(bob::core::array::ccopy(other.m_acc_Snormij));
-
-  m_tmp_wij.reference(bob::core::array::ccopy(other.m_tmp_wij));
-  m_tmp_wij2.reference(bob::core::array::ccopy(other.m_tmp_wij2));
-  m_tmp_d1.reference(bob::core::array::ccopy(other.m_tmp_d1));
-  m_tmp_t1.reference(bob::core::array::ccopy(other.m_tmp_t1));
-  m_tmp_dd1.reference(bob::core::array::ccopy(other.m_tmp_dd1));
-  m_tmp_dt1.reference(bob::core::array::ccopy(other.m_tmp_dt1));
-  m_tmp_tt1.reference(bob::core::array::ccopy(other.m_tmp_tt1));
-  m_tmp_tt2.reference(bob::core::array::ccopy(other.m_tmp_tt2));
-}
-
-bob::learn::em::IVectorTrainer::~IVectorTrainer()
-{
-}
-
-void bob::learn::em::IVectorTrainer::initialize(
-  bob::learn::em::IVectorMachine& machine)
-{
-  // Initializes \f$T\f$ and \f$\Sigma\f$ of the machine
-  blitz::Array<double,2>& T = machine.updateT();
-  bob::core::array::randn(*m_rng, T);
-  blitz::Array<double,1>& sigma = machine.updateSigma();
-  sigma = machine.getUbm()->getVarianceSupervector();
-  machine.precompute();
-}
-
-void bob::learn::em::IVectorTrainer::resetAccumulators(const bob::learn::em::IVectorMachine& machine)
-{
-   // Resize the accumulator
-  const int C = machine.getNGaussians();
-  const int D = machine.getNInputs();
-  const int Rt = machine.getDimRt();
-
-  // Cache
-  m_acc_Nij_wij2.resize(C,Rt,Rt);
-  m_acc_Fnormij_wij.resize(C,D,Rt);
-  if (m_update_sigma)
-  {
-    m_acc_Nij.resize(C);
-    m_acc_Snormij.resize(C,D);
-  }
-
-  // Tmp
-  m_tmp_wij.resize(Rt);
-  m_tmp_wij2.resize(Rt,Rt);
-  m_tmp_d1.resize(D);
-  m_tmp_t1.resize(Rt);
-
-  m_tmp_dt1.resize(D,Rt);
-  m_tmp_tt1.resize(Rt,Rt);
-  m_tmp_tt2.resize(Rt,Rt);
-  if (m_update_sigma)
-    m_tmp_dd1.resize(D,D);
-
-  // initialize with 0
-  m_acc_Nij_wij2 = 0.;
-  m_acc_Fnormij_wij = 0.;
-  if (m_update_sigma)
-  {
-    m_acc_Nij = 0.;
-    m_acc_Snormij = 0.;
-  }
-}
-
-
-void bob::learn::em::IVectorTrainer::eStep(
-  bob::learn::em::IVectorMachine& machine,
-  const std::vector<bob::learn::em::GMMStats>& data)
-{
-  blitz::Range rall = blitz::Range::all();
-  const int C = machine.getNGaussians();
-
-  // Reinitializes accumulators to 0
-  resetAccumulators(machine);
-
-  for (std::vector<bob::learn::em::GMMStats>::const_iterator it = data.begin();
-       it != data.end(); ++it)
-  {
-    // Computes E{wij} and E{wij.wij^{T}}
-    // a. Computes \f$T^{T} \Sigma^{-1} F_{norm}\f$
-    machine.computeTtSigmaInvFnorm(*it, m_tmp_t1);
-    // b. Computes \f$Id + T^{T} \Sigma^{-1} T\f$
-    machine.computeIdTtSigmaInvT(*it, m_tmp_tt1);
-    // c. Computes \f$(Id + T^{T} \Sigma^{-1} T)^{-1}\f$
-
-    bob::math::inv(m_tmp_tt1, m_tmp_tt2);
-    // d. Computes \f$E{wij} = (Id + T^{T} \Sigma^{-1} T)^{-1} T^{T} \Sigma^{-1} F_{norm}\f$
-    bob::math::prod(m_tmp_tt2, m_tmp_t1, m_tmp_wij); // E{wij}
-    // e.  Computes \f$E{wij}.E{wij^{T}}\f$
-    bob::math::prod(m_tmp_wij, m_tmp_wij, m_tmp_wij2);
-    // f. Computes \f$E{wij.wij^{T}} = (Id + T^{T} \Sigma^{-1} T)^{-1} + E{wij}.E{wij^{T}}\f$
-    m_tmp_wij2 += m_tmp_tt2; // E{wij.wij^{T}}
-
-    if (m_update_sigma)
-      m_acc_Nij += (*it).n;
-
-    for (int c=0; c<C; ++c)
-    {
-      blitz::Array<double,2> acc_Nij_wij2_c = m_acc_Nij_wij2(c,rall,rall);
-      blitz::Array<double,2> acc_Fnormij_wij = m_acc_Fnormij_wij(c,rall,rall);
-      // acc_Nij_wij2_c += Nijc . E{wij.wij^{T}}
-      acc_Nij_wij2_c += (*it).n(c) * m_tmp_wij2;
-      blitz::Array<double,1> mc = machine.getUbm()->getGaussian(c)->getMean();
-      // m_tmp_d1 = Fijc - Nijc * ubmmean_{c}
-      m_tmp_d1 = (*it).sumPx(c,rall) - (*it).n(c)*mc; // Fnorm_c
-      // m_tmp_dt1 = (Fijc - Nijc * ubmmean_{c}).E{wij}^{T}
-      bob::math::prod(m_tmp_d1, m_tmp_wij, m_tmp_dt1);
-      // acc_Fnormij_wij += (Fijc - Nijc * ubmmean_{c}).E{wij}^{T}
-      acc_Fnormij_wij += m_tmp_dt1;
-      if (m_update_sigma)
-      {
-        blitz::Array<double,1> acc_Snormij_c = m_acc_Snormij(c,rall);
-        acc_Snormij_c += (*it).sumPxx(c,rall) - mc*((*it).sumPx(c,rall) + m_tmp_d1);
-      }
-    }
-  }
-}
-
-void bob::learn::em::IVectorTrainer::mStep(
-  bob::learn::em::IVectorMachine& machine)
-{
-  blitz::Range rall = blitz::Range::all();
-  blitz::Array<double,2>& T = machine.updateT();
-  blitz::Array<double,1>& sigma = machine.updateSigma();
-  const int C = (int)machine.getNGaussians();
-  const int D = (int)machine.getNInputs();
-  for (int c=0; c<C; ++c)
-  {
-    // Solves linear system A.T = B to update T, based on accumulators of
-    // the eStep()
-    blitz::Array<double,2> acc_Nij_wij2_c = m_acc_Nij_wij2(c,rall,rall);
-    blitz::Array<double,2> tacc_Nij_wij2_c = acc_Nij_wij2_c.transpose(1,0);
-    blitz::Array<double,2> acc_Fnormij_wij_c = m_acc_Fnormij_wij(c,rall,rall);
-    blitz::Array<double,2> tacc_Fnormij_wij_c = acc_Fnormij_wij_c.transpose(1,0);
-    blitz::Array<double,2> T_c = T(blitz::Range(c*D,(c+1)*D-1),rall);
-    blitz::Array<double,2> Tt_c = T_c.transpose(1,0);
-    if (blitz::all(acc_Nij_wij2_c == 0)) // TODO
-      Tt_c = 0;
-    else
-      bob::math::linsolve(tacc_Nij_wij2_c, tacc_Fnormij_wij_c, Tt_c);
-    if (m_update_sigma)
-    {
-      blitz::Array<double,1> sigma_c = sigma(blitz::Range(c*D,(c+1)*D-1));
-      bob::math::prod(acc_Fnormij_wij_c, Tt_c, m_tmp_dd1);
-      bob::math::diag(m_tmp_dd1, m_tmp_d1);
-      sigma_c = (m_acc_Snormij(c,rall) - m_tmp_d1) / m_acc_Nij(c);
-    }
-  }
-  machine.precompute();
-}
-
-
-bob::learn::em::IVectorTrainer& bob::learn::em::IVectorTrainer::operator=
-  (const bob::learn::em::IVectorTrainer &other)
-{
-  if (this != &other)
-  {
-    m_update_sigma = other.m_update_sigma;
-
-    m_acc_Nij_wij2.reference(bob::core::array::ccopy(other.m_acc_Nij_wij2));
-    m_acc_Fnormij_wij.reference(bob::core::array::ccopy(other.m_acc_Fnormij_wij));
-    m_acc_Nij.reference(bob::core::array::ccopy(other.m_acc_Nij));
-    m_acc_Snormij.reference(bob::core::array::ccopy(other.m_acc_Snormij));
-
-    m_tmp_wij.reference(bob::core::array::ccopy(other.m_tmp_wij));
-    m_tmp_wij2.reference(bob::core::array::ccopy(other.m_tmp_wij2));
-    m_tmp_d1.reference(bob::core::array::ccopy(other.m_tmp_d1));
-    m_tmp_t1.reference(bob::core::array::ccopy(other.m_tmp_t1));
-    m_tmp_dd1.reference(bob::core::array::ccopy(other.m_tmp_dd1));
-    m_tmp_dt1.reference(bob::core::array::ccopy(other.m_tmp_dt1));
-    m_tmp_tt1.reference(bob::core::array::ccopy(other.m_tmp_tt1));
-    m_tmp_tt2.reference(bob::core::array::ccopy(other.m_tmp_tt2));
-  }
-  return *this;
-}
-
-bool bob::learn::em::IVectorTrainer::operator==
-  (const bob::learn::em::IVectorTrainer &other) const
-{
-  return m_update_sigma == other.m_update_sigma &&
-         bob::core::array::isEqual(m_acc_Nij_wij2, other.m_acc_Nij_wij2) &&
-         bob::core::array::isEqual(m_acc_Fnormij_wij, other.m_acc_Fnormij_wij) &&
-         bob::core::array::isEqual(m_acc_Nij, other.m_acc_Nij) &&
-         bob::core::array::isEqual(m_acc_Snormij, other.m_acc_Snormij);
-}
-
-bool bob::learn::em::IVectorTrainer::operator!=
-  (const bob::learn::em::IVectorTrainer &other) const
-{
-  return !(this->operator==(other));
-}
-
-bool bob::learn::em::IVectorTrainer::is_similar_to
-  (const bob::learn::em::IVectorTrainer &other, const double r_epsilon,
-   const double a_epsilon) const
-{
-  return m_update_sigma == other.m_update_sigma &&
-         bob::core::array::isClose(m_acc_Nij_wij2, other.m_acc_Nij_wij2, r_epsilon, a_epsilon) &&
-         bob::core::array::isClose(m_acc_Fnormij_wij, other.m_acc_Fnormij_wij, r_epsilon, a_epsilon) &&
-         bob::core::array::isClose(m_acc_Nij, other.m_acc_Nij, r_epsilon, a_epsilon) &&
-         bob::core::array::isClose(m_acc_Snormij, other.m_acc_Snormij, r_epsilon, a_epsilon);
-}
diff --git a/bob/learn/em/cpp/JFABase.cpp b/bob/learn/em/cpp/JFABase.cpp
deleted file mode 100644
index b80bfd31d959f8e0e7fc3004a3ee0980b49e4a50..0000000000000000000000000000000000000000
--- a/bob/learn/em/cpp/JFABase.cpp
+++ /dev/null
@@ -1,75 +0,0 @@
-/**
- * @date Tue Jan 27 15:54:00 2015 +0200
- * @author Laurent El Shafey <Laurent.El-Shafey@idiap.ch>
- * @author Tiago de Freitas Pereira <tiago.pereira@idiap.ch>
- *
- * Copyright (C) Idiap Research Institute, Martigny, Switzerland
- */
-
-
-#include <bob.learn.em/JFABase.h>
-#include <bob.core/array_copy.h>
-#include <bob.math/linear.h>
-#include <bob.math/inv.h>
-#include <bob.learn.em/LinearScoring.h>
-#include <limits>
-
-
-//////////////////// JFABase ////////////////////
-bob::learn::em::JFABase::JFABase()
-{
-}
-
-bob::learn::em::JFABase::JFABase(const boost::shared_ptr<bob::learn::em::GMMMachine> ubm,
-    const size_t ru, const size_t rv):
-  m_base(ubm, ru, rv)
-{
-}
-
-bob::learn::em::JFABase::JFABase(const bob::learn::em::JFABase& other):
-  m_base(other.m_base)
-{
-}
-
-
-bob::learn::em::JFABase::JFABase(bob::io::base::HDF5File& config)
-{
-  load(config);
-}
-
-bob::learn::em::JFABase::~JFABase() {
-}
-
-void bob::learn::em::JFABase::save(bob::io::base::HDF5File& config) const
-{
-  config.setArray("U", m_base.getU());
-  config.setArray("V", m_base.getV());
-  config.setArray("d", m_base.getD());
-}
-
-void bob::learn::em::JFABase::load(bob::io::base::HDF5File& config)
-{
-  //reads all data directly into the member variables
-  blitz::Array<double,2> U = config.readArray<double,2>("U");
-  blitz::Array<double,2> V = config.readArray<double,2>("V");
-  blitz::Array<double,1> d = config.readArray<double,1>("d");
-  const int ru = U.extent(1);
-  const int rv = V.extent(1);
-  if (!m_base.getUbm())
-    m_base.resize(ru, rv, U.extent(0));
-  else
-    m_base.resize(ru, rv);
-  m_base.setU(U);
-  m_base.setV(V);
-  m_base.setD(d);
-}
-
-bob::learn::em::JFABase&
-bob::learn::em::JFABase::operator=(const bob::learn::em::JFABase& other)
-{
-  if (this != &other)
-  {
-    m_base = other.m_base;
-  }
-  return *this;
-}
diff --git a/bob/learn/em/cpp/JFAMachine.cpp b/bob/learn/em/cpp/JFAMachine.cpp
deleted file mode 100644
index 508c5008bcb59479e4a966eb6de31a6f14649d99..0000000000000000000000000000000000000000
--- a/bob/learn/em/cpp/JFAMachine.cpp
+++ /dev/null
@@ -1,206 +0,0 @@
-/**
- * @date Tue Jan 27 16:47:00 2015 +0200
- * @author Laurent El Shafey <Laurent.El-Shafey@idiap.ch>
- * @author Tiago de Freitas Pereira <tiago.pereira@idiap.ch>
- *
- * Copyright (C) Idiap Research Institute, Martigny, Switzerland
- */
-
-
-#include <bob.learn.em/JFAMachine.h>
-#include <bob.core/array_copy.h>
-#include <bob.math/linear.h>
-#include <bob.math/inv.h>
-#include <bob.learn.em/LinearScoring.h>
-#include <limits>
-
-
-//////////////////// JFAMachine ////////////////////
-bob::learn::em::JFAMachine::JFAMachine():
-  m_y(1), m_z(1)
-{
-  resizeTmp();
-}
-
-bob::learn::em::JFAMachine::JFAMachine(const boost::shared_ptr<bob::learn::em::JFABase> jfa_base):
-  m_jfa_base(jfa_base),
-  m_y(jfa_base->getDimRv()), m_z(jfa_base->getSupervectorLength())
-{
-  if (!m_jfa_base->getUbm()) throw std::runtime_error("No UBM was set in the JFA machine.");
-  updateCache();
-  resizeTmp();
-}
-
-
-bob::learn::em::JFAMachine::JFAMachine(const bob::learn::em::JFAMachine& other):
-  m_jfa_base(other.m_jfa_base),
-  m_y(bob::core::array::ccopy(other.m_y)),
-  m_z(bob::core::array::ccopy(other.m_z))
-{
-  updateCache();
-  resizeTmp();
-}
-
-bob::learn::em::JFAMachine::JFAMachine(bob::io::base::HDF5File& config)
-{
-  load(config);
-}
-
-bob::learn::em::JFAMachine::~JFAMachine() {
-}
-
-bob::learn::em::JFAMachine&
-bob::learn::em::JFAMachine::operator=(const bob::learn::em::JFAMachine& other)
-{
-  if (this != &other)
-  {
-    m_jfa_base = other.m_jfa_base;
-    m_y.reference(bob::core::array::ccopy(other.m_y));
-    m_z.reference(bob::core::array::ccopy(other.m_z));
-  }
-  return *this;
-}
-
-bool bob::learn::em::JFAMachine::operator==(const bob::learn::em::JFAMachine& other) const
-{
-  return (*m_jfa_base == *(other.m_jfa_base) &&
-          bob::core::array::isEqual(m_y, other.m_y) &&
-          bob::core::array::isEqual(m_z, other.m_z));
-}
-
-bool bob::learn::em::JFAMachine::operator!=(const bob::learn::em::JFAMachine& b) const
-{
-  return !(this->operator==(b));
-}
-
-
-bool bob::learn::em::JFAMachine::is_similar_to(const bob::learn::em::JFAMachine& b,
-    const double r_epsilon, const double a_epsilon) const
-{
-  return (m_jfa_base->is_similar_to(*(b.m_jfa_base), r_epsilon, a_epsilon) &&
-          bob::core::array::isClose(m_y, b.m_y, r_epsilon, a_epsilon) &&
-          bob::core::array::isClose(m_z, b.m_z, r_epsilon, a_epsilon));
-}
-
-void bob::learn::em::JFAMachine::save(bob::io::base::HDF5File& config) const
-{
-  config.setArray("y", m_y);
-  config.setArray("z", m_z);
-}
-
-void bob::learn::em::JFAMachine::load(bob::io::base::HDF5File& config)
-{
-  //reads all data directly into the member variables
-  blitz::Array<double,1> y = config.readArray<double,1>("y");
-  blitz::Array<double,1> z = config.readArray<double,1>("z");
-  if (!m_jfa_base)
-  {
-    m_y.resize(y.extent(0));
-    m_z.resize(z.extent(0));
-  }
-  setY(y);
-  setZ(z);
-  // update cache
-  updateCache();
-  resizeTmp();
-}
-
-
-void bob::learn::em::JFAMachine::setY(const blitz::Array<double,1>& y)
-{
-  if(y.extent(0) != m_y.extent(0)) { //checks dimension
-    boost::format m("size of input vector `y' (%d) does not match the expected size (%d)");
-    m % y.extent(0) % m_y.extent(0);
-    throw std::runtime_error(m.str());
-  }
-  m_y.reference(bob::core::array::ccopy(y));
-  // update cache
-  updateCache();
-}
-
-void bob::learn::em::JFAMachine::setZ(const blitz::Array<double,1>& z)
-{
-  if(z.extent(0) != m_z.extent(0)) { //checks dimension
-    boost::format m("size of input vector `z' (%d) does not match the expected size (%d)");
-    m % z.extent(0) % m_z.extent(0);
-    throw std::runtime_error(m.str());
-  }
-  m_z.reference(bob::core::array::ccopy(z));
-  // update cache
-  updateCache();
-}
-
-void bob::learn::em::JFAMachine::setJFABase(const boost::shared_ptr<bob::learn::em::JFABase> jfa_base)
-{
-  if (!jfa_base->getUbm())
-    throw std::runtime_error("No UBM was set in the JFA machine.");
-  m_jfa_base = jfa_base;
-  // Resize variables
-  resize();
-}
-
-void bob::learn::em::JFAMachine::resize()
-{
-  m_y.resizeAndPreserve(getDimRv());
-  m_z.resizeAndPreserve(getSupervectorLength());
-  updateCache();
-  resizeTmp();
-}
-
-void bob::learn::em::JFAMachine::resizeTmp()
-{
-  if (m_jfa_base)
-  {
-    m_tmp_Ux.resize(getSupervectorLength());
-  }
-}
-
-void bob::learn::em::JFAMachine::updateCache()
-{
-  if (m_jfa_base)
-  {
-    // m + Vy + Dz
-    m_cache_mVyDz.resize(getSupervectorLength());
-    bob::math::prod(m_jfa_base->getV(), m_y, m_cache_mVyDz);
-    m_cache_mVyDz += m_jfa_base->getD()*m_z + m_jfa_base->getUbm()->getMeanSupervector();
-    m_cache_x.resize(getDimRu());
-  }
-}
-
-void bob::learn::em::JFAMachine::estimateUx(const bob::learn::em::GMMStats& gmm_stats,
-  blitz::Array<double,1>& Ux)
-{
-  estimateX(gmm_stats, m_cache_x);
-  bob::math::prod(m_jfa_base->getU(), m_cache_x, Ux);
-}
-
-double bob::learn::em::JFAMachine::forward(const bob::learn::em::GMMStats& input)
-{
-  return forward_(input);
-}
-
-double bob::learn::em::JFAMachine::forward(const bob::learn::em::GMMStats& gmm_stats,
-  const blitz::Array<double,1>& Ux)
-{
-  // Checks that a Base machine has been set
-  if (!m_jfa_base) throw std::runtime_error("No UBM was set in the JFA machine.");
-
-  return bob::learn::em::linearScoring(m_cache_mVyDz,
-            m_jfa_base->getUbm()->getMeanSupervector(), m_jfa_base->getUbm()->getVarianceSupervector(),
-            gmm_stats, Ux, true);
-}
-
-double bob::learn::em::JFAMachine::forward_(const bob::learn::em::GMMStats& input)
-{
-  // Checks that a Base machine has been set
-  if (!m_jfa_base) throw std::runtime_error("No UBM was set in the JFA machine.");
-
-  // Ux and GMMStats
-  estimateX(input, m_cache_x);
-  bob::math::prod(m_jfa_base->getU(), m_cache_x, m_tmp_Ux);
-
-  return bob::learn::em::linearScoring(m_cache_mVyDz,
-            m_jfa_base->getUbm()->getMeanSupervector(), m_jfa_base->getUbm()->getVarianceSupervector(),
-            input, m_tmp_Ux, true);
-}
-
diff --git a/bob/learn/em/cpp/JFATrainer.cpp b/bob/learn/em/cpp/JFATrainer.cpp
deleted file mode 100644
index 23591c8845d3795e48e9865d58fee23cf95360b8..0000000000000000000000000000000000000000
--- a/bob/learn/em/cpp/JFATrainer.cpp
+++ /dev/null
@@ -1,199 +0,0 @@
-/**
- * @date Tue Jul 19 12:16:17 2011 +0200
- * @author Laurent El Shafey <Laurent.El-Shafey@idiap.ch>
- *
- * @brief Joint Factor Analysis Trainer
- *
- * Copyright (C) Idiap Research Institute, Martigny, Switzerland
- */
-
-#include <bob.learn.em/JFATrainer.h>
-#include <bob.core/check.h>
-#include <bob.core/array_copy.h>
-#include <bob.core/array_random.h>
-#include <bob.math/inv.h>
-#include <bob.math/linear.h>
-#include <bob.core/check.h>
-#include <bob.core/array_repmat.h>
-#include <algorithm>
-
-
-//////////////////////////// JFATrainer ///////////////////////////
-bob::learn::em::JFATrainer::JFATrainer():
-  m_rng(new boost::mt19937())
-{}
-
-bob::learn::em::JFATrainer::JFATrainer(const bob::learn::em::JFATrainer& other):
- m_rng(other.m_rng)
-{}
-
-bob::learn::em::JFATrainer::~JFATrainer()
-{}
-
-bob::learn::em::JFATrainer& bob::learn::em::JFATrainer::operator=
-(const bob::learn::em::JFATrainer& other)
-{
-  if (this != &other)
-  {
-    //m_max_iterations = other.m_max_iterations;
-    m_rng = other.m_rng;
-  }
-  return *this;
-}
-
-bool bob::learn::em::JFATrainer::operator==(const bob::learn::em::JFATrainer& b) const
-{
-  //return m_max_iterations == b.m_max_iterations && *m_rng == *(b.m_rng);
-  return *m_rng == *(b.m_rng);
-}
-
-bool bob::learn::em::JFATrainer::operator!=(const bob::learn::em::JFATrainer& b) const
-{
-  return !(this->operator==(b));
-}
-
-bool bob::learn::em::JFATrainer::is_similar_to(const bob::learn::em::JFATrainer& b,
-  const double r_epsilon, const double a_epsilon) const
-{
-  //return m_max_iterations == b.m_max_iterations && *m_rng == *(b.m_rng);
-  return *m_rng == *(b.m_rng);
-}
-
-void bob::learn::em::JFATrainer::initialize(bob::learn::em::JFABase& machine,
-  const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& ar)
-{
-  m_base_trainer.initUbmNidSumStatistics(machine.getBase(), ar);
-  m_base_trainer.initializeXYZ(ar);
-
-  blitz::Array<double,2>& U = machine.updateU();
-  bob::core::array::randn(*m_rng, U);
-  blitz::Array<double,2>& V = machine.updateV();
-  bob::core::array::randn(*m_rng, V);
-  blitz::Array<double,1>& D = machine.updateD();
-  bob::core::array::randn(*m_rng, D);
-  machine.precompute();
-}
-
-void bob::learn::em::JFATrainer::eStep1(bob::learn::em::JFABase& machine,
-  const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& ar)
-{
-  const bob::learn::em::FABase& base = machine.getBase();
-  m_base_trainer.updateY(base, ar);
-  m_base_trainer.computeAccumulatorsV(base, ar);
-}
-
-void bob::learn::em::JFATrainer::mStep1(bob::learn::em::JFABase& machine,
-  const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& ar)
-{
-  blitz::Array<double,2>& V = machine.updateV();
-  m_base_trainer.updateV(V);
-}
-
-void bob::learn::em::JFATrainer::finalize1(bob::learn::em::JFABase& machine,
-  const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& ar)
-{
-  const bob::learn::em::FABase& base = machine.getBase();
-  m_base_trainer.updateY(base, ar);
-}
-
-
-void bob::learn::em::JFATrainer::eStep2(bob::learn::em::JFABase& machine,
-  const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& ar)
-{
-  const bob::learn::em::FABase& base = machine.getBase();
-  m_base_trainer.updateX(base, ar);
-  m_base_trainer.computeAccumulatorsU(base, ar);
-}
-
-void bob::learn::em::JFATrainer::mStep2(bob::learn::em::JFABase& machine,
-  const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& ar)
-{
-  blitz::Array<double,2>& U = machine.updateU();
-  m_base_trainer.updateU(U);
-  machine.precompute();
-}
-
-void bob::learn::em::JFATrainer::finalize2(bob::learn::em::JFABase& machine,
-  const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& ar)
-{
-  const bob::learn::em::FABase& base = machine.getBase();
-  m_base_trainer.updateX(base, ar);
-}
-
-
-void bob::learn::em::JFATrainer::eStep3(bob::learn::em::JFABase& machine,
-  const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& ar)
-{
-  const bob::learn::em::FABase& base = machine.getBase();
-  m_base_trainer.updateZ(base, ar);
-  m_base_trainer.computeAccumulatorsD(base, ar);
-}
-
-void bob::learn::em::JFATrainer::mStep3(bob::learn::em::JFABase& machine,
-  const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& ar)
-{
-  blitz::Array<double,1>& d = machine.updateD();
-  m_base_trainer.updateD(d);
-}
-
-void bob::learn::em::JFATrainer::finalize3(bob::learn::em::JFABase& machine,
-  const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& ar)
-{
-}
-
-/*
-void bob::learn::em::JFATrainer::train_loop(bob::learn::em::JFABase& machine,
-  const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& ar)
-{
-  // V subspace
-  for (size_t i=0; i<m_max_iterations; ++i) {
-    eStep1(machine, ar);
-    mStep1(machine, ar);
-  }
-  finalize1(machine, ar);
-  // U subspace
-  for (size_t i=0; i<m_max_iterations; ++i) {
-    eStep2(machine, ar);
-    mStep2(machine, ar);
-  }
-  finalize2(machine, ar);
-  // d subspace
-  for (size_t i=0; i<m_max_iterations; ++i) {
-    eStep3(machine, ar);
-    mStep3(machine, ar);
-  }
-  finalize3(machine, ar);
-}*/
-
-/*
-void bob::learn::em::JFATrainer::train(bob::learn::em::JFABase& machine,
-  const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& ar)
-{
-  initialize(machine, ar);
-  train_loop(machine, ar);
-}
-*/
-
-void bob::learn::em::JFATrainer::enroll(bob::learn::em::JFAMachine& machine,
-  const std::vector<boost::shared_ptr<bob::learn::em::GMMStats> >& ar,
-  const size_t n_iter)
-{
-  std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > > vvec;
-  vvec.push_back(ar);
-
-  const bob::learn::em::FABase& fb = machine.getJFABase()->getBase();
-
-  m_base_trainer.initUbmNidSumStatistics(fb, vvec);
-  m_base_trainer.initializeXYZ(vvec);
-
-  for (size_t i=0; i<n_iter; ++i) {
-    m_base_trainer.updateY(fb, vvec);
-    m_base_trainer.updateX(fb, vvec);
-    m_base_trainer.updateZ(fb, vvec);
-  }
-
-  const blitz::Array<double,1> y(m_base_trainer.getY()[0]);
-  const blitz::Array<double,1> z(m_base_trainer.getZ()[0]);
-  machine.setY(y);
-  machine.setZ(z);
-}
diff --git a/bob/learn/em/cpp/KMeansMachine.cpp b/bob/learn/em/cpp/KMeansMachine.cpp
deleted file mode 100644
index 395cb1460996cb0c77988515a3d9a8b1e7304eca..0000000000000000000000000000000000000000
--- a/bob/learn/em/cpp/KMeansMachine.cpp
+++ /dev/null
@@ -1,258 +0,0 @@
-/**
- * @date Tue May 10 11:35:58 2011 +0200
- * @author Francois Moulin <Francois.Moulin@idiap.ch>
- * @author Laurent El Shafey <Laurent.El-Shafey@idiap.ch>
- *
- * Copyright (C) Idiap Research Institute, Martigny, Switzerland
- */
-
-#include <bob.learn.em/KMeansMachine.h>
-
-#include <bob.core/assert.h>
-#include <bob.core/check.h>
-#include <bob.core/array_copy.h>
-#include <limits>
-
-bob::learn::em::KMeansMachine::KMeansMachine():
-  m_n_means(0), m_n_inputs(0), m_means(0,0),
-  m_cache_means(0,0)
-{
-  m_means = 0;
-}
-
-bob::learn::em::KMeansMachine::KMeansMachine(const size_t n_means, const size_t n_inputs):
-  m_n_means(n_means), m_n_inputs(n_inputs), m_means(n_means, n_inputs),
-  m_cache_means(n_means, n_inputs)
-{
-  m_means = 0;
-}
-
-bob::learn::em::KMeansMachine::KMeansMachine(const blitz::Array<double,2>& means):
-  m_n_means(means.extent(0)), m_n_inputs(means.extent(1)),
-  m_means(bob::core::array::ccopy(means)),
-  m_cache_means(means.shape())
-{
-}
-
-bob::learn::em::KMeansMachine::KMeansMachine(const bob::learn::em::KMeansMachine& other):
-  m_n_means(other.m_n_means), m_n_inputs(other.m_n_inputs),
-  m_means(bob::core::array::ccopy(other.m_means)),
-  m_cache_means(other.m_cache_means.shape())
-{
-}
-
-bob::learn::em::KMeansMachine::KMeansMachine(bob::io::base::HDF5File& config)
-{
-  load(config);
-}
-
-bob::learn::em::KMeansMachine::~KMeansMachine() { }
-
-bob::learn::em::KMeansMachine& bob::learn::em::KMeansMachine::operator=
-(const bob::learn::em::KMeansMachine& other)
-{
-  if(this != &other)
-  {
-    m_n_means = other.m_n_means;
-    m_n_inputs = other.m_n_inputs;
-    m_means.reference(bob::core::array::ccopy(other.m_means));
-    m_cache_means.resize(other.m_means.shape());
-  }
-  return *this;
-}
-
-bool bob::learn::em::KMeansMachine::operator==(const bob::learn::em::KMeansMachine& b) const
-{
-  return m_n_inputs == b.m_n_inputs && m_n_means == b.m_n_means &&
-         bob::core::array::isEqual(m_means, b.m_means);
-}
-
-bool bob::learn::em::KMeansMachine::operator!=(const bob::learn::em::KMeansMachine& b) const
-{
-  return !(this->operator==(b));
-}
-
-bool bob::learn::em::KMeansMachine::is_similar_to(const bob::learn::em::KMeansMachine& b,
-  const double r_epsilon, const double a_epsilon) const
-{
-  return m_n_inputs == b.m_n_inputs && m_n_means == b.m_n_means &&
-         bob::core::array::isClose(m_means, b.m_means, r_epsilon, a_epsilon);
-}
-
-void bob::learn::em::KMeansMachine::load(bob::io::base::HDF5File& config)
-{
-  //reads all data directly into the member variables
-  m_means.reference(config.readArray<double,2>("means"));
-  m_n_means = m_means.extent(0);
-  m_n_inputs = m_means.extent(1);
-  m_cache_means.resize(m_n_means, m_n_inputs);
-}
-
-void bob::learn::em::KMeansMachine::save(bob::io::base::HDF5File& config) const
-{
-  config.setArray("means", m_means);
-}
-
-void bob::learn::em::KMeansMachine::setMeans(const blitz::Array<double,2> &means)
-{
-  bob::core::array::assertSameShape(means, m_means);
-  m_means = means;
-}
-
-void bob::learn::em::KMeansMachine::setMean(const size_t i, const blitz::Array<double,1> &mean)
-{
-  if(i>=m_n_means) {
-    boost::format m("cannot set mean with index %lu: out of bounds [0,%lu[");
-    m % i % m_n_means;
-    throw std::runtime_error(m.str());
-  }
-  bob::core::array::assertSameDimensionLength(mean.extent(0), m_means.extent(1));
-  m_means(i,blitz::Range::all()) = mean;
-}
-
-const blitz::Array<double,1> bob::learn::em::KMeansMachine::getMean(const size_t i) const
-{
-  if(i>=m_n_means) {
-    boost::format m("cannot get mean with index %lu: out of bounds [0,%lu[");
-    m % i % m_n_means;
-    throw std::runtime_error(m.str());
-  }
-
-  return m_means(i,blitz::Range::all());
-
-}
-
-double bob::learn::em::KMeansMachine::getDistanceFromMean(const blitz::Array<double,1> &x,
-  const size_t i) const
-{
-  return blitz::sum(blitz::pow2(m_means(i,blitz::Range::all()) - x));
-}
-
-void bob::learn::em::KMeansMachine::getClosestMean(const blitz::Array<double,1> &x,
-  size_t &closest_mean, double &min_distance) const
-{
-  min_distance = std::numeric_limits<double>::max();
-
-  for(size_t i=0; i<m_n_means; ++i) {
-    double this_distance = getDistanceFromMean(x,i);
-    if(this_distance < min_distance) {
-      min_distance = this_distance;
-      closest_mean = i;
-    }
-  }
-}
-
-double bob::learn::em::KMeansMachine::getMinDistance(const blitz::Array<double,1>& input) const
-{
-  size_t closest_mean = 0;
-  double min_distance = 0;
-  getClosestMean(input,closest_mean,min_distance);
-  return min_distance;
-}
-
-void bob::learn::em::KMeansMachine::getVariancesAndWeightsForEachClusterInit(blitz::Array<double,2>& variances, blitz::Array<double,1>& weights) const
-{
-  // check arguments
-  bob::core::array::assertSameShape(variances, m_means);
-  bob::core::array::assertSameDimensionLength(weights.extent(0), m_n_means);
-
-  // initialise output arrays
-  bob::core::array::assertSameShape(variances, m_means);
-  bob::core::array::assertSameDimensionLength(weights.extent(0), m_n_means);
-  variances = 0;
-  weights = 0;
-
-  // initialise (temporary) mean array
-  m_cache_means = 0;
-}
-
-void bob::learn::em::KMeansMachine::getVariancesAndWeightsForEachClusterAcc(const blitz::Array<double,2>& data, blitz::Array<double,2>& variances, blitz::Array<double,1>& weights) const
-{
-  // check arguments
-  bob::core::array::assertSameShape(variances, m_means);
-  bob::core::array::assertSameDimensionLength(weights.extent(0), m_n_means);
-
-  // iterate over data
-  blitz::Range a = blitz::Range::all();
-  for(int i=0; i<data.extent(0); ++i) {
-    // - get example
-    blitz::Array<double,1> x(data(i,a));
-
-    // - find closest mean
-    size_t closest_mean = 0;
-    double min_distance = 0;
-    getClosestMean(x,closest_mean,min_distance);
-
-    // - accumulate stats
-    m_cache_means(closest_mean, blitz::Range::all()) += x;
-    variances(closest_mean, blitz::Range::all()) += blitz::pow2(x);
-    ++weights(closest_mean);
-  }
-}
-
-void bob::learn::em::KMeansMachine::getVariancesAndWeightsForEachClusterFin(blitz::Array<double,2>& variances, blitz::Array<double,1>& weights) const
-{
-  // check arguments
-  bob::core::array::assertSameShape(variances, m_means);
-  bob::core::array::assertSameDimensionLength(weights.extent(0), m_n_means);
-
-  // calculate final variances and weights
-  blitz::firstIndex idx1;
-  blitz::secondIndex idx2;
-
-  // find means
-  m_cache_means = m_cache_means(idx1,idx2) / weights(idx1);
-
-  // find variances
-  variances = variances(idx1,idx2) / weights(idx1);
-  variances -= blitz::pow2(m_cache_means);
-
-  // find weights
-  weights = weights / blitz::sum(weights);
-}
-
-void bob::learn::em::KMeansMachine::setCacheMeans(const blitz::Array<double,2> &cache_means)
-{
-  bob::core::array::assertSameShape(cache_means, m_cache_means);
-  m_cache_means = cache_means;
-}
-
-void bob::learn::em::KMeansMachine::getVariancesAndWeightsForEachCluster(const blitz::Array<double,2>& data, blitz::Array<double,2>& variances, blitz::Array<double,1>& weights) const
-{
-  // initialise
-  getVariancesAndWeightsForEachClusterInit(variances, weights);
-  // accumulate
-  getVariancesAndWeightsForEachClusterAcc(data, variances, weights);
-  // merge/finalize
-  getVariancesAndWeightsForEachClusterFin(variances, weights);
-}
-
-void bob::learn::em::KMeansMachine::forward(const blitz::Array<double,1>& input, double& output) const
-{
-  if(static_cast<size_t>(input.extent(0)) != m_n_inputs) {
-    boost::format m("machine input size (%u) does not match the size of input array (%d)");
-    m % m_n_inputs % input.extent(0);
-    throw std::runtime_error(m.str());
-  }
-  forward_(input,output);
-}
-
-void bob::learn::em::KMeansMachine::forward_(const blitz::Array<double,1>& input, double& output) const
-{
-  output = getMinDistance(input);
-}
-
-void bob::learn::em::KMeansMachine::resize(const size_t n_means, const size_t n_inputs)
-{
-  m_n_means = n_means;
-  m_n_inputs = n_inputs;
-  m_means.resizeAndPreserve(n_means, n_inputs);
-  m_cache_means.resizeAndPreserve(n_means, n_inputs);
-}
-
-namespace bob { namespace learn { namespace em {
-  std::ostream& operator<<(std::ostream& os, const KMeansMachine& km) {
-    os << "Means = " << km.m_means << std::endl;
-    return os;
-  }
-} } }
diff --git a/bob/learn/em/cpp/KMeansTrainer.cpp b/bob/learn/em/cpp/KMeansTrainer.cpp
deleted file mode 100644
index 0c6e3c6da2e8de4addd0e97ead72086952c23329..0000000000000000000000000000000000000000
--- a/bob/learn/em/cpp/KMeansTrainer.cpp
+++ /dev/null
@@ -1,228 +0,0 @@
-/**
- * @date Tue May 10 11:35:58 2011 +0200
- * @author Francois Moulin <Francois.Moulin@idiap.ch>
- * @author Laurent El Shafey <Laurent.El-Shafey@idiap.ch>
- *
- * Copyright (C) Idiap Research Institute, Martigny, Switzerland
- */
-
-#include <bob.learn.em/KMeansTrainer.h>
-#include <bob.core/array_copy.h>
-
-#include <boost/random.hpp>
-#include <bob.core/random.h>
-
-
-bob::learn::em::KMeansTrainer::KMeansTrainer(InitializationMethod i_m):
-m_rng(new boost::mt19937()),
-m_average_min_distance(0),
-m_zeroethOrderStats(0),
-m_firstOrderStats(0)
-{
-  m_initialization_method = i_m;
-}
-
-
-bob::learn::em::KMeansTrainer::KMeansTrainer(const bob::learn::em::KMeansTrainer& other){
-
-  m_initialization_method = other.m_initialization_method;
-  m_rng                   = other.m_rng;
-  m_average_min_distance  = other.m_average_min_distance;
-  m_zeroethOrderStats     = bob::core::array::ccopy(other.m_zeroethOrderStats);
-  m_firstOrderStats       = bob::core::array::ccopy(other.m_firstOrderStats);
-}
-
-
-bob::learn::em::KMeansTrainer& bob::learn::em::KMeansTrainer::operator=
-(const bob::learn::em::KMeansTrainer& other)
-{
-  if(this != &other)
-  {
-    m_rng                         = other.m_rng;
-    m_initialization_method       = other.m_initialization_method;
-    m_average_min_distance        = other.m_average_min_distance;
-
-    m_zeroethOrderStats = bob::core::array::ccopy(other.m_zeroethOrderStats);
-    m_firstOrderStats   = bob::core::array::ccopy(other.m_firstOrderStats);
-  }
-  return *this;
-}
-
-
-bool bob::learn::em::KMeansTrainer::operator==(const bob::learn::em::KMeansTrainer& b) const {
-  return
-         m_initialization_method == b.m_initialization_method &&
-         *m_rng == *(b.m_rng) && m_average_min_distance == b.m_average_min_distance &&
-         bob::core::array::hasSameShape(m_zeroethOrderStats, b.m_zeroethOrderStats) &&
-         bob::core::array::hasSameShape(m_firstOrderStats, b.m_firstOrderStats) &&
-         blitz::all(m_zeroethOrderStats == b.m_zeroethOrderStats) &&
-         blitz::all(m_firstOrderStats == b.m_firstOrderStats);
-}
-
-bool bob::learn::em::KMeansTrainer::operator!=(const bob::learn::em::KMeansTrainer& b) const {
-  return !(this->operator==(b));
-}
-
-void bob::learn::em::KMeansTrainer::initialize(bob::learn::em::KMeansMachine& kmeans,
-  const blitz::Array<double,2>& ar)
-{
-  // split data into as many chunks as there are means
-  size_t n_data = ar.extent(0);
-
-  // assign the i'th mean to a random example within the i'th chunk
-  blitz::Range a = blitz::Range::all();
-  if(m_initialization_method == RANDOM || m_initialization_method == RANDOM_NO_DUPLICATE) // Random initialization
-  {
-    unsigned int n_chunk = n_data / kmeans.getNMeans();
-    size_t n_max_trials = (size_t)n_chunk * 5;
-    blitz::Array<double,1> cur_mean;
-    if(m_initialization_method == RANDOM_NO_DUPLICATE)
-      cur_mean.resize(kmeans.getNInputs());
-
-    for(size_t i=0; i<kmeans.getNMeans(); ++i)
-    {
-      boost::uniform_int<> die(i*n_chunk, (i+1)*n_chunk-1);
-
-      // get random index within chunk
-      unsigned int index = die(*m_rng);
-
-      // get the example at that index
-      blitz::Array<double, 1> mean = ar(index,a);
-
-      if(m_initialization_method == RANDOM_NO_DUPLICATE)
-      {
-        size_t count = 0;
-        while(count < n_max_trials)
-        {
-          // check that the selected sampled is different than all the previously
-          // selected ones
-          bool valid = true;
-          for(size_t j=0; j<i && valid; ++j)
-          {
-            cur_mean = kmeans.getMean(j);
-            valid = blitz::any(mean != cur_mean);
-          }
-          // if different, stop otherwise, try with another one
-          if(valid)
-            break;
-          else
-          {
-            index = die(*m_rng);
-            mean = ar(index,a);
-            ++count;
-          }
-        }
-        // Initialization fails
-        if(count >= n_max_trials) {
-          boost::format m("initialization failure: surpassed the maximum number of trials (%u)");
-          m % n_max_trials;
-          throw std::runtime_error(m.str());
-        }
-      }
-
-      // set the mean
-      kmeans.setMean(i, mean);
-    }
-  }
-  else // K-Means++
-  {
-    // 1.a. Selects one sample randomly
-    boost::uniform_int<> die(0, n_data-1);
-    //   Gets the example at a random index
-    blitz::Array<double,1> mean = ar(die(*m_rng),a);
-    kmeans.setMean(0, mean);
-
-    // 1.b. Loops, computes probability distribution and select samples accordingly
-    blitz::Array<double,1> weights(n_data);
-    for(size_t m=1; m<kmeans.getNMeans(); ++m)
-    {
-      // For each sample, puts the distance to the closest mean in the weight vector
-      for(size_t s=0; s<n_data; ++s)
-      {
-        blitz::Array<double,1> s_cur = ar(s,a);
-        double& w_cur = weights(s);
-        // Initializes with the distance to first mean
-        w_cur = kmeans.getDistanceFromMean(s_cur, 0);
-        // Loops over the remaining mean and update the mean distance if required
-        for(size_t i=1; i<m; ++i)
-          w_cur = std::min(w_cur, kmeans.getDistanceFromMean(s_cur, i));
-      }
-      // Square and normalize the weights vectors such that
-      // \f$weights[x] = D(x)^{2} \sum_{y} D(y)^{2}\f$
-      weights = blitz::pow2(weights);
-      weights /= blitz::sum(weights);
-
-      // Takes a sample according to the weights distribution
-      // Blitz iterators is fine as the weights array should be C-style contiguous
-      bob::core::array::assertCContiguous(weights);
-      bob::core::random::discrete_distribution<> die2(weights.begin(), weights.end());
-      blitz::Array<double,1> new_mean = ar(die2(*m_rng),a);
-      kmeans.setMean(m, new_mean);
-    }
-  }
-}
-
-void bob::learn::em::KMeansTrainer::eStep(bob::learn::em::KMeansMachine& kmeans,
-  const blitz::Array<double,2>& ar)
-{
-  // initialise the accumulators
-  resetAccumulators(kmeans);
-
-  // iterate over data samples
-  blitz::Range a = blitz::Range::all();
-  for(int i=0; i<ar.extent(0); ++i) {
-    // get example
-    blitz::Array<double, 1> x(ar(i,a));
-
-    // find closest mean, and distance from that mean
-    size_t closest_mean = 0;
-    double min_distance = 0;
-    kmeans.getClosestMean(x,closest_mean,min_distance);
-
-    // accumulate the stats
-    m_average_min_distance += min_distance;
-    ++m_zeroethOrderStats(closest_mean);
-    m_firstOrderStats(closest_mean,blitz::Range::all()) += x;
-  }
-  m_average_min_distance /= static_cast<double>(ar.extent(0));
-}
-
-void bob::learn::em::KMeansTrainer::mStep(bob::learn::em::KMeansMachine& kmeans)
-{
-  blitz::Array<double,2>& means = kmeans.updateMeans();
-  for(size_t i=0; i<kmeans.getNMeans(); ++i)
-  {
-    means(i,blitz::Range::all()) =
-      m_firstOrderStats(i,blitz::Range::all()) / m_zeroethOrderStats(i);
-  }
-}
-
-double bob::learn::em::KMeansTrainer::computeLikelihood(bob::learn::em::KMeansMachine& kmeans)
-{
-  return m_average_min_distance;
-}
-
-
-void bob::learn::em::KMeansTrainer::resetAccumulators(bob::learn::em::KMeansMachine& kmeans)
-{
-   // Resize the accumulator
-  m_zeroethOrderStats.resize(kmeans.getNMeans());
-  m_firstOrderStats.resize(kmeans.getNMeans(), kmeans.getNInputs());
-
-  // initialize with 0
-  m_average_min_distance = 0;
-  m_zeroethOrderStats = 0;
-  m_firstOrderStats = 0;
-}
-
-void bob::learn::em::KMeansTrainer::setZeroethOrderStats(const blitz::Array<double,1>& zeroethOrderStats)
-{
-  bob::core::array::assertSameShape(m_zeroethOrderStats, zeroethOrderStats);
-  m_zeroethOrderStats = zeroethOrderStats;
-}
-
-void bob::learn::em::KMeansTrainer::setFirstOrderStats(const blitz::Array<double,2>& firstOrderStats)
-{
-  bob::core::array::assertSameShape(m_firstOrderStats, firstOrderStats);
-  m_firstOrderStats = firstOrderStats;
-}
diff --git a/bob/learn/em/cpp/LinearScoring.cpp b/bob/learn/em/cpp/LinearScoring.cpp
deleted file mode 100644
index 63fdb1cce18f97686948b7ed0cfb0db3413a530b..0000000000000000000000000000000000000000
--- a/bob/learn/em/cpp/LinearScoring.cpp
+++ /dev/null
@@ -1,168 +0,0 @@
-/**
- * @date Wed Jul 13 16:00:04 2011 +0200
- * @author Francois Moulin <Francois.Moulin@idiap.ch>
- *
- * Copyright (C) Idiap Research Institute, Martigny, Switzerland
- */
-#include <bob.learn.em/LinearScoring.h>
-#include <bob.math/linear.h>
-
-
-static void _linearScoring(const std::vector<blitz::Array<double,1> >& models,
-                   const blitz::Array<double,1>& ubm_mean,
-                   const blitz::Array<double,1>& ubm_variance,
-                   const std::vector<boost::shared_ptr<const bob::learn::em::GMMStats> >& test_stats,
-                   const std::vector<blitz::Array<double,1> >* test_channelOffset,
-                   const bool frame_length_normalisation,
-                   blitz::Array<double,2>& scores)
-{
-  int C = test_stats[0]->sumPx.extent(0);
-  int D = test_stats[0]->sumPx.extent(1);
-  int CD = C*D;
-  int Tt = test_stats.size();
-  int Tm = models.size();
-
-  // Check output size
-  bob::core::array::assertSameDimensionLength(scores.extent(0), models.size());
-  bob::core::array::assertSameDimensionLength(scores.extent(1), test_stats.size());
-
-  blitz::Array<double,2> A(Tm, CD);
-  blitz::Array<double,2> B(CD, Tt);
-
-  // 1) Compute A
-  for(int t=0; t<Tm; ++t) {
-    blitz::Array<double, 1> tmp = A(t, blitz::Range::all());
-    tmp = (models[t] - ubm_mean) / ubm_variance;
-  }
-
-  // 2) Compute B
-  if(test_channelOffset == 0) {
-    for(int t=0; t<Tt; ++t)
-      for(int s=0; s<CD; ++s)
-        B(s, t) = test_stats[t]->sumPx(s/D, s%D) - (ubm_mean(s) * test_stats[t]->n(s/D));
-  }
-  else {
-    bob::core::array::assertSameDimensionLength((*test_channelOffset).size(), Tt);
-
-    for(int t=0; t<Tt; ++t) {
-      bob::core::array::assertSameDimensionLength((*test_channelOffset)[t].extent(0), CD);
-      for(int s=0; s<CD; ++s)
-        B(s, t) = test_stats[t]->sumPx(s/D, s%D) - (test_stats[t]->n(s/D) * (ubm_mean(s) + (*test_channelOffset)[t](s)));
-    }
-  }
-
-  // Apply the normalisation if needed
-  if(frame_length_normalisation) {
-    for(int t=0; t<Tt; ++t) {
-      double sum_N = test_stats[t]->T;
-      blitz::Array<double, 1> v_t = B(blitz::Range::all(),t);
-
-      if (sum_N <= std::numeric_limits<double>::epsilon() && sum_N >= -std::numeric_limits<double>::epsilon())
-        v_t = 0;
-      else
-        v_t /= sum_N;
-    }
-  }
-
-  // 3) Compute LLR
-  bob::math::prod(A, B, scores);
-}
-
-
-void bob::learn::em::linearScoring(const std::vector<blitz::Array<double,1> >& models,
-                   const blitz::Array<double,1>& ubm_mean, const blitz::Array<double,1>& ubm_variance,
-                   const std::vector<boost::shared_ptr<const bob::learn::em::GMMStats> >& test_stats,
-                   const std::vector<blitz::Array<double,1> >& test_channelOffset,
-                   const bool frame_length_normalisation,
-                   blitz::Array<double, 2>& scores)
-{
-  _linearScoring(models, ubm_mean, ubm_variance, test_stats, &test_channelOffset, frame_length_normalisation, scores);
-}
-
-void bob::learn::em::linearScoring(const std::vector<blitz::Array<double,1> >& models,
-                   const blitz::Array<double,1>& ubm_mean, const blitz::Array<double,1>& ubm_variance,
-                   const std::vector<boost::shared_ptr<const bob::learn::em::GMMStats> >& test_stats,
-                   const bool frame_length_normalisation,
-                   blitz::Array<double, 2>& scores)
-{
-  _linearScoring(models, ubm_mean, ubm_variance, test_stats, 0, frame_length_normalisation, scores);
-}
-
-void bob::learn::em::linearScoring(const std::vector<boost::shared_ptr<const bob::learn::em::GMMMachine> >& models,
-                   const bob::learn::em::GMMMachine& ubm,
-                   const std::vector<boost::shared_ptr<const bob::learn::em::GMMStats> >& test_stats,
-                   const bool frame_length_normalisation,
-                   blitz::Array<double, 2>& scores)
-{
-  int C = test_stats[0]->sumPx.extent(0);
-  int D = test_stats[0]->sumPx.extent(1);
-  int CD = C*D;
-  std::vector<blitz::Array<double,1> > models_b;
-  // Allocate and get the mean supervector
-  for(size_t i=0; i<models.size(); ++i) {
-    blitz::Array<double,1> mod(CD);
-    mod = models[i]->getMeanSupervector();
-    models_b.push_back(mod);
-  }
-  const blitz::Array<double,1>& ubm_mean = ubm.getMeanSupervector();
-  const blitz::Array<double,1>& ubm_variance = ubm.getVarianceSupervector();
-  _linearScoring(models_b, ubm_mean, ubm_variance, test_stats, 0, frame_length_normalisation, scores);
-}
-
-void bob::learn::em::linearScoring(const std::vector<boost::shared_ptr<const bob::learn::em::GMMMachine> >& models,
-                   const bob::learn::em::GMMMachine& ubm,
-                   const std::vector<boost::shared_ptr<const bob::learn::em::GMMStats> >& test_stats,
-                   const std::vector<blitz::Array<double,1> >& test_channelOffset,
-                   const bool frame_length_normalisation,
-                   blitz::Array<double, 2>& scores)
-{
-  int C = test_stats[0]->sumPx.extent(0);
-  int D = test_stats[0]->sumPx.extent(1);
-  int CD = C*D;
-  std::vector<blitz::Array<double,1> > models_b;
-  // Allocate and get the mean supervector
-  for(size_t i=0; i<models.size(); ++i) {
-    blitz::Array<double,1> mod(CD);
-    mod = models[i]->getMeanSupervector();
-    models_b.push_back(mod);
-  }
-  const blitz::Array<double,1>& ubm_mean = ubm.getMeanSupervector();
-  const blitz::Array<double,1>& ubm_variance = ubm.getVarianceSupervector();
-  _linearScoring(models_b, ubm_mean, ubm_variance, test_stats, &test_channelOffset, frame_length_normalisation, scores);
-}
-
-
-
-double bob::learn::em::linearScoring(const blitz::Array<double,1>& models,
-                     const blitz::Array<double,1>& ubm_mean, const blitz::Array<double,1>& ubm_variance,
-                     const bob::learn::em::GMMStats& test_stats,
-                     const blitz::Array<double,1>& test_channelOffset,
-                     const bool frame_length_normalisation)
-{
-  int C = test_stats.sumPx.extent(0);
-  int D = test_stats.sumPx.extent(1);
-  int CD = C*D;
-
-
-  blitz::Array<double,1> A(CD);
-  blitz::Array<double,1> B(CD);
-
-  // 1) Compute A
-  A = (models - ubm_mean) / ubm_variance;
-
-  // 2) Compute B
-  for (int s=0; s<CD; ++s)
-    B(s) = test_stats.sumPx(s/D, s%D) - (test_stats.n(s/D) * (ubm_mean(s) + test_channelOffset(s)));
-
-  // Apply the normalisation if needed
-  if (frame_length_normalisation) {
-    double sum_N = test_stats.T;
-    if (sum_N == 0)
-      B = 0;
-    else
-      B /= sum_N;
-  }
-
-  return blitz::sum(A * B);
-}
-
diff --git a/bob/learn/em/cpp/MAP_GMMTrainer.cpp b/bob/learn/em/cpp/MAP_GMMTrainer.cpp
deleted file mode 100644
index fc9a82ecfc44a3c8bc1329f6c3ae9a7bff6ab3c9..0000000000000000000000000000000000000000
--- a/bob/learn/em/cpp/MAP_GMMTrainer.cpp
+++ /dev/null
@@ -1,202 +0,0 @@
-/**
- * @date Tue May 10 11:35:58 2011 +0200
- * @author Francois Moulin <Francois.Moulin@idiap.ch>
- *
- * Copyright (C) Idiap Research Institute, Martigny, Switzerland
- */
-
-#include <bob.learn.em/MAP_GMMTrainer.h>
-#include <bob.core/check.h>
-
-bob::learn::em::MAP_GMMTrainer::MAP_GMMTrainer(
-   const bool update_means,
-   const bool update_variances,
-   const bool update_weights,
-   const double mean_var_update_responsibilities_threshold,
-
-   const bool reynolds_adaptation,
-   const double relevance_factor,
-   const double alpha,
-   boost::shared_ptr<bob::learn::em::GMMMachine> prior_gmm):
-
-  m_gmm_base_trainer(update_means, update_variances, update_weights, mean_var_update_responsibilities_threshold),
-  m_prior_gmm(prior_gmm)
-{
-  m_reynolds_adaptation = reynolds_adaptation;
-  m_relevance_factor    = relevance_factor;
-  m_alpha               = alpha;
-}
-
-
-bob::learn::em::MAP_GMMTrainer::MAP_GMMTrainer(const bob::learn::em::MAP_GMMTrainer& b):
-  m_gmm_base_trainer(b.m_gmm_base_trainer),
-  m_prior_gmm(b.m_prior_gmm)
-{
-  m_relevance_factor    = b.m_relevance_factor;
-  m_alpha               = b.m_alpha;
-  m_reynolds_adaptation = b.m_reynolds_adaptation;
-}
-
-bob::learn::em::MAP_GMMTrainer::~MAP_GMMTrainer()
-{}
-
-void bob::learn::em::MAP_GMMTrainer::initialize(bob::learn::em::GMMMachine& gmm)
-{
-  // Check that the prior GMM has been specified
-  if (!m_prior_gmm)
-    throw std::runtime_error("MAP_GMMTrainer: Prior GMM distribution has not been set");
-
-  // Allocate memory for the sufficient statistics and initialise
-  m_gmm_base_trainer.initialize(gmm);
-
-  const size_t n_gaussians = gmm.getNGaussians();
-  // TODO: check size?
-  gmm.setWeights(m_prior_gmm->getWeights());
-  for(size_t i=0; i<n_gaussians; ++i)
-  {
-    gmm.getGaussian(i)->updateMean() = m_prior_gmm->getGaussian(i)->getMean();
-    gmm.getGaussian(i)->updateVariance() = m_prior_gmm->getGaussian(i)->getVariance();
-    gmm.getGaussian(i)->applyVarianceThresholds();
-  }
-  // Initializes cache
-  m_cache_alpha.resize(n_gaussians);
-  m_cache_ml_weights.resize(n_gaussians);
-}
-
-bool bob::learn::em::MAP_GMMTrainer::setPriorGMM(boost::shared_ptr<bob::learn::em::GMMMachine> prior_gmm)
-{
-  if (!prior_gmm) return false;
-  m_prior_gmm = prior_gmm;
-  return true;
-}
-
-
-void bob::learn::em::MAP_GMMTrainer::mStep(bob::learn::em::GMMMachine& gmm)
-{
-  // Read options and variables
-  double n_gaussians = gmm.getNGaussians();
-
-  //Checking if it is necessary to resize the cache
-  if((size_t)m_cache_alpha.extent(0) != n_gaussians)
-    initialize(gmm); //If it is different for some reason, there is no way, you have to initialize
-
-  // Check that the prior GMM has been specified
-  if (!m_prior_gmm)
-    throw std::runtime_error("MAP_GMMTrainer: Prior GMM distribution has not been set");
-
-  blitz::firstIndex i;
-  blitz::secondIndex j;
-
-  // Calculate the "data-dependent adaptation coefficient", alpha_i
-  // TODO: check if required // m_cache_alpha.resize(n_gaussians);
-  if (!m_reynolds_adaptation)
-    m_cache_alpha = m_alpha;
-  else
-    m_cache_alpha = m_gmm_base_trainer.getGMMStats()->n(i) / (m_gmm_base_trainer.getGMMStats()->n(i) + m_relevance_factor);
-
-  // - Update weights if requested
-  //   Equation 11 of Reynolds et al., "Speaker Verification Using Adapted Gaussian Mixture Models", Digital Signal Processing, 2000
-  if (m_gmm_base_trainer.getUpdateWeights()) {
-    // Calculate the maximum likelihood weights
-    m_cache_ml_weights = m_gmm_base_trainer.getGMMStats()->n / static_cast<double>(m_gmm_base_trainer.getGMMStats()->T); //cast req. for linux/32-bits & osx
-
-    // Get the prior weights
-    const blitz::Array<double,1>& prior_weights = m_prior_gmm->getWeights();
-    blitz::Array<double,1>& new_weights = gmm.updateWeights();
-
-    // Calculate the new weights
-    new_weights = m_cache_alpha * m_cache_ml_weights + (1-m_cache_alpha) * prior_weights;
-
-    // Apply the scale factor, gamma, to ensure the new weights sum to unity
-    double gamma = blitz::sum(new_weights);
-    new_weights /= gamma;
-
-    // Recompute the log weights in the cache of the GMMMachine
-    gmm.recomputeLogWeights();
-  }
-
-  // Update GMM parameters
-  // - Update means if requested
-  //   Equation 12 of Reynolds et al., "Speaker Verification Using Adapted Gaussian Mixture Models", Digital Signal Processing, 2000
-  if (m_gmm_base_trainer.getUpdateMeans()) {
-    // Calculate new means
-    for (size_t i=0; i<n_gaussians; ++i) {
-      const blitz::Array<double,1>& prior_means = m_prior_gmm->getGaussian(i)->getMean();
-      blitz::Array<double,1>& means = gmm.getGaussian(i)->updateMean();
-      if (m_gmm_base_trainer.getGMMStats()->n(i) < m_gmm_base_trainer.getMeanVarUpdateResponsibilitiesThreshold()) {
-        means = prior_means;
-      }
-      else {
-        // Use the maximum likelihood means
-        means = m_cache_alpha(i) * (m_gmm_base_trainer.getGMMStats()->sumPx(i,blitz::Range::all()) / m_gmm_base_trainer.getGMMStats()->n(i)) + (1-m_cache_alpha(i)) * prior_means;
-      }
-    }
-  }
-
-  // - Update variance if requested
-  //   Equation 13 of Reynolds et al., "Speaker Verification Using Adapted Gaussian Mixture Models", Digital Signal Processing, 2000
-  if (m_gmm_base_trainer.getUpdateVariances()) {
-    // Calculate new variances (equation 13)
-    for (size_t i=0; i<n_gaussians; ++i) {
-      const blitz::Array<double,1>& prior_means = m_prior_gmm->getGaussian(i)->getMean();
-      blitz::Array<double,1>& means = gmm.getGaussian(i)->updateMean();
-      const blitz::Array<double,1>& prior_variances = m_prior_gmm->getGaussian(i)->getVariance();
-      blitz::Array<double,1>& variances = gmm.getGaussian(i)->updateVariance();
-      if (m_gmm_base_trainer.getGMMStats()->n(i) < m_gmm_base_trainer.getMeanVarUpdateResponsibilitiesThreshold()) {
-        variances = (prior_variances + prior_means) - blitz::pow2(means);
-      }
-      else {
-        variances = m_cache_alpha(i) * m_gmm_base_trainer.getGMMStats()->sumPxx(i,blitz::Range::all()) / m_gmm_base_trainer.getGMMStats()->n(i) + (1-m_cache_alpha(i)) * (prior_variances + prior_means) - blitz::pow2(means);
-      }
-      gmm.getGaussian(i)->applyVarianceThresholds();
-    }
-  }
-}
-
-
-
-bob::learn::em::MAP_GMMTrainer& bob::learn::em::MAP_GMMTrainer::operator=
-  (const bob::learn::em::MAP_GMMTrainer &other)
-{
-  if (this != &other)
-  {
-    m_gmm_base_trainer    = other.m_gmm_base_trainer;
-    m_relevance_factor    = other.m_relevance_factor;
-    m_prior_gmm           = other.m_prior_gmm;
-    m_alpha               = other.m_alpha;
-    m_reynolds_adaptation = other.m_reynolds_adaptation;
-    m_cache_alpha.resize(other.m_cache_alpha.extent(0));
-    m_cache_ml_weights.resize(other.m_cache_ml_weights.extent(0));
-  }
-  return *this;
-}
-
-
-bool bob::learn::em::MAP_GMMTrainer::operator==
-  (const bob::learn::em::MAP_GMMTrainer &other) const
-{
-  return m_gmm_base_trainer    == other.m_gmm_base_trainer &&
-         m_relevance_factor    == other.m_relevance_factor &&
-         m_prior_gmm           == other.m_prior_gmm &&
-         m_alpha               == other.m_alpha &&
-         m_reynolds_adaptation == other.m_reynolds_adaptation;
-}
-
-
-bool bob::learn::em::MAP_GMMTrainer::operator!=
-  (const bob::learn::em::MAP_GMMTrainer &other) const
-{
-  return !(this->operator==(other));
-}
-
-
-bool bob::learn::em::MAP_GMMTrainer::is_similar_to
-  (const bob::learn::em::MAP_GMMTrainer &other, const double r_epsilon,
-   const double a_epsilon) const
-{
-  return //m_gmm_base_trainer.is_similar_to(other.m_gmm_base_trainer, r_epsilon, a_epsilon) &&
-         bob::core::isClose(m_relevance_factor, other.m_relevance_factor, r_epsilon, a_epsilon) &&
-         m_prior_gmm == other.m_prior_gmm &&
-         bob::core::isClose(m_alpha, other.m_alpha, r_epsilon, a_epsilon) &&
-         m_reynolds_adaptation == other.m_reynolds_adaptation;
-}
diff --git a/bob/learn/em/cpp/ML_GMMTrainer.cpp b/bob/learn/em/cpp/ML_GMMTrainer.cpp
deleted file mode 100644
index 9ac71029c3048fc36b98c85a7f675dd79feac8c8..0000000000000000000000000000000000000000
--- a/bob/learn/em/cpp/ML_GMMTrainer.cpp
+++ /dev/null
@@ -1,107 +0,0 @@
-/**
- * @date Tue May 10 11:35:58 2011 +0200
- * @author Francois Moulin <Francois.Moulin@idiap.ch>
- *
- * Copyright (C) Idiap Research Institute, Martigny, Switzerland
- */
-
-#include <bob.learn.em/ML_GMMTrainer.h>
-#include <algorithm>
-
-bob::learn::em::ML_GMMTrainer::ML_GMMTrainer(
-   const bool update_means,
-   const bool update_variances,
-   const bool update_weights,
-   const double mean_var_update_responsibilities_threshold
-):
-  m_gmm_base_trainer(update_means, update_variances, update_weights, mean_var_update_responsibilities_threshold)
-{}
-
-
-
-bob::learn::em::ML_GMMTrainer::ML_GMMTrainer(const bob::learn::em::ML_GMMTrainer& b):
-  m_gmm_base_trainer(b.m_gmm_base_trainer)
-{}
-
-bob::learn::em::ML_GMMTrainer::~ML_GMMTrainer()
-{}
-
-void bob::learn::em::ML_GMMTrainer::initialize(bob::learn::em::GMMMachine& gmm)
-{
-  m_gmm_base_trainer.initialize(gmm);
-
-  // Allocate cache
-  size_t n_gaussians = gmm.getNGaussians();
-  m_cache_ss_n_thresholded.resize(n_gaussians);
-}
-
-
-void bob::learn::em::ML_GMMTrainer::mStep(bob::learn::em::GMMMachine& gmm)
-{
-  // Read options and variables
-  const size_t n_gaussians = gmm.getNGaussians();
-
- //Checking if it is necessary to resize the cache
- if((size_t)m_cache_ss_n_thresholded.extent(0) != n_gaussians)
-   initialize(gmm); //If it is different for some reason, there is no way, you have to initialize
-
-  // - Update weights if requested
-  //   Equation 9.26 of Bishop, "Pattern recognition and machine learning", 2006
-  if (m_gmm_base_trainer.getUpdateWeights()) {
-    blitz::Array<double,1>& weights = gmm.updateWeights();
-    weights = m_gmm_base_trainer.getGMMStats()->n / static_cast<double>(m_gmm_base_trainer.getGMMStats()->T); //cast req. for linux/32-bits & osx
-    // Recompute the log weights in the cache of the GMMMachine
-    gmm.recomputeLogWeights();
-  }
-
-  // Generate a thresholded version of m_ss.n
-  for(size_t i=0; i<n_gaussians; ++i)
-    m_cache_ss_n_thresholded(i) = std::max(m_gmm_base_trainer.getGMMStats()->n(i), m_gmm_base_trainer.getMeanVarUpdateResponsibilitiesThreshold());
-
-  // Update GMM parameters using the sufficient statistics (m_ss)
-  // - Update means if requested
-  //   Equation 9.24 of Bishop, "Pattern recognition and machine learning", 2006
-  if (m_gmm_base_trainer.getUpdateMeans()) {
-    for(size_t i=0; i<n_gaussians; ++i) {
-      blitz::Array<double,1>& means = gmm.getGaussian(i)->updateMean();
-      means = m_gmm_base_trainer.getGMMStats()->sumPx(i, blitz::Range::all()) / m_cache_ss_n_thresholded(i);
-    }
-  }
-
-  // - Update variance if requested
-  //   See Equation 9.25 of Bishop, "Pattern recognition and machine learning", 2006
-  //   ...but we use the "computational formula for the variance", i.e.
-  //   var = 1/n * sum (P(x-mean)(x-mean))
-  //       = 1/n * sum (Pxx) - mean^2
-  if (m_gmm_base_trainer.getUpdateVariances()) {
-    for(size_t i=0; i<n_gaussians; ++i) {
-      const blitz::Array<double,1>& means = gmm.getGaussian(i)->getMean();
-      blitz::Array<double,1>& variances = gmm.getGaussian(i)->updateVariance();
-      variances = m_gmm_base_trainer.getGMMStats()->sumPxx(i, blitz::Range::all()) / m_cache_ss_n_thresholded(i) - blitz::pow2(means);
-      gmm.getGaussian(i)->applyVarianceThresholds();
-    }
-  }
-}
-
-bob::learn::em::ML_GMMTrainer& bob::learn::em::ML_GMMTrainer::operator=
-  (const bob::learn::em::ML_GMMTrainer &other)
-{
-  if (this != &other)
-  {
-    m_gmm_base_trainer = other.m_gmm_base_trainer;
-    m_cache_ss_n_thresholded.resize(other.m_cache_ss_n_thresholded.extent(0));
-  }
-  return *this;
-}
-
-bool bob::learn::em::ML_GMMTrainer::operator==
-  (const bob::learn::em::ML_GMMTrainer &other) const
-{
-  return m_gmm_base_trainer == other.m_gmm_base_trainer;
-}
-
-bool bob::learn::em::ML_GMMTrainer::operator!=
-  (const bob::learn::em::ML_GMMTrainer &other) const
-{
-  return !(this->operator==(other));
-}
diff --git a/bob/learn/em/cpp/PLDAMachine.cpp b/bob/learn/em/cpp/PLDAMachine.cpp
deleted file mode 100644
index fe60630d42ad877943afb52946e7ae42018788f0..0000000000000000000000000000000000000000
--- a/bob/learn/em/cpp/PLDAMachine.cpp
+++ /dev/null
@@ -1,960 +0,0 @@
-/**
- * @date Fri Oct 14 18:07:56 2011 +0200
- * @author Laurent El Shafey <Laurent.El-Shafey@idiap.ch>
- *
- * @brief Machines that implements the PLDA model
- *
- * Copyright (C) Idiap Research Institute, Martigny, Switzerland
- */
-
-#include <bob.core/assert.h>
-#include <bob.core/check.h>
-#include <bob.core/array_copy.h>
-#include <bob.learn.em/PLDAMachine.h>
-#include <bob.math/linear.h>
-#include <bob.math/det.h>
-#include <bob.math/inv.h>
-
-#include <cmath>
-#include <boost/lexical_cast.hpp>
-#include <string>
-
-bob::learn::em::PLDABase::PLDABase():
-  m_variance_threshold(0.)
-{
-  resizeNoInit(0, 0, 0);
-}
-
-bob::learn::em::PLDABase::PLDABase(const size_t dim_d, const size_t dim_f,
-    const size_t dim_g, const double variance_threshold):
-  m_variance_threshold(variance_threshold)
-{
-  resize(dim_d, dim_f, dim_g);
-}
-
-
-bob::learn::em::PLDABase::PLDABase(const bob::learn::em::PLDABase& other):
-  m_dim_d(other.m_dim_d),
-  m_dim_f(other.m_dim_f),
-  m_dim_g(other.m_dim_g),
-  m_F(bob::core::array::ccopy(other.m_F)),
-  m_G(bob::core::array::ccopy(other.m_G)),
-  m_sigma(bob::core::array::ccopy(other.m_sigma)),
-  m_mu(bob::core::array::ccopy(other.m_mu)),
-  m_variance_threshold(other.m_variance_threshold),
-  m_cache_isigma(bob::core::array::ccopy(other.m_cache_isigma)),
-  m_cache_alpha(bob::core::array::ccopy(other.m_cache_alpha)),
-  m_cache_beta(bob::core::array::ccopy(other.m_cache_beta)),
-  m_cache_gamma(),
-  m_cache_Ft_beta(bob::core::array::ccopy(other.m_cache_Ft_beta)),
-  m_cache_Gt_isigma(bob::core::array::ccopy(other.m_cache_Gt_isigma)),
-  m_cache_logdet_alpha(other.m_cache_logdet_alpha),
-  m_cache_logdet_sigma(other.m_cache_logdet_sigma),
-  m_cache_loglike_constterm(other.m_cache_loglike_constterm)
-{
-  bob::core::array::ccopy(other.m_cache_gamma, m_cache_gamma);
-  resizeTmp();
-}
-
-bob::learn::em::PLDABase::PLDABase(bob::io::base::HDF5File& config) {
-  load(config);
-}
-
-bob::learn::em::PLDABase::~PLDABase() {
-}
-
-bob::learn::em::PLDABase& bob::learn::em::PLDABase::operator=
-    (const bob::learn::em::PLDABase& other)
-{
-  if (this != &other)
-  {
-    m_dim_d = other.m_dim_d;
-    m_dim_f = other.m_dim_f;
-    m_dim_g = other.m_dim_g;
-    m_F.reference(bob::core::array::ccopy(other.m_F));
-    m_G.reference(bob::core::array::ccopy(other.m_G));
-    m_sigma.reference(bob::core::array::ccopy(other.m_sigma));
-    m_mu.reference(bob::core::array::ccopy(other.m_mu));
-    m_variance_threshold = other.m_variance_threshold;
-    m_cache_isigma.reference(bob::core::array::ccopy(other.m_cache_isigma));
-    m_cache_alpha.reference(bob::core::array::ccopy(other.m_cache_alpha));
-    m_cache_beta.reference(bob::core::array::ccopy(other.m_cache_beta));
-    bob::core::array::ccopy(other.m_cache_gamma, m_cache_gamma);
-    m_cache_Ft_beta.reference(bob::core::array::ccopy(other.m_cache_Ft_beta));
-    m_cache_Gt_isigma.reference(bob::core::array::ccopy(other.m_cache_Gt_isigma));
-    m_cache_logdet_alpha = other.m_cache_logdet_alpha;
-    m_cache_logdet_sigma = other.m_cache_logdet_sigma;
-    m_cache_loglike_constterm = other.m_cache_loglike_constterm;
-    resizeTmp();
-  }
-  return *this;
-}
-
-bool bob::learn::em::PLDABase::operator==
-    (const bob::learn::em::PLDABase& b) const
-{
-  if (!(m_dim_d == b.m_dim_d && m_dim_f == b.m_dim_f &&
-        m_dim_g == b.m_dim_g &&
-        bob::core::array::isEqual(m_F, b.m_F) &&
-        bob::core::array::isEqual(m_G, b.m_G) &&
-        bob::core::array::isEqual(m_sigma, b.m_sigma) &&
-        bob::core::array::isEqual(m_mu, b.m_mu) &&
-        m_variance_threshold == b.m_variance_threshold &&
-        bob::core::array::isEqual(m_cache_isigma, b.m_cache_isigma) &&
-        bob::core::array::isEqual(m_cache_alpha, b.m_cache_alpha) &&
-        bob::core::array::isEqual(m_cache_beta, b.m_cache_beta) &&
-        bob::core::array::isEqual(m_cache_gamma, b.m_cache_gamma) &&
-        bob::core::array::isEqual(m_cache_Ft_beta, b.m_cache_Ft_beta) &&
-        bob::core::array::isEqual(m_cache_Gt_isigma, b.m_cache_Gt_isigma) &&
-        m_cache_logdet_alpha == b.m_cache_logdet_alpha &&
-        m_cache_logdet_sigma == b.m_cache_logdet_sigma))
-    return false;
-
-  // m_cache_loglike_constterm
-  if (this->m_cache_loglike_constterm.size() != b.m_cache_loglike_constterm.size())
-    return false;  // differing sizes, they are not the same
-  std::map<size_t, double>::const_iterator i, j;
-  for (i = this->m_cache_loglike_constterm.begin(), j = b.m_cache_loglike_constterm.begin();
-    i != this->m_cache_loglike_constterm.end(); ++i, ++j)
-  {
-    if (i->first != j->first || i->second != j->second)
-      return false;
-  }
-
-  return true;
-}
-
-bool bob::learn::em::PLDABase::operator!=
-    (const bob::learn::em::PLDABase& b) const
-{
-  return !(this->operator==(b));
-}
-
-bool bob::learn::em::PLDABase::is_similar_to(const bob::learn::em::PLDABase& b,
-  const double r_epsilon, const double a_epsilon) const
-{
-  return (m_dim_d == b.m_dim_d && m_dim_f == b.m_dim_f &&
-          m_dim_g == b.m_dim_g &&
-          bob::core::array::isClose(m_F, b.m_F, r_epsilon, a_epsilon) &&
-          bob::core::array::isClose(m_G, b.m_G, r_epsilon, a_epsilon) &&
-          bob::core::array::isClose(m_sigma, b.m_sigma, r_epsilon, a_epsilon) &&
-          bob::core::array::isClose(m_mu, b.m_mu, r_epsilon, a_epsilon) &&
-          bob::core::isClose(m_variance_threshold, b.m_variance_threshold, r_epsilon, a_epsilon) &&
-          bob::core::array::isClose(m_cache_isigma, b.m_cache_isigma, r_epsilon, a_epsilon) &&
-          bob::core::array::isClose(m_cache_alpha, b.m_cache_alpha, r_epsilon, a_epsilon) &&
-          bob::core::array::isClose(m_cache_beta, b.m_cache_beta, r_epsilon, a_epsilon) &&
-          bob::core::array::isClose(m_cache_gamma, b.m_cache_gamma, r_epsilon, a_epsilon) &&
-          bob::core::array::isClose(m_cache_Ft_beta, b.m_cache_Ft_beta, r_epsilon, a_epsilon) &&
-          bob::core::array::isClose(m_cache_Gt_isigma, b.m_cache_Gt_isigma, r_epsilon, a_epsilon) &&
-          bob::core::isClose(m_cache_logdet_alpha, b.m_cache_logdet_alpha, r_epsilon, a_epsilon) &&
-          bob::core::isClose(m_cache_logdet_sigma, b.m_cache_logdet_sigma, r_epsilon, a_epsilon) &&
-          bob::core::isClose(m_cache_loglike_constterm, b.m_cache_loglike_constterm));
-}
-
-void bob::learn::em::PLDABase::load(bob::io::base::HDF5File& config)
-{
-  if (!config.contains("dim_d"))
-  {
-    // Then the model was saved using bob < 1.2.0
-    //reads all data directly into the member variables
-    m_F.reference(config.readArray<double,2>("F"));
-    m_G.reference(config.readArray<double,2>("G"));
-    m_dim_d = m_F.extent(0);
-    m_dim_f = m_F.extent(1);
-    m_dim_g = m_G.extent(1);
-    m_sigma.reference(config.readArray<double,1>("sigma"));
-    m_mu.reference(config.readArray<double,1>("mu"));
-    m_cache_isigma.resize(m_dim_d);
-    precomputeISigma();
-    m_variance_threshold = 0.;
-    m_cache_alpha.reference(config.readArray<double,2>("alpha"));
-    m_cache_beta.reference(config.readArray<double,2>("beta"));
-    // gamma and log like constant term (a-dependent terms)
-    if (config.contains("a_indices"))
-    {
-      blitz::Array<uint32_t, 1> a_indices;
-      a_indices.reference(config.readArray<uint32_t,1>("a_indices"));
-      for (int i=0; i<a_indices.extent(0); ++i)
-      {
-        std::string str1 = "gamma_" + boost::lexical_cast<std::string>(a_indices(i));
-        m_cache_gamma[a_indices(i)].reference(config.readArray<double,2>(str1));
-        std::string str2 = "loglikeconstterm_" + boost::lexical_cast<std::string>(a_indices(i));
-        m_cache_loglike_constterm[a_indices(i)] = config.read<double>(str2);
-      }
-    }
-    m_cache_Ft_beta.reference(config.readArray<double,2>("Ft_beta"));
-    m_cache_Gt_isigma.reference(config.readArray<double,2>("Gt_isigma"));
-    m_cache_logdet_alpha = config.read<double>("logdet_alpha");
-    m_cache_logdet_sigma = config.read<double>("logdet_sigma");
-  }
-  else
-  {
-    // Then the model was saved using bob >= 1.2.0
-    //reads all data directly into the member variables
-    m_F.reference(config.readArray<double,2>("F"));
-    m_G.reference(config.readArray<double,2>("G"));
-    // Conditional because previous versions had not these variables
-    m_dim_d = config.read<uint64_t>("dim_d");
-    m_dim_f = config.read<uint64_t>("dim_f");
-    m_dim_g = config.read<uint64_t>("dim_g");
-    m_sigma.reference(config.readArray<double,1>("sigma"));
-    m_mu.reference(config.readArray<double,1>("mu"));
-    m_cache_isigma.resize(m_dim_d);
-    precomputeISigma();
-    if (config.contains("variance_threshold"))
-      m_variance_threshold = config.read<double>("variance_threshold");
-    else if (config.contains("variance_thresholds")) // In case 1.2.0 alpha/beta version has been used
-    {
-      blitz::Array<double,1> tmp;
-      tmp.reference(config.readArray<double,1>("variance_thresholds"));
-      m_variance_threshold = tmp(0);
-    }
-    m_cache_alpha.reference(config.readArray<double,2>("alpha"));
-    m_cache_beta.reference(config.readArray<double,2>("beta"));
-    // gamma's (a-dependent terms)
-    if(config.contains("a_indices_gamma"))
-    {
-      blitz::Array<uint32_t, 1> a_indices;
-      a_indices.reference(config.readArray<uint32_t,1>("a_indices_gamma"));
-      for(int i=0; i<a_indices.extent(0); ++i)
-      {
-        std::string str = "gamma_" + boost::lexical_cast<std::string>(a_indices(i));
-        m_cache_gamma[a_indices(i)].reference(config.readArray<double,2>(str));
-      }
-    }
-    // log likelihood constant term's (a-dependent terms)
-    if(config.contains("a_indices_loglikeconstterm"))
-    {
-      blitz::Array<uint32_t, 1> a_indices;
-      a_indices.reference(config.readArray<uint32_t,1>("a_indices_loglikeconstterm"));
-      for(int i=0; i<a_indices.extent(0); ++i)
-      {
-        std::string str = "loglikeconstterm_" + boost::lexical_cast<std::string>(a_indices(i));
-        m_cache_loglike_constterm[a_indices(i)] = config.read<double>(str);
-      }
-    }
-    m_cache_Ft_beta.reference(config.readArray<double,2>("Ft_beta"));
-    m_cache_Gt_isigma.reference(config.readArray<double,2>("Gt_isigma"));
-    m_cache_logdet_alpha = config.read<double>("logdet_alpha");
-    m_cache_logdet_sigma = config.read<double>("logdet_sigma");
-  }
-  resizeTmp();
-}
-
-void bob::learn::em::PLDABase::save(bob::io::base::HDF5File& config) const
-{
-  config.set("dim_d", (uint64_t)m_dim_d);
-  config.set("dim_f", (uint64_t)m_dim_f);
-  config.set("dim_g", (uint64_t)m_dim_g);
-  config.setArray("F", m_F);
-  config.setArray("G", m_G);
-  config.setArray("sigma", m_sigma);
-  config.setArray("mu", m_mu);
-  config.set("variance_threshold", m_variance_threshold);
-  config.setArray("alpha", m_cache_alpha);
-  config.setArray("beta", m_cache_beta);
-  // gamma's
-  if(m_cache_gamma.size() > 0)
-  {
-    blitz::Array<uint32_t, 1> a_indices(m_cache_gamma.size());
-    int i = 0;
-    for(std::map<size_t,blitz::Array<double,2> >::const_iterator
-        it=m_cache_gamma.begin(); it!=m_cache_gamma.end(); ++it)
-    {
-      a_indices(i) = it->first;
-      std::string str = "gamma_" + boost::lexical_cast<std::string>(it->first);
-      config.setArray(str, it->second);
-      ++i;
-    }
-    config.setArray("a_indices_gamma", a_indices);
-  }
-  // log likelihood constant terms
-  if(m_cache_loglike_constterm.size() > 0)
-  {
-    blitz::Array<uint32_t, 1> a_indices(m_cache_loglike_constterm.size());
-    int i = 0;
-    for(std::map<size_t,double>::const_iterator
-        it=m_cache_loglike_constterm.begin(); it!=m_cache_loglike_constterm.end(); ++it)
-    {
-      a_indices(i) = it->first;
-      std::string str = "loglikeconstterm_" + boost::lexical_cast<std::string>(it->first);
-      config.set(str, it->second);
-      ++i;
-    }
-    config.setArray("a_indices_loglikeconstterm", a_indices);
-  }
-
-  config.setArray("Ft_beta", m_cache_Ft_beta);
-  config.setArray("Gt_isigma", m_cache_Gt_isigma);
-  config.set("logdet_alpha", m_cache_logdet_alpha);
-  config.set("logdet_sigma", m_cache_logdet_sigma);
-}
-
-void bob::learn::em::PLDABase::resizeNoInit(const size_t dim_d, const size_t dim_f,
-    const size_t dim_g)
-{
-  m_dim_d = dim_d;
-  m_dim_f = dim_f;
-  m_dim_g = dim_g;
-  m_F.resize(dim_d, dim_f);
-  m_G.resize(dim_d, dim_g);
-  m_sigma.resize(dim_d);
-  m_mu.resize(dim_d);
-  m_cache_alpha.resize(dim_g, dim_g);
-  m_cache_beta.resize(dim_d, dim_d);
-  m_cache_Ft_beta.resize(dim_f, dim_d);
-  m_cache_Gt_isigma.resize(dim_g, dim_d);
-  m_cache_gamma.clear();
-  m_cache_isigma.resize(dim_d);
-  m_cache_loglike_constterm.clear();
-  resizeTmp();
-}
-
-void bob::learn::em::PLDABase::resizeTmp()
-{
-  m_tmp_d_1.resize(m_dim_d);
-  m_tmp_d_2.resize(m_dim_d);
-  m_tmp_d_ng_1.resize(m_dim_d, m_dim_g);
-  m_tmp_nf_nf_1.resize(m_dim_f, m_dim_f);
-  m_tmp_ng_ng_1.resize(m_dim_g, m_dim_g);
-}
-
-void bob::learn::em::PLDABase::resize(const size_t dim_d, const size_t dim_f,
-    const size_t dim_g)
-{
-  resizeNoInit(dim_d, dim_f, dim_g);
-  initMuFGSigma();
-}
-
-void bob::learn::em::PLDABase::setF(const blitz::Array<double,2>& F)
-{
-  bob::core::array::assertSameShape(F, m_F);
-  m_F.reference(bob::core::array::ccopy(F));
-  // Precomputes useful matrices
-  precompute();
-}
-
-void bob::learn::em::PLDABase::setG(const blitz::Array<double,2>& G)
-{
-  bob::core::array::assertSameShape(G, m_G);
-  m_G.reference(bob::core::array::ccopy(G));
-  // Precomputes useful matrices and values
-  precompute();
-  precomputeLogDetAlpha();
-}
-
-void bob::learn::em::PLDABase::setSigma(const blitz::Array<double,1>& sigma)
-{
-  bob::core::array::assertSameShape(sigma, m_sigma);
-  m_sigma.reference(bob::core::array::ccopy(sigma));
-  // Apply variance flooring threshold: This will also
-  // call the precompute() and precomputeLogLike() methods!
-  applyVarianceThreshold();
-}
-
-void bob::learn::em::PLDABase::setMu(const blitz::Array<double,1>& mu)
-{
-  bob::core::array::assertSameShape(mu, m_mu);
-  m_mu.reference(bob::core::array::ccopy(mu));
-}
-
-void bob::learn::em::PLDABase::setVarianceThreshold(const double value)
-{
-  // Variance flooring
-  m_variance_threshold = value;
-  // Apply variance flooring thresholds: This will also
-  // call the precompute() and precomputeLogLike() methods!
-  applyVarianceThreshold();
-}
-
-void bob::learn::em::PLDABase::applyVarianceThreshold()
-{
-   // Apply variance flooring threshold
-  m_sigma = blitz::where( m_sigma < m_variance_threshold, m_variance_threshold, m_sigma);
-  // Re-compute constants, because m_sigma has changed
-  precompute();
-  precomputeLogLike();
-}
-
-const blitz::Array<double,2>& bob::learn::em::PLDABase::getGamma(const size_t a) const
-{
-  if(!hasGamma(a))
-    throw std::runtime_error("Gamma for this number of samples is not currently in cache. You could use the getAddGamma() method instead");
-  return (m_cache_gamma.find(a))->second;
-}
-
-const blitz::Array<double,2>& bob::learn::em::PLDABase::getAddGamma(const size_t a)
-{
-  if(!hasGamma(a)) precomputeGamma(a);
-  return m_cache_gamma[a];
-}
-
-void bob::learn::em::PLDABase::initMuFGSigma()
-{
-  // To avoid problems related to precomputation
-  m_mu = 0.;
-  bob::math::eye(m_F);
-  bob::math::eye(m_G);
-  m_sigma = 1.;
-  // Precompute variables
-  precompute();
-  precomputeLogLike();
-}
-
-void bob::learn::em::PLDABase::precompute()
-{
-  precomputeISigma();
-  precomputeGtISigma();
-  precomputeAlpha();
-  precomputeBeta();
-  m_cache_gamma.clear();
-  precomputeFtBeta();
-  m_cache_loglike_constterm.clear();
-}
-
-void bob::learn::em::PLDABase::precomputeLogLike()
-{
-  precomputeLogDetAlpha();
-  precomputeLogDetSigma();
-}
-
-void bob::learn::em::PLDABase::precomputeISigma()
-{
-  // Updates inverse of sigma
-  m_cache_isigma = 1. / m_sigma;
-}
-
-void bob::learn::em::PLDABase::precomputeGtISigma()
-{
-  // m_cache_Gt_isigma = G^T \Sigma^{-1}
-  blitz::firstIndex i;
-  blitz::secondIndex j;
-  blitz::Array<double,2> Gt = m_G.transpose(1,0);
-  m_cache_Gt_isigma = Gt(i,j) * m_cache_isigma(j);
-}
-
-void bob::learn::em::PLDABase::precomputeAlpha()
-{
-  // alpha = (Id + G^T.sigma^-1.G)^-1
-
-  // m_tmp_ng_ng_1 = G^T.sigma^-1.G
-  bob::math::prod(m_cache_Gt_isigma, m_G, m_tmp_ng_ng_1);
-  // m_tmp_ng_ng_1 = Id + G^T.sigma^-1.G
-  for(int i=0; i<m_tmp_ng_ng_1.extent(0); ++i) m_tmp_ng_ng_1(i,i) += 1;
-  // m_cache_alpha = (Id + G^T.sigma^-1.G)^-1
-  bob::math::inv(m_tmp_ng_ng_1, m_cache_alpha);
-}
-
-void bob::learn::em::PLDABase::precomputeBeta()
-{
-  // beta = (sigma + G.G^T)^-1
-  // BUT, there is a more efficient computation (Woodbury identity):
-  // beta = sigma^-1 - sigma^-1.G.(Id + G^T.sigma^-1.G)^-1.G^T.sigma^-1
-  // beta =  sigma^-1 - sigma^-1.G.alpha.G^T.sigma^-1
-
-  blitz::Array<double,2> GtISigmaT = m_cache_Gt_isigma.transpose(1,0);
-  // m_tmp_d_ng_1 = sigma^-1.G.alpha
-  bob::math::prod(GtISigmaT, m_cache_alpha, m_tmp_d_ng_1);
-  // m_cache_beta = -sigma^-1.G.alpha.G^T.sigma^-1
-  bob::math::prod(m_tmp_d_ng_1, m_cache_Gt_isigma, m_cache_beta);
-  m_cache_beta = -m_cache_beta;
-  // m_cache_beta = sigma^-1 - sigma^-1.G.alpha.G^T.sigma^-1
-  for(int i=0; i<m_cache_beta.extent(0); ++i) m_cache_beta(i,i) += m_cache_isigma(i);
-}
-
-void bob::learn::em::PLDABase::precomputeGamma(const size_t a)
-{
-
-  blitz::Array<double,2> gamma_a(getDimF(),getDimF());
-  m_cache_gamma[a].reference(gamma_a);
-  computeGamma(a, gamma_a);
-}
-
-void bob::learn::em::PLDABase::precomputeFtBeta()
-{
-  // m_cache_Ft_beta = F^T.beta = F^T.(sigma + G.G^T)^-1
-  blitz::Array<double,2> Ft = m_F.transpose(1,0);
-  bob::math::prod(Ft, m_cache_beta, m_cache_Ft_beta);
-}
-
-void bob::learn::em::PLDABase::computeGamma(const size_t a,
-  blitz::Array<double,2> res) const
-{
-  // gamma = (Id + a.F^T.beta.F)^-1
-
-  // Checks destination size
-  bob::core::array::assertSameShape(res, m_tmp_nf_nf_1);
-  // m_tmp_nf_nf_1 = F^T.beta.F
-  bob::math::prod(m_cache_Ft_beta, m_F, m_tmp_nf_nf_1);
-   // m_tmp_nf_nf_1 = a.F^T.beta.F
-  m_tmp_nf_nf_1 *= static_cast<double>(a);
-  // m_tmp_nf_nf_1 = Id + a.F^T.beta.F
-  for(int i=0; i<m_tmp_nf_nf_1.extent(0); ++i) m_tmp_nf_nf_1(i,i) += 1;
-
-  // res = (Id + a.F^T.beta.F)^-1
-  bob::math::inv(m_tmp_nf_nf_1, res);
-}
-
-void bob::learn::em::PLDABase::precomputeLogDetAlpha()
-{
-  int sign;
-  m_cache_logdet_alpha = bob::math::slogdet(m_cache_alpha, sign);
-}
-
-void bob::learn::em::PLDABase::precomputeLogDetSigma()
-{
-  m_cache_logdet_sigma = blitz::sum(blitz::log(m_sigma));
-}
-
-double bob::learn::em::PLDABase::computeLogLikeConstTerm(const size_t a,
-  const blitz::Array<double,2>& gamma_a) const
-{
-  // loglike_constterm[a] = a/2 *
-  //  ( -D*log(2*pi) -log|sigma| +log|alpha| +log|gamma_a|)
-  int sign;
-  double logdet_gamma_a = bob::math::slogdet(gamma_a, sign);
-  double ah = static_cast<double>(a)/2.;
-  double res = ( -ah*((double)m_dim_d)*log(2*M_PI) -
-      ah*m_cache_logdet_sigma + ah*m_cache_logdet_alpha + logdet_gamma_a/2.);
-  return res;
-}
-
-double bob::learn::em::PLDABase::computeLogLikeConstTerm(const size_t a)
-{
-  const blitz::Array<double,2>& gamma_a = getAddGamma(a);
-  return computeLogLikeConstTerm(a, gamma_a);
-}
-
-void bob::learn::em::PLDABase::precomputeLogLikeConstTerm(const size_t a)
-{
-  double val = computeLogLikeConstTerm(a);
-  m_cache_loglike_constterm[a] = val;
-}
-
-double bob::learn::em::PLDABase::getLogLikeConstTerm(const size_t a) const
-{
-  if(!hasLogLikeConstTerm(a))
-    throw std::runtime_error("The LogLikelihood constant term for this number of samples is not currently in cache. You could use the getAddLogLikeConstTerm() method instead");
-  return (m_cache_loglike_constterm.find(a))->second;
-}
-
-double bob::learn::em::PLDABase::getAddLogLikeConstTerm(const size_t a)
-{
-  if(!hasLogLikeConstTerm(a)) precomputeLogLikeConstTerm(a);
-  return m_cache_loglike_constterm[a];
-}
-
-void bob::learn::em::PLDABase::clearMaps()
-{
-  m_cache_gamma.clear();
-  m_cache_loglike_constterm.clear();
-}
-
-double bob::learn::em::PLDABase::computeLogLikelihoodPointEstimate(
-  const blitz::Array<double,1>& xij, const blitz::Array<double,1>& hi,
-  const blitz::Array<double,1>& wij) const
-{
-  // Check inputs
-  bob::core::array::assertSameDimensionLength(xij.extent(0), getDimD());
-  bob::core::array::assertSameDimensionLength(hi.extent(0), getDimF());
-  bob::core::array::assertSameDimensionLength(wij.extent(0), getDimG());
-  // Computes: -D/2 log(2pi) -1/2 log(det(\Sigma))
-  //   -1/2 {(x_{ij}-(\mu+Fh_{i}+Gw_{ij}))^{T}\Sigma^{-1}(x_{ij}-(\mu+Fh_{i}+Gw_{ij}))}
-  double res = -0.5*((double)m_dim_d)*log(2*M_PI) - 0.5*m_cache_logdet_sigma;
-  // m_tmp_d_1 = (x_{ij} - (\mu+Fh_{i}+Gw_{ij}))
-  m_tmp_d_1 = xij - m_mu;
-  bob::math::prod(m_F, hi, m_tmp_d_2);
-  m_tmp_d_1 -= m_tmp_d_2;
-  bob::math::prod(m_G, wij, m_tmp_d_2);
-  m_tmp_d_1 -= m_tmp_d_2;
-  // add third term to res
-  res += -0.5*blitz::sum(blitz::pow2(m_tmp_d_1) * m_cache_isigma);
-  return res;
-}
-
-namespace bob { namespace learn { namespace em {
-  /**
-   * @brief Prints a PLDABase in the output stream. This will print
-   * the values of the parameters \f$\mu\f$, \f$F\f$, \f$G\f$ and
-   * \f$\Sigma\f$ of the PLDA model.
-   */
-  std::ostream& operator<<(std::ostream& os, const PLDABase& m) {
-    os << "mu = " << m.m_mu << std::endl;
-    os << "sigma = " << m.m_sigma << std::endl;
-    os << "F = " << m.m_F << std::endl;
-    os << "G = " << m.m_G << std::endl;
-    return os;
-  }
-} } }
-
-
-bob::learn::em::PLDAMachine::PLDAMachine():
-  m_plda_base(),
-  m_n_samples(0), m_nh_sum_xit_beta_xi(0), m_weighted_sum(0),
-  m_loglikelihood(0), m_cache_gamma(), m_cache_loglike_constterm(),
-  m_tmp_d_1(0), m_tmp_d_2(0), m_tmp_nf_1(0), m_tmp_nf_2(0), m_tmp_nf_nf_1(0,0)
-{
-}
-
-bob::learn::em::PLDAMachine::PLDAMachine(const boost::shared_ptr<bob::learn::em::PLDABase> plda_base):
-  m_plda_base(plda_base),
-  m_n_samples(0), m_nh_sum_xit_beta_xi(0), m_weighted_sum(plda_base->getDimF()),
-  m_loglikelihood(0), m_cache_gamma(), m_cache_loglike_constterm()
-{
-  resizeTmp();
-}
-
-
-bob::learn::em::PLDAMachine::PLDAMachine(const bob::learn::em::PLDAMachine& other):
-  m_plda_base(other.m_plda_base),
-  m_n_samples(other.m_n_samples),
-  m_nh_sum_xit_beta_xi(other.m_nh_sum_xit_beta_xi),
-  m_weighted_sum(bob::core::array::ccopy(other.m_weighted_sum)),
-  m_loglikelihood(other.m_loglikelihood), m_cache_gamma(),
-  m_cache_loglike_constterm(other.m_cache_loglike_constterm)
-{
-  bob::core::array::ccopy(other.m_cache_gamma, m_cache_gamma);
-  resizeTmp();
-}
-
-bob::learn::em::PLDAMachine::PLDAMachine(bob::io::base::HDF5File& config,
-    const boost::shared_ptr<bob::learn::em::PLDABase> plda_base):
-  m_plda_base(plda_base)
-{
-  load(config);
-}
-
-bob::learn::em::PLDAMachine::~PLDAMachine() {
-}
-
-bob::learn::em::PLDAMachine& bob::learn::em::PLDAMachine::operator=
-(const bob::learn::em::PLDAMachine& other)
-{
-  if(this!=&other)
-  {
-    m_plda_base = other.m_plda_base;
-    m_n_samples = other.m_n_samples;
-    m_nh_sum_xit_beta_xi = other.m_nh_sum_xit_beta_xi;
-    m_weighted_sum.reference(bob::core::array::ccopy(other.m_weighted_sum));
-    m_loglikelihood = other.m_loglikelihood;
-    bob::core::array::ccopy(other.m_cache_gamma, m_cache_gamma);
-    m_cache_loglike_constterm = other.m_cache_loglike_constterm;
-    resizeTmp();
-  }
-  return *this;
-}
-
-bool bob::learn::em::PLDAMachine::operator==
-    (const bob::learn::em::PLDAMachine& b) const
-{
-  if (!(( (!m_plda_base && !b.m_plda_base) ||
-          ((m_plda_base && b.m_plda_base) && *(m_plda_base) == *(b.m_plda_base))) &&
-        m_n_samples == b.m_n_samples &&
-        m_nh_sum_xit_beta_xi ==b.m_nh_sum_xit_beta_xi &&
-        bob::core::array::isEqual(m_weighted_sum, b.m_weighted_sum) &&
-        m_loglikelihood == b.m_loglikelihood &&
-        bob::core::array::isEqual(m_cache_gamma, b.m_cache_gamma)))
-    return false;
-
-  // m_cache_loglike_constterm
-  if (this->m_cache_loglike_constterm.size() != b.m_cache_loglike_constterm.size())
-    return false;  // differing sizes, they are not the same
-  std::map<size_t, double>::const_iterator i, j;
-  for (i = this->m_cache_loglike_constterm.begin(), j = b.m_cache_loglike_constterm.begin();
-    i != this->m_cache_loglike_constterm.end(); ++i, ++j)
-  {
-    if (i->first != j->first || i->second != j->second)
-      return false;
-  }
-
-  return true;
-}
-
-bool bob::learn::em::PLDAMachine::operator!=
-    (const bob::learn::em::PLDAMachine& b) const
-{
-  return !(this->operator==(b));
-}
-
-bool bob::learn::em::PLDAMachine::is_similar_to(
-  const bob::learn::em::PLDAMachine& b, const double r_epsilon,
-  const double a_epsilon) const
-{
-  return (( (!m_plda_base && !b.m_plda_base) ||
-            ((m_plda_base && b.m_plda_base) &&
-             m_plda_base->is_similar_to(*(b.m_plda_base), r_epsilon, a_epsilon))) &&
-          m_n_samples == b.m_n_samples &&
-          bob::core::isClose(m_nh_sum_xit_beta_xi, b.m_nh_sum_xit_beta_xi, r_epsilon, a_epsilon) &&
-          bob::core::array::isClose(m_weighted_sum, b.m_weighted_sum, r_epsilon, a_epsilon) &&
-          bob::core::isClose(m_loglikelihood, b.m_loglikelihood, r_epsilon, a_epsilon) &&
-          bob::core::array::isClose(m_cache_gamma, b.m_cache_gamma, r_epsilon, a_epsilon) &&
-          bob::core::isClose(m_cache_loglike_constterm, b.m_cache_loglike_constterm, r_epsilon, a_epsilon));
-}
-
-void bob::learn::em::PLDAMachine::load(bob::io::base::HDF5File& config)
-{
-  //reads all data directly into the member variables
-  m_n_samples = config.read<uint64_t>("n_samples");
-  m_nh_sum_xit_beta_xi = config.read<double>("nh_sum_xit_beta_xi");
-  m_weighted_sum.reference(config.readArray<double,1>("weighted_sum"));
-  m_loglikelihood = config.read<double>("loglikelihood");
-  // gamma and log like constant term (a-dependent terms)
-  clearMaps();
-  if(config.contains("a_indices"))
-  {
-    blitz::Array<uint32_t, 1> a_indices;
-    a_indices.reference(config.readArray<uint32_t,1>("a_indices"));
-    for(int i=0; i<a_indices.extent(0); ++i)
-    {
-      std::string str1 = "gamma_" + boost::lexical_cast<std::string>(a_indices(i));
-      m_cache_gamma[a_indices(i)].reference(config.readArray<double,2>(str1));
-      std::string str2 = "loglikeconstterm_" + boost::lexical_cast<std::string>(a_indices(i));
-      m_cache_loglike_constterm[a_indices(i)] = config.read<double>(str2);
-    }
-  }
-  resizeTmp();
-}
-
-void bob::learn::em::PLDAMachine::save(bob::io::base::HDF5File& config) const
-{
-  config.set("n_samples", m_n_samples);
-  config.set("nh_sum_xit_beta_xi", m_nh_sum_xit_beta_xi);
-  config.setArray("weighted_sum", m_weighted_sum);
-  config.set("loglikelihood", m_loglikelihood);
-  // Gamma
-  if(m_cache_gamma.size() > 0)
-  {
-    blitz::Array<uint32_t, 1> a_indices(m_cache_gamma.size());
-    int i = 0;
-    for(std::map<size_t,blitz::Array<double,2> >::const_iterator
-        it=m_cache_gamma.begin(); it!=m_cache_gamma.end(); ++it)
-    {
-      a_indices(i) = it->first;
-      std::string str1 = "gamma_" + boost::lexical_cast<std::string>(it->first);
-      config.setArray(str1, it->second);
-      std::string str2 = "loglikeconstterm_" + boost::lexical_cast<std::string>(it->first);
-      double v = m_cache_loglike_constterm.find(it->first)->second;
-      config.set(str2, v);
-      ++i;
-    }
-    config.setArray("a_indices", a_indices);
-  }
-}
-
-void bob::learn::em::PLDAMachine::setPLDABase(const boost::shared_ptr<bob::learn::em::PLDABase> plda_base)
-{
-  m_plda_base = plda_base;
-  m_weighted_sum.resizeAndPreserve(getDimF());
-  clearMaps();
-  resizeTmp();
-}
-
-
-void bob::learn::em::PLDAMachine::setWeightedSum(const blitz::Array<double,1>& ws)
-{
-  if(ws.extent(0) != m_weighted_sum.extent(0)) {
-    boost::format m("size of parameter `ws' (%d) does not match the expected size (%d)");
-    m % ws.extent(0) % m_weighted_sum.extent(0);
-    throw std::runtime_error(m.str());
-  }
-  m_weighted_sum.reference(bob::core::array::ccopy(ws));
-}
-
-const blitz::Array<double,2>& bob::learn::em::PLDAMachine::getGamma(const size_t a) const
-{
-  // Checks in both base machine and this machine
-  if (m_plda_base->hasGamma(a)) return m_plda_base->getGamma(a);
-  else if (!hasGamma(a))
-    throw std::runtime_error("Gamma for this number of samples is not currently in cache. You could use the getAddGamma() method instead");
-  return (m_cache_gamma.find(a))->second;
-}
-
-const blitz::Array<double,2>& bob::learn::em::PLDAMachine::getAddGamma(const size_t a)
-{
-  if (m_plda_base->hasGamma(a)) return m_plda_base->getGamma(a);
-  else if (hasGamma(a)) return m_cache_gamma[a];
-  // else computes it and adds it to this machine
-  blitz::Array<double,2> gamma_a(getDimF(),getDimF());
-  m_cache_gamma[a].reference(gamma_a);
-  m_plda_base->computeGamma(a, gamma_a);
-  return m_cache_gamma[a];
-}
-
-double bob::learn::em::PLDAMachine::getLogLikeConstTerm(const size_t a) const
-{
-  // Checks in both base machine and this machine
-  if (!m_plda_base) throw std::runtime_error("No PLDABase set to this machine");
-  if (m_plda_base->hasLogLikeConstTerm(a)) return m_plda_base->getLogLikeConstTerm(a);
-  else if (!hasLogLikeConstTerm(a))
-    throw std::runtime_error("The LogLikelihood constant term for this number of samples is not currently in cache. You could use the getAddLogLikeConstTerm() method instead");
-  return (m_cache_loglike_constterm.find(a))->second;
-}
-
-double bob::learn::em::PLDAMachine::getAddLogLikeConstTerm(const size_t a)
-{
-  if (!m_plda_base) throw std::runtime_error("No PLDABase set to this machine");
-  if (m_plda_base->hasLogLikeConstTerm(a)) return m_plda_base->getLogLikeConstTerm(a);
-  else if (hasLogLikeConstTerm(a)) return m_cache_loglike_constterm[a];
-  // else computes it and adds it to this machine
-  m_cache_loglike_constterm[a] =
-        m_plda_base->computeLogLikeConstTerm(a, getAddGamma(a));
-  return m_cache_loglike_constterm[a];
-}
-
-void bob::learn::em::PLDAMachine::clearMaps()
-{
-  m_cache_gamma.clear();
-  m_cache_loglike_constterm.clear();
-}
-
-double bob::learn::em::PLDAMachine::forward(const blitz::Array<double,1>& sample)
-{
-  return forward_(sample);
-}
-
-double bob::learn::em::PLDAMachine::forward_(const blitz::Array<double,1>& sample)
-{
-  // Computes the log likelihood ratio
-  return computeLogLikelihood(sample, true) - // match
-          (computeLogLikelihood(sample, false) + m_loglikelihood); // no match
-}
-
-double bob::learn::em::PLDAMachine::forward(const blitz::Array<double,2>& samples)
-{
-  // Computes the log likelihood ratio
-  return computeLogLikelihood(samples, true) - // match
-          (computeLogLikelihood(samples, false) + m_loglikelihood); // no match
-}
-
-double bob::learn::em::PLDAMachine::computeLogLikelihood(const blitz::Array<double,1>& sample,
-  bool enroll) const
-{
-  if (!m_plda_base) throw std::runtime_error("No PLDABase set to this machine");
-  // Check dimensionality
-  bob::core::array::assertSameDimensionLength(sample.extent(0), getDimD());
-
-  int n_samples = 1 + (enroll?m_n_samples:0);
-
-  // 3/ Third term of the likelihood: -1/2*X^T*(SIGMA+A.A^T)^-1*X
-  //    Efficient way: -1/2*sum_i(xi^T.sigma^-1.xi - xi^T.sigma^-1*G*(I+G^T.sigma^-1.G)^-1*G^T*sigma^-1.xi
-  //      -1/2*sumWeighted^T*(I+aF^T.(sigma^-1-sigma^-1*G*(I+G^T.sigma^-1.G)^-1*G^T*sigma^-1).F)^-1*sumWeighted
-  //      where sumWeighted = sum_i(F^T*(sigma^-1-sigma^-1*G*(I+G^T.sigma^-1.G)^-1*G^T*sigma^-1)*xi)
-  const blitz::Array<double,2>& beta = getPLDABase()->getBeta();
-  const blitz::Array<double,2>& Ft_beta = getPLDABase()->getFtBeta();
-  const blitz::Array<double,1>& mu = getPLDABase()->getMu();
-  double terma = (enroll?m_nh_sum_xit_beta_xi:0.);
-  // sumWeighted
-  if (enroll && m_n_samples > 0) m_tmp_nf_1 = m_weighted_sum;
-  else m_tmp_nf_1 = 0;
-
-  // terma += -1 / 2. * (xi^t*beta*xi)
-  m_tmp_d_1 = sample - mu;
-  bob::math::prod(beta, m_tmp_d_1, m_tmp_d_2);
-  terma += -1 / 2. * (blitz::sum(m_tmp_d_1*m_tmp_d_2));
-
-  // sumWeighted
-  bob::math::prod(Ft_beta, m_tmp_d_1, m_tmp_nf_2);
-  m_tmp_nf_1 += m_tmp_nf_2;
-  blitz::Array<double,2> gamma_a;
-  if (hasGamma(n_samples) || m_plda_base->hasGamma(n_samples))
-    gamma_a.reference(getGamma(n_samples));
-  else
-  {
-    gamma_a.reference(m_tmp_nf_nf_1);
-    m_plda_base->computeGamma(n_samples, gamma_a);
-  }
-  bob::math::prod(gamma_a, m_tmp_nf_1, m_tmp_nf_2);
-  double termb = 1 / 2. * (blitz::sum(m_tmp_nf_1*m_tmp_nf_2));
-
-  // 1/2/ Constant term of the log likelihood:
-  //      1/ First term of the likelihood: -Nsamples*D/2*log(2*PI)
-  //      2/ Second term of the likelihood: -1/2*log(det(SIGMA+A.A^T))
-  //        Efficient way: -Nsamples/2*log(det(sigma))-Nsamples/2*log(det(I+G^T.sigma^-1.G))
-  //       -1/2*log(det(I+aF^T.(sigma^-1-sigma^-1*G*(I+G^T.sigma^-1.G)*G^T*sigma^-1).F))
-  double log_likelihood; // = getAddLogLikeConstTerm(static_cast<size_t>(n_samples));
-  if (hasLogLikeConstTerm(n_samples) || m_plda_base->hasLogLikeConstTerm(n_samples))
-    log_likelihood = getLogLikeConstTerm(n_samples);
-  else
-    log_likelihood = m_plda_base->computeLogLikeConstTerm(n_samples, gamma_a);
-
-  log_likelihood += terma + termb;
-  return log_likelihood;
-}
-
-double bob::learn::em::PLDAMachine::computeLogLikelihood(const blitz::Array<double,2>& samples,
-  bool enroll) const
-{
-  if (!m_plda_base) throw std::runtime_error("No PLDABase set to this machine");
-  // Check dimensionality
-  bob::core::array::assertSameDimensionLength(samples.extent(1), getDimD());
-
-  int n_samples = samples.extent(0) + (enroll?m_n_samples:0);
-  // 3/ Third term of the likelihood: -1/2*X^T*(SIGMA+A.A^T)^-1*X
-  //    Efficient way: -1/2*sum_i(xi^T.sigma^-1.xi - xi^T.sigma^-1*G*(I+G^T.sigma^-1.G)^-1*G^T*sigma^-1.xi
-  //      -1/2*sumWeighted^T*(I+aF^T.(sigma^-1-sigma^-1*G*(I+G^T.sigma^-1.G)^-1*G^T*sigma^-1).F)^-1*sumWeighted
-  //      where sumWeighted = sum_i(F^T*(sigma^-1-sigma^-1*G*(I+G^T.sigma^-1.G)^-1*G^T*sigma^-1)*xi)
-  const blitz::Array<double,2>& beta = getPLDABase()->getBeta();
-  const blitz::Array<double,2>& Ft_beta = getPLDABase()->getFtBeta();
-  const blitz::Array<double,1>& mu = getPLDABase()->getMu();
-  double terma = (enroll?m_nh_sum_xit_beta_xi:0.);
-  // sumWeighted
-  if (enroll && m_n_samples > 0) m_tmp_nf_1 = m_weighted_sum;
-  else m_tmp_nf_1 = 0;
-  for (int k=0; k<samples.extent(0); ++k)
-  {
-    blitz::Array<double,1> samp = samples(k,blitz::Range::all());
-    m_tmp_d_1 = samp - mu;
-    // terma += -1 / 2. * (xi^t*beta*xi)
-    bob::math::prod(beta, m_tmp_d_1, m_tmp_d_2);
-    terma += -1 / 2. * (blitz::sum(m_tmp_d_1*m_tmp_d_2));
-
-    // sumWeighted
-    bob::math::prod(Ft_beta, m_tmp_d_1, m_tmp_nf_2);
-    m_tmp_nf_1 += m_tmp_nf_2;
-  }
-
-  blitz::Array<double,2> gamma_a;
-  if (hasGamma(n_samples) || m_plda_base->hasGamma(n_samples))
-    gamma_a.reference(getGamma(n_samples));
-  else
-  {
-    gamma_a.reference(m_tmp_nf_nf_1);
-    m_plda_base->computeGamma(n_samples, gamma_a);
-  }
-  bob::math::prod(gamma_a, m_tmp_nf_1, m_tmp_nf_2);
-  double termb = 1 / 2. * (blitz::sum(m_tmp_nf_1*m_tmp_nf_2));
-
-  // 1/2/ Constant term of the log likelihood:
-  //      1/ First term of the likelihood: -Nsamples*D/2*log(2*PI)
-  //      2/ Second term of the likelihood: -1/2*log(det(SIGMA+A.A^T))
-  //        Efficient way: -Nsamples/2*log(det(sigma))-Nsamples/2*log(det(I+G^T.sigma^-1.G))
-  //       -1/2*log(det(I+aF^T.(sigma^-1-sigma^-1*G*(I+G^T.sigma^-1.G)*G^T*sigma^-1).F))
-  double log_likelihood; // = getAddLogLikeConstTerm(static_cast<size_t>(n_samples));
-  if (hasLogLikeConstTerm(n_samples) || m_plda_base->hasLogLikeConstTerm(n_samples))
-    log_likelihood = getLogLikeConstTerm(n_samples);
-  else
-    log_likelihood = m_plda_base->computeLogLikeConstTerm(n_samples, gamma_a);
-
-  log_likelihood += terma + termb;
-  return log_likelihood;
-}
-
-void bob::learn::em::PLDAMachine::resize(const size_t dim_d, const size_t dim_f,
-  const size_t dim_g)
-{
-  m_weighted_sum.resizeAndPreserve(dim_f);
-  clearMaps();
-  resizeTmp();
-}
-
-void bob::learn::em::PLDAMachine::resizeTmp()
-{
-  if (m_plda_base)
-  {
-    m_tmp_d_1.resize(getDimD());
-    m_tmp_d_2.resize(getDimD());
-    m_tmp_nf_1.resize(getDimF());
-    m_tmp_nf_2.resize(getDimF());
-    m_tmp_nf_nf_1.resize(getDimF(), getDimF());
-  }
-}
diff --git a/bob/learn/em/cpp/PLDATrainer.cpp b/bob/learn/em/cpp/PLDATrainer.cpp
deleted file mode 100644
index 323ea6c9420c808d122c734d1d8537ea9d0666fc..0000000000000000000000000000000000000000
--- a/bob/learn/em/cpp/PLDATrainer.cpp
+++ /dev/null
@@ -1,800 +0,0 @@
-/**
- * @date Fri Oct 14 18:07:56 2011 +0200
- * @author Laurent El Shafey <Laurent.El-Shafey@idiap.ch>
- *
- * @brief Probabilistic Linear Discriminant Analysis
- *
- * Copyright (C) Idiap Research Institute, Martigny, Switzerland
- */
-
-
-#include <bob.learn.em/PLDATrainer.h>
-#include <bob.core/check.h>
-#include <bob.core/array_copy.h>
-#include <bob.core/array_random.h>
-#include <bob.math/inv.h>
-#include <bob.math/svd.h>
-#include <bob.core/check.h>
-#include <bob.core/array_repmat.h>
-#include <algorithm>
-#include <limits>
-#include <vector>
-
-#include <bob.math/linear.h>
-#include <bob.math/linsolve.h>
-
-
-
-bob::learn::em::PLDATrainer::PLDATrainer(const bool use_sum_second_order):
-  m_rng(new boost::mt19937()),
-  m_dim_d(0), m_dim_f(0), m_dim_g(0),
-  m_use_sum_second_order(use_sum_second_order),
-  m_initF_method(bob::learn::em::PLDATrainer::RANDOM_F), m_initF_ratio(1.),
-  m_initG_method(bob::learn::em::PLDATrainer::RANDOM_G), m_initG_ratio(1.),
-  m_initSigma_method(bob::learn::em::PLDATrainer::RANDOM_SIGMA),
-  m_initSigma_ratio(1.),
-  m_cache_S(0,0),
-  m_cache_z_first_order(0), m_cache_sum_z_second_order(0,0), m_cache_z_second_order(0),
-  m_cache_n_samples_per_id(0), m_cache_n_samples_in_training(), m_cache_B(0,0),
-  m_cache_Ft_isigma_G(0,0), m_cache_eta(0,0), m_cache_zeta(), m_cache_iota(),
-  m_tmp_nf_1(0), m_tmp_nf_2(0), m_tmp_ng_1(0),
-  m_tmp_D_1(0), m_tmp_D_2(0),
-  m_tmp_nfng_nfng(0,0), m_tmp_D_nfng_1(0,0), m_tmp_D_nfng_2(0,0)
-{
-}
-
-bob::learn::em::PLDATrainer::PLDATrainer(const bob::learn::em::PLDATrainer& other):
-  m_rng(other.m_rng),
-  m_dim_d(other.m_dim_d), m_dim_f(other.m_dim_f), m_dim_g(other.m_dim_g),
-  m_use_sum_second_order(other.m_use_sum_second_order),
-  m_initF_method(other.m_initF_method), m_initF_ratio(other.m_initF_ratio),
-  m_initG_method(other.m_initG_method), m_initG_ratio(other.m_initG_ratio),
-  m_initSigma_method(other.m_initSigma_method), m_initSigma_ratio(other.m_initSigma_ratio),
-  m_cache_S(bob::core::array::ccopy(other.m_cache_S)),
-  m_cache_z_first_order(),
-  m_cache_sum_z_second_order(bob::core::array::ccopy(other.m_cache_sum_z_second_order)),
-  m_cache_z_second_order(),
-  m_cache_n_samples_per_id(other.m_cache_n_samples_per_id),
-  m_cache_n_samples_in_training(other.m_cache_n_samples_in_training),
-  m_cache_B(bob::core::array::ccopy(other.m_cache_B)),
-  m_cache_Ft_isigma_G(bob::core::array::ccopy(other.m_cache_Ft_isigma_G)),
-  m_cache_eta(bob::core::array::ccopy(other.m_cache_eta))
-{
-  bob::core::array::ccopy(other.m_cache_z_first_order, m_cache_z_first_order);
-  bob::core::array::ccopy(other.m_cache_z_second_order, m_cache_z_second_order);
-  bob::core::array::ccopy(other.m_cache_zeta, m_cache_zeta);
-  bob::core::array::ccopy(other.m_cache_iota, m_cache_iota);
-  // Resize working arrays
-  resizeTmp();
-}
-
-bob::learn::em::PLDATrainer::~PLDATrainer() {}
-
-bob::learn::em::PLDATrainer& bob::learn::em::PLDATrainer::operator=
-(const bob::learn::em::PLDATrainer& other)
-{
-  if(this != &other)
-  {
-    m_rng = m_rng,
-    m_dim_d = other.m_dim_d;
-    m_dim_f = other.m_dim_f;
-    m_dim_g = other.m_dim_g;
-    m_use_sum_second_order = other.m_use_sum_second_order;
-    m_initF_method = other.m_initF_method;
-    m_initF_ratio = other.m_initF_ratio;
-    m_initG_method = other.m_initG_method;
-    m_initG_ratio = other.m_initG_ratio;
-    m_initSigma_method = other.m_initSigma_method;
-    m_initSigma_ratio = other.m_initSigma_ratio;
-    m_cache_S = bob::core::array::ccopy(other.m_cache_S);
-    bob::core::array::ccopy(other.m_cache_z_first_order, m_cache_z_first_order);
-    m_cache_sum_z_second_order = bob::core::array::ccopy(other.m_cache_sum_z_second_order);
-    bob::core::array::ccopy(other.m_cache_z_second_order, m_cache_z_second_order);
-    m_cache_n_samples_per_id = other.m_cache_n_samples_per_id;
-    m_cache_n_samples_in_training = other.m_cache_n_samples_in_training;
-    m_cache_B = bob::core::array::ccopy(other.m_cache_B);
-    m_cache_Ft_isigma_G = bob::core::array::ccopy(other.m_cache_Ft_isigma_G);
-    m_cache_eta = bob::core::array::ccopy(other.m_cache_eta);
-    bob::core::array::ccopy(other.m_cache_iota, m_cache_iota);
-    // Resize working arrays
-    resizeTmp();
-  }
-  return *this;
-}
-
-bool bob::learn::em::PLDATrainer::operator==
-  (const bob::learn::em::PLDATrainer& other) const
-{
-  return m_rng == m_rng &&
-         m_dim_d == other.m_dim_d &&
-         m_dim_f == other.m_dim_f &&
-         m_dim_g == other.m_dim_g &&
-         m_initF_method == other.m_initF_method &&
-         m_initF_ratio == other.m_initF_ratio &&
-         m_initG_method == other.m_initG_method &&
-         m_initG_ratio == other.m_initG_ratio &&
-         m_initSigma_method == other.m_initSigma_method &&
-         m_initSigma_ratio == other.m_initSigma_ratio &&
-         bob::core::array::isEqual(m_cache_S, m_cache_S) &&
-         bob::core::array::isEqual(m_cache_z_first_order, other.m_cache_z_first_order) &&
-         bob::core::array::isEqual(m_cache_sum_z_second_order, other.m_cache_sum_z_second_order) &&
-         bob::core::array::isEqual(m_cache_z_second_order, other.m_cache_z_second_order) &&
-         m_cache_n_samples_per_id.size() == m_cache_n_samples_per_id.size() &&
-         std::equal(m_cache_n_samples_per_id.begin(), m_cache_n_samples_per_id.end(), other.m_cache_n_samples_per_id.begin()) &&
-         m_cache_n_samples_in_training.size() == m_cache_n_samples_in_training.size() &&
-         std::equal(m_cache_n_samples_in_training.begin(), m_cache_n_samples_in_training.end(), other.m_cache_n_samples_in_training.begin()) &&
-         bob::core::array::isEqual(m_cache_B, other.m_cache_B) &&
-         bob::core::array::isEqual(m_cache_Ft_isigma_G, other.m_cache_Ft_isigma_G) &&
-         bob::core::array::isEqual(m_cache_eta, other.m_cache_eta) &&
-         bob::core::array::isEqual(m_cache_zeta, other.m_cache_zeta) &&
-         bob::core::array::isEqual(m_cache_iota, other.m_cache_iota);
-}
-
-bool bob::learn::em::PLDATrainer::operator!=
-  (const bob::learn::em::PLDATrainer &other) const
-{
-  return !(this->operator==(other));
-}
-
-bool bob::learn::em::PLDATrainer::is_similar_to
-  (const bob::learn::em::PLDATrainer &other, const double r_epsilon,
-   const double a_epsilon) const
-{
-  return m_rng == m_rng &&
-         m_dim_d == other.m_dim_d &&
-         m_dim_f == other.m_dim_f &&
-         m_dim_g == other.m_dim_g &&
-         m_use_sum_second_order == other.m_use_sum_second_order &&
-         m_initF_method == other.m_initF_method &&
-         bob::core::isClose(m_initF_ratio, other.m_initF_ratio, r_epsilon, a_epsilon) &&
-         m_initG_method == other.m_initG_method &&
-         bob::core::isClose(m_initG_ratio, other.m_initG_ratio, r_epsilon, a_epsilon) &&
-         m_initSigma_method == other.m_initSigma_method &&
-         bob::core::isClose(m_initSigma_ratio, other.m_initSigma_ratio, r_epsilon, a_epsilon) &&
-         bob::core::array::isClose(m_cache_S, m_cache_S, r_epsilon, a_epsilon) &&
-         bob::core::array::isClose(m_cache_z_first_order, other.m_cache_z_first_order, r_epsilon, a_epsilon) &&
-         bob::core::array::isClose(m_cache_sum_z_second_order, other.m_cache_sum_z_second_order, r_epsilon, a_epsilon) &&
-         bob::core::array::isClose(m_cache_z_second_order, other.m_cache_z_second_order, r_epsilon, a_epsilon) &&
-         m_cache_n_samples_per_id.size() == m_cache_n_samples_per_id.size() &&
-         std::equal(m_cache_n_samples_per_id.begin(), m_cache_n_samples_per_id.end(), other.m_cache_n_samples_per_id.begin()) &&
-         m_cache_n_samples_in_training.size() == m_cache_n_samples_in_training.size() &&
-         std::equal(m_cache_n_samples_in_training.begin(), m_cache_n_samples_in_training.end(), other.m_cache_n_samples_in_training.begin()) &&
-         bob::core::array::isClose(m_cache_B, other.m_cache_B, r_epsilon, a_epsilon) &&
-         bob::core::array::isClose(m_cache_Ft_isigma_G, other.m_cache_Ft_isigma_G, r_epsilon, a_epsilon) &&
-         bob::core::array::isClose(m_cache_eta, other.m_cache_eta, r_epsilon, a_epsilon) &&
-         bob::core::array::isClose(m_cache_zeta, other.m_cache_zeta, r_epsilon, a_epsilon) &&
-         bob::core::array::isClose(m_cache_iota, other.m_cache_iota, r_epsilon, a_epsilon);
-}
-
-void bob::learn::em::PLDATrainer::initialize(bob::learn::em::PLDABase& machine,
-  const std::vector<blitz::Array<double,2> >& v_ar)
-{
-  // Checks training data
-  checkTrainingData(v_ar);
-
-  // Gets dimension (first Arrayset)
-  size_t n_features = v_ar[0].extent(1);
-  m_dim_d = machine.getDimD();
-  // Get dimensionalities from the PLDABase
-  bob::core::array::assertSameDimensionLength(n_features, m_dim_d);
-  m_dim_f = machine.getDimF();
-  m_dim_g = machine.getDimG();
-
-  // Reinitializes array members
-  initMembers(v_ar);
-
-  // Computes the mean and the covariance if required
-  computeMeanVariance(machine, v_ar);
-
-  // Initialization (e.g. using scatter)
-  initFGSigma(machine, v_ar);
-}
-
-void bob::learn::em::PLDATrainer::finalize(bob::learn::em::PLDABase& machine,
-  const std::vector<blitz::Array<double,2> >& v_ar)
-{
-  // Precomputes constant parts of the log likelihood and (gamma_a)
-  precomputeLogLike(machine, v_ar);
-  // Adds the case 1 sample if not already done (always used for scoring)
-  machine.getAddGamma(1);
-  machine.getAddLogLikeConstTerm(1);
-}
-
-void bob::learn::em::PLDATrainer::checkTrainingData(const std::vector<blitz::Array<double,2> >& v_ar)
-{
-  // Checks that the vector of Arraysets is not empty
-  if (v_ar.size() == 0) {
-    throw std::runtime_error("input training set is empty");
-  }
-
-  // Gets dimension (first Arrayset)
-  int n_features = v_ar[0].extent(1);
-  // Checks dimension consistency
-  for (size_t i=0; i<v_ar.size(); ++i) {
-    if (v_ar[i].extent(1) != n_features) {
-      boost::format m("number of features (columns) of array for class %u (%d) does not match that of array for class 0 (%d)");
-      m % i % v_ar[0].extent(1) % n_features;
-      throw std::runtime_error(m.str());
-    }
-  }
-}
-
-void bob::learn::em::PLDATrainer::initMembers(const std::vector<blitz::Array<double,2> >& v_ar)
-{
-  // Gets dimension (first Arrayset)
-  const size_t n_features = v_ar[0].extent(1); // dimensionality of the data
-  const size_t n_identities = v_ar.size();
-
-  m_cache_S.resize(n_features, n_features);
-  m_cache_sum_z_second_order.resize(m_dim_f+m_dim_g, m_dim_f+m_dim_g);
-
-  // Loops over the identities
-  for (size_t i=0; i<n_identities; ++i)
-  {
-    // Number of training samples for this identity
-    const size_t n_i = v_ar[i].extent(0);
-    // m_cache_z_first_order
-    blitz::Array<double,2> z_i(n_i, m_dim_f+m_dim_g);
-    m_cache_z_first_order.push_back(z_i);
-    // m_z_second_order
-    if (!m_use_sum_second_order)
-    {
-      blitz::Array<double,3> z2_i(n_i, m_dim_f+m_dim_g, m_dim_f+m_dim_g);
-      m_cache_z_second_order.push_back(z2_i);
-    }
-
-    // m_cache_n_samples_per_id
-    m_cache_n_samples_per_id.push_back(n_i);
-
-    // Maps dependent on the number of samples per identity
-    std::map<size_t,bool>::iterator it;
-    it = m_cache_n_samples_in_training.find(n_i);
-    if (it == m_cache_n_samples_in_training.end())
-    {
-      // Indicates if there are identities with n_i training samples and if
-      // corresponding matrices are up to date.
-      m_cache_n_samples_in_training[n_i] = false;
-      // Allocates arrays for identities with n_i training samples
-      m_cache_zeta[n_i].reference(blitz::Array<double,2>(m_dim_g, m_dim_g));
-      m_cache_iota[n_i].reference(blitz::Array<double,2>(m_dim_f, m_dim_g));
-    }
-  }
-
-  m_cache_B.resize(n_features, m_dim_f+m_dim_g);
-  m_cache_Ft_isigma_G.resize(m_dim_f, m_dim_g);
-  m_cache_eta.resize(m_dim_f, m_dim_g);
-
-  // Working arrays
-  resizeTmp();
-}
-
-void bob::learn::em::PLDATrainer::resizeTmp()
-{
-  m_tmp_nf_1.resize(m_dim_f);
-  m_tmp_nf_2.resize(m_dim_f);
-  m_tmp_ng_1.resize(m_dim_g);
-  m_tmp_D_1.resize(m_dim_d);
-  m_tmp_D_2.resize(m_dim_d);
-  m_tmp_nfng_nfng.resize(m_dim_f+m_dim_g, m_dim_f+m_dim_g);
-  m_tmp_D_nfng_1.resize(m_dim_d, m_dim_f+m_dim_g);
-  m_tmp_D_nfng_2.resize(m_dim_d, m_dim_f+m_dim_g);
-}
-
-void bob::learn::em::PLDATrainer::computeMeanVariance(bob::learn::em::PLDABase& machine,
-  const std::vector<blitz::Array<double,2> >& v_ar)
-{
-  blitz::Array<double,1>& mu = machine.updateMu();
-  blitz::Range all = blitz::Range::all();
-  // TODO: Uncomment variance computation if required
-  /*  if(m_compute_likelihood)
-  {
-    // loads all the data in a single shot - required for scatter
-    blitz::Array<double,2> data(n_features, n_samples);
-    for (size_t i=0; i<n_samples; ++i)
-      data(all,i) = ar(i,all);
-    // Mean and scatter computation
-    bob::math::scatter(data, m_cache_S, mu);
-    // divides scatter by N-1
-    m_cache_S /= static_cast<double>(n_samples-1);
-  }
-  else */
-  {
-    // Computes the mean and updates mu
-    mu = 0.;
-    size_t n_samples = 0;
-    for (size_t j=0; j<v_ar.size(); ++j) {
-      n_samples += v_ar[j].extent(0);
-      for (int i=0; i<v_ar[j].extent(0); ++i)
-        mu += v_ar[j](i,all);
-    }
-    mu /= static_cast<double>(n_samples);
-    m_cache_S = 0.;
-  }
-}
-
-void bob::learn::em::PLDATrainer::initFGSigma(bob::learn::em::PLDABase& machine,
-  const std::vector<blitz::Array<double,2> >& v_ar)
-{
-  // Initializes F, G and sigma
-  initF(machine, v_ar);
-  initG(machine, v_ar);
-  initSigma(machine, v_ar);
-
-  // Precomputes values using new F, G and sigma
-  machine.precompute();
-}
-
-void bob::learn::em::PLDATrainer::initF(bob::learn::em::PLDABase& machine,
-  const std::vector<blitz::Array<double,2> >& v_ar)
-{
-  blitz::Array<double,2>& F = machine.updateF();
-  blitz::Range a = blitz::Range::all();
-
-  // 1: between-class scatter
-  if (m_initF_method == bob::learn::em::PLDATrainer::BETWEEN_SCATTER)
-  {
-    if (machine.getDimF() > v_ar.size()) {
-      boost::format m("The rank of the matrix F ('%ld') can't be larger than the number of classes in the training set ('%ld')");
-      m % machine.getDimF() % v_ar.size();
-      throw std::runtime_error(m.str());
-    }
-
-    // a/ Computes between-class scatter matrix
-    blitz::firstIndex bi;
-    blitz::secondIndex bj;
-    blitz::Array<double,2> S(machine.getDimD(), v_ar.size());
-    S = 0.;
-    m_tmp_D_1 = 0.;
-    for (size_t i=0; i<v_ar.size(); ++i)
-    {
-      blitz::Array<double,1> Si = S(blitz::Range::all(),i);
-      Si = 0.;
-      for (int j=0; j<v_ar[i].extent(0); ++j)
-      {
-        // Si += x_ij
-        Si += v_ar[i](j,a);
-      }
-      // Si = mean of the samples class i
-      Si /= static_cast<double>(v_ar[i].extent(0));
-      m_tmp_D_1 += Si;
-    }
-    m_tmp_D_1 /= static_cast<double>(v_ar.size());
-
-    // b/ Removes the mean
-    S = S(bi,bj) - m_tmp_D_1(bi);
-
-    // c/ SVD of the between-class scatter matrix
-    const size_t n_singular = std::min(machine.getDimD(),v_ar.size());
-    blitz::Array<double,2> U(machine.getDimD(), n_singular);
-    blitz::Array<double,1> sigma(n_singular);
-    bob::math::svd(S, U, sigma);
-
-    // d/ Updates F
-    blitz::Array<double,2> Uslice = U(a, blitz::Range(0,m_dim_f-1));
-    blitz::Array<double,1> sigma_slice = sigma(blitz::Range(0,m_dim_f-1));
-    sigma_slice = blitz::sqrt(sigma_slice);
-    F = Uslice(bi,bj) / sigma_slice(bj);
-  }
-  // otherwise: random initialization
-  else {
-    // F initialization
-    bob::core::array::randn(*m_rng, F);
-    F *= m_initF_ratio;
-  }
-}
-
-void bob::learn::em::PLDATrainer::initG(bob::learn::em::PLDABase& machine,
-  const std::vector<blitz::Array<double,2> >& v_ar)
-{
-  blitz::Array<double,2>& G = machine.updateG();
-  blitz::Range a = blitz::Range::all();
-
-  // 1: within-class scatter
-  if (m_initG_method == bob::learn::em::PLDATrainer::WITHIN_SCATTER)
-  {
-    // a/ Computes within-class scatter matrix
-    blitz::firstIndex bi;
-    blitz::secondIndex bj;
-    size_t Nsamples=0;
-    for (size_t i=0; i<v_ar.size(); ++i)
-      Nsamples += v_ar[i].extent(0);
-
-    blitz::Array<double,2> S(machine.getDimD(), Nsamples);
-    S = 0.;
-    m_tmp_D_1 = 0.;
-    int counter = 0;
-    for (size_t i=0; i<v_ar.size(); ++i)
-    {
-      // Computes the mean of the samples class i
-      m_tmp_D_2 = 0.;
-      for (int j=0; j<v_ar[i].extent(0); ++j)
-      {
-        // m_tmp_D_2 += x_ij
-        m_tmp_D_2 += v_ar[i](j,a);
-      }
-      // m_tmp_D_2 = mean of the samples class i
-      m_tmp_D_2 /= static_cast<double>(v_ar[i].extent(0));
-
-      // Generates the scatter
-      for (int j=0; j<v_ar[i].extent(0); ++j)
-      {
-        blitz::Array<double,1> Si = S(a, counter);
-        // Si = x_ij - mean_i
-        Si = v_ar[i](j,a) - m_tmp_D_2;
-        // mean of the within class
-        m_tmp_D_1 += Si;
-        ++counter;
-      }
-    }
-    m_tmp_D_1 /= static_cast<double>(Nsamples);
-
-    // b/ Removes the mean
-    S = S(bi,bj) - m_tmp_D_1(bi);
-
-    // c/ SVD of the between-class scatter matrix
-    blitz::Array<double,2> U(m_dim_d, std::min(m_dim_d, Nsamples));
-    blitz::Array<double,1> sigma(std::min(m_dim_d, Nsamples));
-    bob::math::svd(S, U, sigma);
-
-    // d/ Updates G
-    blitz::Array<double,2> Uslice = U(blitz::Range::all(), blitz::Range(0,m_dim_g-1));
-    blitz::Array<double,1> sigma_slice = sigma(blitz::Range(0,m_dim_g-1));
-    sigma_slice = blitz::sqrt(sigma_slice);
-    G = Uslice(bi,bj) / sigma_slice(bj);
-  }
-  // otherwise: random initialization
-  else {
-    // G initialization
-    bob::core::array::randn(*m_rng, G);
-    G *= m_initG_ratio;
-  }
-}
-
-void bob::learn::em::PLDATrainer::initSigma(bob::learn::em::PLDABase& machine,
-  const std::vector<blitz::Array<double,2> >& v_ar)
-{
-  blitz::Array<double,1>& sigma = machine.updateSigma();
-  blitz::Range a = blitz::Range::all();
-
-  // 1: percentage of the variance of G
-  if (m_initSigma_method == bob::learn::em::PLDATrainer::VARIANCE_G) {
-    const blitz::Array<double,2>& G = machine.getG();
-    blitz::secondIndex bj;
-    m_tmp_D_1 = blitz::mean(G, bj);
-    // Updates sigma
-    sigma = blitz::fabs(m_tmp_D_1) * m_initSigma_ratio;
-  }
-  // 2: constant value
-  else if (m_initSigma_method == bob::learn::em::PLDATrainer::CONSTANT) {
-    sigma = m_initSigma_ratio;
-  }
-  // 3: percentage of the variance of the data
-  else if (m_initSigma_method == bob::learn::em::PLDATrainer::VARIANCE_DATA) {
-    // a/ Computes the global mean
-    //    m_tmp_D_1 = 1/N sum_i x_i
-    m_tmp_D_1 = 0.;
-    size_t Ns = 0;
-    for (size_t i=0; i<v_ar.size(); ++i)
-    {
-      for (int j=0; j<v_ar[i].extent(0); ++j)
-        m_tmp_D_1 += v_ar[i](j,a);
-      Ns += v_ar[i].extent(0);
-    }
-    m_tmp_D_1 /= static_cast<double>(Ns);
-
-    // b/ Computes the variance:
-    m_tmp_D_2 = 0.;
-    for (size_t i=0; i<v_ar.size(); ++i)
-      for (int j=0; j<v_ar[i].extent(0); ++j)
-        m_tmp_D_2 += blitz::pow2(v_ar[i](j,a) - m_tmp_D_1);
-    sigma = m_initSigma_ratio * m_tmp_D_2 / static_cast<double>(Ns-1);
-  }
-  // otherwise: random initialization
-  else {
-    // sigma initialization
-    bob::core::array::randn(*m_rng, sigma);
-    sigma = blitz::fabs(sigma) * m_initSigma_ratio;
-  }
-  // Apply variance threshold
-  machine.applyVarianceThreshold();
-}
-
-void bob::learn::em::PLDATrainer::eStep(bob::learn::em::PLDABase& machine,
-  const std::vector<blitz::Array<double,2> >& v_ar)
-{
-  // Precomputes useful variables using current estimates of F,G, and sigma
-  precomputeFromFGSigma(machine);
-  // Gets the mean mu from the machine
-  const blitz::Array<double,1>& mu = machine.getMu();
-  const blitz::Array<double,2>& alpha = machine.getAlpha();
-  const blitz::Array<double,2>& F = machine.getF();
-  const blitz::Array<double,2>& FtBeta = machine.getFtBeta();
-  const blitz::Array<double,2>& GtISigma = machine.getGtISigma();
-  blitz::Range a = blitz::Range::all();
-
-  // blitz indices
-  blitz::firstIndex bi;
-  blitz::secondIndex bj;
-  // Initializes sum of z second order statistics to 0
-  m_cache_sum_z_second_order = 0.;
-  for (size_t i=0; i<v_ar.size(); ++i)
-  {
-    // Computes expectation of z_ij = [h_i w_ij]
-    // 1/a/ Computes expectation of h_i
-    // Loop over the samples
-    m_tmp_nf_1 = 0.;
-    for (int j=0; j<v_ar[i].extent(0); ++j)
-    {
-      // m_tmp_D_1 = x_sj-mu
-      m_tmp_D_1 = v_ar[i](j,a) - mu;
-
-      // m_tmp_nf_2 = F^T.beta.(x_sj-mu)
-      bob::math::prod(FtBeta, m_tmp_D_1, m_tmp_nf_2);
-      // m_tmp_nf_1 = sum_j F^T.beta.(x_sj-mu)
-      m_tmp_nf_1 += m_tmp_nf_2;
-    }
-    const blitz::Array<double,2>& gamma_a = machine.getAddGamma(v_ar[i].extent(0));
-    blitz::Range r_hi(0, m_dim_f-1);
-    // m_tmp_nf_2 = E(h_i) = gamma_A  sum_j F^T.beta.(x_sj-mu)
-    bob::math::prod(gamma_a, m_tmp_nf_1, m_tmp_nf_2);
-
-    // 1/b/ Precomputes: m_tmp_D_2 = F.E{h_i}
-    bob::math::prod(F, m_tmp_nf_2, m_tmp_D_2);
-
-    // 2/ First and second order statistics of z
-    // Precomputed values
-    blitz::Array<double,2>& zeta_a = m_cache_zeta[v_ar[i].extent(0)];
-    blitz::Array<double,2>& iota_a = m_cache_iota[v_ar[i].extent(0)];
-    blitz::Array<double,2> iotat_a = iota_a.transpose(1,0);
-
-    // Extracts statistics of z_ij = [h_i w_ij] from y_i = [h_i w_i1 ... w_iJ]
-    blitz::Range r1(0, m_dim_f-1);
-    blitz::Range r2(m_dim_f, m_dim_f+m_dim_g-1);
-    for (int j=0; j<v_ar[i].extent(0); ++j)
-    {
-      // 1/ First order statistics of z
-      blitz::Array<double,1> z_first_order_ij_1 = m_cache_z_first_order[i](j,r1);
-      z_first_order_ij_1 = m_tmp_nf_2; // E{h_i}
-      // m_tmp_D_1 = x_sj - mu - F.E{h_i}
-      m_tmp_D_1 = v_ar[i](j,a) - mu - m_tmp_D_2;
-      // m_tmp_ng_1 = G^T.sigma^-1.(x_sj-mu-fhi)
-      bob::math::prod(GtISigma, m_tmp_D_1, m_tmp_ng_1);
-      // z_first_order_ij_2 = (Id+G^T.sigma^-1.G)^-1.G^T.sigma^-1.(x_sj-mu) = E{w_ij}
-      blitz::Array<double,1> z_first_order_ij_2 = m_cache_z_first_order[i](j,r2);
-      bob::math::prod(alpha, m_tmp_ng_1, z_first_order_ij_2);
-
-      // 2/ Second order statistics of z
-      blitz::Array<double,2> z_sum_so_11 = m_cache_sum_z_second_order(r1,r1);
-      blitz::Array<double,2> z_sum_so_12 = m_cache_sum_z_second_order(r1,r2);
-      blitz::Array<double,2> z_sum_so_21 = m_cache_sum_z_second_order(r2,r1);
-      blitz::Array<double,2> z_sum_so_22 = m_cache_sum_z_second_order(r2,r2);
-      if (m_use_sum_second_order)
-      {
-        z_sum_so_11 += gamma_a + z_first_order_ij_1(bi) * z_first_order_ij_1(bj);
-        z_sum_so_12 += iota_a + z_first_order_ij_1(bi) * z_first_order_ij_2(bj);
-        z_sum_so_21 += iotat_a + z_first_order_ij_2(bi) * z_first_order_ij_1(bj);
-        z_sum_so_22 += zeta_a + z_first_order_ij_2(bi) * z_first_order_ij_2(bj);
-      }
-      else
-      {
-        blitz::Array<double,2> z_so_11 = m_cache_z_second_order[i](j,r1,r1);
-        z_so_11 = gamma_a + z_first_order_ij_1(bi) * z_first_order_ij_1(bj);
-        z_sum_so_11 += z_so_11;
-        blitz::Array<double,2> z_so_12 = m_cache_z_second_order[i](j,r1,r2);
-        z_so_12 = iota_a + z_first_order_ij_1(bi) * z_first_order_ij_2(bj);
-        z_sum_so_12 += z_so_12;
-        blitz::Array<double,2> z_so_21 = m_cache_z_second_order[i](j,r2,r1);
-        z_so_21 = iotat_a + z_first_order_ij_2(bi) * z_first_order_ij_1(bj);
-        z_sum_so_21 += z_so_21;
-        blitz::Array<double,2> z_so_22 = m_cache_z_second_order[i](j,r2,r2);
-        z_so_22 = zeta_a + z_first_order_ij_2(bi) * z_first_order_ij_2(bj);
-        z_sum_so_22 += z_so_22;
-      }
-    }
-  }
-}
-
-void bob::learn::em::PLDATrainer::precomputeFromFGSigma(bob::learn::em::PLDABase& machine)
-{
-  // Blitz compatibility: ugly fix (const_cast, as old blitz version does not
-  // provide a non-const version of transpose())
-  const blitz::Array<double,2>& F = machine.getF();
-  const blitz::Array<double,2> Ft = const_cast<blitz::Array<double,2>&>(F).transpose(1,0);
-  const blitz::Array<double,2>& Gt_isigma = machine.getGtISigma();
-  const blitz::Array<double,2> Gt_isigma_t = const_cast<blitz::Array<double,2>&>(Gt_isigma).transpose(1,0);
-  const blitz::Array<double,2>& alpha = machine.getAlpha();
-
-  // Precomputes F, G and sigma-based expressions
-  bob::math::prod(Ft, Gt_isigma_t, m_cache_Ft_isigma_G);
-  bob::math::prod(m_cache_Ft_isigma_G, alpha, m_cache_eta);
-  blitz::Array<double,2> etat = m_cache_eta.transpose(1,0);
-
-  // Reinitializes all the zeta_a and iota_a
-  std::map<size_t,bool>::iterator it;
-  for (it=m_cache_n_samples_in_training.begin(); it!=m_cache_n_samples_in_training.end();
-      ++it)
-    it->second = false;
-
-  for (it=m_cache_n_samples_in_training.begin(); it!=m_cache_n_samples_in_training.end();
-      ++it)
-  {
-    size_t n_i = it->first;
-    // Precomputes zeta and iota for identities with q_i training samples,
-    // if not already done
-    if (!it->second)
-    {
-      const blitz::Array<double,2>& gamma_a = machine.getAddGamma(n_i);
-      blitz::Array<double,2>& zeta_a = m_cache_zeta[n_i];
-      blitz::Array<double,2>& iota_a = m_cache_iota[n_i];
-      bob::math::prod(gamma_a, m_cache_eta, iota_a);
-      bob::math::prod(etat, iota_a, zeta_a);
-      zeta_a += alpha;
-      iota_a = - iota_a;
-      // Now up to date
-      it->second = true;
-    }
-  }
-}
-
-void bob::learn::em::PLDATrainer::precomputeLogLike(bob::learn::em::PLDABase& machine,
-  const std::vector<blitz::Array<double,2> >& v_ar)
-{
-  // Precomputes the log determinant of alpha and sigma
-  machine.precomputeLogLike();
-
-  // Precomputes the log likelihood constant term
-  std::map<size_t,bool>::iterator it;
-  for (it=m_cache_n_samples_in_training.begin();
-       it!=m_cache_n_samples_in_training.end(); ++it)
-  {
-    // Precomputes the log likelihood constant term for identities with q_i
-    // training samples, if not already done
-    machine.getAddLogLikeConstTerm(it->first);
-  }
-}
-
-
-void bob::learn::em::PLDATrainer::mStep(bob::learn::em::PLDABase& machine,
-  const std::vector<blitz::Array<double,2> >& v_ar)
-{
-  // 1/ New estimate of B = {F G}
-  updateFG(machine, v_ar);
-
-  // 2/ New estimate of Sigma
-  updateSigma(machine, v_ar);
-
-  // 3/ Precomputes new values after updating F, G and sigma
-  machine.precompute();
-  // Precomputes useful variables using current estimates of F,G, and sigma
-  precomputeFromFGSigma(machine);
-}
-
-void bob::learn::em::PLDATrainer::updateFG(bob::learn::em::PLDABase& machine,
-  const std::vector<blitz::Array<double,2> >& v_ar)
-{
-  /// Computes the B matrix (B = [F G])
-  /// B = (sum_ij (x_ij-mu).E{z_i}^T).(sum_ij E{z_i.z_i^T})^-1
-
-  // 1/ Computes the numerator (sum_ij (x_ij-mu).E{z_i}^T)
-  // Gets the mean mu from the machine
-  const blitz::Array<double,1>& mu = machine.getMu();
-  blitz::Range a = blitz::Range::all();
-  m_tmp_D_nfng_2 = 0.;
-  for (size_t i=0; i<v_ar.size(); ++i)
-  {
-    // Loop over the samples
-    for (int j=0; j<v_ar[i].extent(0); ++j)
-    {
-      // m_tmp_D_1 = x_sj-mu
-      m_tmp_D_1 = v_ar[i](j,a) - mu;
-      // z_first_order_ij = E{z_ij}
-      blitz::Array<double,1> z_first_order_ij = m_cache_z_first_order[i](j, a);
-      // m_tmp_D_nfng_1 = (x_sj-mu).E{z_ij}^T
-      bob::math::prod(m_tmp_D_1, z_first_order_ij, m_tmp_D_nfng_1);
-      m_tmp_D_nfng_2 += m_tmp_D_nfng_1;
-    }
-  }
-
-  // 2/ Computes the denominator inv(sum_ij E{z_i.z_i^T})
-  bob::math::inv(m_cache_sum_z_second_order, m_tmp_nfng_nfng);
-
-  // 3/ Computes numerator / denominator
-  bob::math::prod(m_tmp_D_nfng_2, m_tmp_nfng_nfng, m_cache_B);
-
-  // 4/ Updates the machine
-  blitz::Array<double, 2>& F = machine.updateF();
-  blitz::Array<double, 2>& G = machine.updateG();
-  F = m_cache_B(a, blitz::Range(0, m_dim_f-1));
-  G = m_cache_B(a, blitz::Range(m_dim_f, m_dim_f+m_dim_g-1));
-}
-
-void bob::learn::em::PLDATrainer::updateSigma(bob::learn::em::PLDABase& machine,
-  const std::vector<blitz::Array<double,2> >& v_ar)
-{
-  /// Computes the Sigma matrix
-  /// Sigma = 1/IJ sum_ij Diag{(x_ij-mu).(x_ij-mu)^T - B.E{z_i}.(x_ij-mu)^T}
-
-  // Gets the mean mu and the matrix sigma from the machine
-  blitz::Array<double,1>& sigma = machine.updateSigma();
-  const blitz::Array<double,1>& mu = machine.getMu();
-  blitz::Range a = blitz::Range::all();
-
-  sigma = 0.;
-  size_t n_IJ=0; /// counts the number of samples
-  for (size_t i=0; i<v_ar.size(); ++i)
-  {
-    // Loop over the samples
-    for (int j=0; j<v_ar[i].extent(0); ++j)
-    {
-      // m_tmp_D_1 = x_ij-mu
-      m_tmp_D_1 = v_ar[i](j,a) - mu;
-      // sigma += Diag{(x_ij-mu).(x_ij-mu)^T}
-      sigma += blitz::pow2(m_tmp_D_1);
-
-      // z_first_order_ij = E{z_ij}
-      blitz::Array<double,1> z_first_order_ij = m_cache_z_first_order[i](j,a);
-      // m_tmp_D_2 = B.E{z_ij}
-      bob::math::prod(m_cache_B, z_first_order_ij, m_tmp_D_2);
-      // sigma -= Diag{B.E{z_ij}.(x_ij-mu)
-      sigma -= (m_tmp_D_1 * m_tmp_D_2);
-      ++n_IJ;
-    }
-  }
-  // Normalizes by the number of samples
-  sigma /= static_cast<double>(n_IJ);
-  // Apply variance threshold
-  machine.applyVarianceThreshold();
-}
-
-
-void bob::learn::em::PLDATrainer::enroll(bob::learn::em::PLDAMachine& plda_machine,
-  const blitz::Array<double,2>& ar) const
-{
-  // Gets dimension
-  const size_t dim_d = ar.extent(1);
-  const int n_samples = ar.extent(0);
-  // Compare the dimensionality from the base trainer/machine with the one
-  // of the enrollment samples
-  if (plda_machine.getDimD() != dim_d) {
-    boost::format m("the extent of the D dimension of the input machine (%u) does not match the input sample (%u)");
-    m % plda_machine.getDimD() % dim_d;
-    throw std::runtime_error(m.str());
-  }
-  const size_t dim_f = plda_machine.getDimF();
-
-  // Resize working arrays
-  m_tmp_D_1.resize(dim_d);
-  m_tmp_D_2.resize(dim_d);
-  m_tmp_nf_1.resize(dim_f);
-
-  // Useful values from the base machine
-  blitz::Array<double,1>& weighted_sum = plda_machine.updateWeightedSum();
-  const blitz::Array<double,1>& mu = plda_machine.getPLDABase()->getMu();
-  const blitz::Array<double,2>& beta = plda_machine.getPLDABase()->getBeta();
-  const blitz::Array<double,2>& FtBeta = plda_machine.getPLDABase()->getFtBeta();
-
-  // Updates the PLDA machine
-  plda_machine.setNSamples(n_samples);
-  double terma = 0.;
-  weighted_sum = 0.;
-  blitz::Range a = blitz::Range::all();
-  for (int i=0; i<n_samples; ++i) {
-    m_tmp_D_1 =  ar(i,a) - mu;
-    // a/ weighted sum
-    bob::math::prod(FtBeta, m_tmp_D_1, m_tmp_nf_1);
-    weighted_sum += m_tmp_nf_1;
-    // b/ first xi dependent term of the log likelihood
-    bob::math::prod(beta, m_tmp_D_1, m_tmp_D_2);
-    terma += -1 / 2. * blitz::sum(m_tmp_D_1 * m_tmp_D_2);
-  }
-  plda_machine.setWSumXitBetaXi(terma);
-
-  // Adds the precomputed values for the cases N and N+1 if not already
-  // in the base machine (used by the forward function, 1 already added)
-  plda_machine.getAddGamma(n_samples);
-  plda_machine.getAddLogLikeConstTerm(n_samples);
-  plda_machine.getAddGamma(n_samples+1);
-  plda_machine.getAddLogLikeConstTerm(n_samples+1);
-  plda_machine.setLogLikelihood(plda_machine.computeLogLikelihood(
-                                  blitz::Array<double,2>(0,dim_d),true));
-}
diff --git a/bob/learn/em/empca_trainer.cpp b/bob/learn/em/empca_trainer.cpp
deleted file mode 100644
index 70cbe2037f8adc051c8db0202dd8da6e352bbe53..0000000000000000000000000000000000000000
--- a/bob/learn/em/empca_trainer.cpp
+++ /dev/null
@@ -1,352 +0,0 @@
-/**
- * @author Tiago de Freitas Pereira <tiago.pereira@idiap.ch>
- * @date Tue 03 Fev 11:22:00 2015
- *
- * @brief Python API for bob::learn::em
- *
- * Copyright (C) 2011-2014 Idiap Research Institute, Martigny, Switzerland
- */
-
-#include "main.h"
-
-/******************************************************************/
-/************ Constructor Section *********************************/
-/******************************************************************/
-
-static auto EMPCATrainer_doc = bob::extension::ClassDoc(
-  BOB_EXT_MODULE_PREFIX ".EMPCATrainer",
-   "Trains a :py:class:`bob.learn.linear.Machine` using an Expectation-Maximization algorithm on the given dataset [Bishop1999]_ [Roweis1998]_ \n\n"
-    "Notations used are the ones from [Bishop1999]_\n\n"
-    "The probabilistic model is given by: :math:`t = Wx + \\mu + \\epsilon`\n\n"
-    " - :math:`t` is the observed data (dimension :math:`f`)\n"
-    " - :math:`W` is a  projection matrix (dimension :math:`f \\times d`)\n"
-    " - :math:`x` is the projected data (dimension :math:`d < f`)\n"
-    " - :math:`\\mu` is the mean of the data (dimension :math:`f`)\n"
-    " - :math:`\\epsilon` is the noise of the data (dimension :math:`f`) \n"
-    " - Gaussian with zero-mean and covariance matrix :math:`\\sigma^2 Id`"
-
-).add_constructor(
-  bob::extension::FunctionDoc(
-    "__init__",
-
-    "Creates a EMPCATrainer",
-    "",
-    true
-  )
-  .add_prototype("other","")
-  .add_prototype("","")
-
-  .add_parameter("other", ":py:class:`bob.learn.em.EMPCATrainer`", "A EMPCATrainer object to be copied.")
-
-);
-
-
-static int PyBobLearnEMEMPCATrainer_init_copy(PyBobLearnEMEMPCATrainerObject* self, PyObject* args, PyObject* kwargs) {
-
-  char** kwlist = EMPCATrainer_doc.kwlist(1);
-  PyBobLearnEMEMPCATrainerObject* tt;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", kwlist, &PyBobLearnEMEMPCATrainer_Type, &tt)){
-    EMPCATrainer_doc.print_usage();
-    return -1;
-  }
-
-  self->cxx.reset(new bob::learn::em::EMPCATrainer(*tt->cxx));
-  return 0;
-}
-
-
-static int PyBobLearnEMEMPCATrainer_init(PyBobLearnEMEMPCATrainerObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  int nargs = (args?PyTuple_Size(args):0) + (kwargs?PyDict_Size(kwargs):0);
-
-  switch (nargs) {
-
-    case 0:{ //default initializer ()
-      self->cxx.reset(new bob::learn::em::EMPCATrainer());
-      return 0;
-    }
-    case 1:{
-      return PyBobLearnEMEMPCATrainer_init_copy(self, args, kwargs);
-    }
-    default:{
-      PyErr_Format(PyExc_RuntimeError, "number of arguments mismatch - %s requires 0 or 1 arguments, but you provided %d (see help)", Py_TYPE(self)->tp_name, nargs);
-      EMPCATrainer_doc.print_usage();
-      return -1;
-    }
-  }
-  BOB_CATCH_MEMBER("cannot create EMPCATrainer", -1)
-  return 0;
-}
-
-
-static void PyBobLearnEMEMPCATrainer_delete(PyBobLearnEMEMPCATrainerObject* self) {
-  self->cxx.reset();
-  Py_TYPE(self)->tp_free((PyObject*)self);
-}
-
-
-int PyBobLearnEMEMPCATrainer_Check(PyObject* o) {
-  return PyObject_IsInstance(o, reinterpret_cast<PyObject*>(&PyBobLearnEMEMPCATrainer_Type));
-}
-
-
-static PyObject* PyBobLearnEMEMPCATrainer_RichCompare(PyBobLearnEMEMPCATrainerObject* self, PyObject* other, int op) {
-  BOB_TRY
-
-  if (!PyBobLearnEMEMPCATrainer_Check(other)) {
-    PyErr_Format(PyExc_TypeError, "cannot compare `%s' with `%s'", Py_TYPE(self)->tp_name, Py_TYPE(other)->tp_name);
-    return 0;
-  }
-  auto other_ = reinterpret_cast<PyBobLearnEMEMPCATrainerObject*>(other);
-  switch (op) {
-    case Py_EQ:
-      if (*self->cxx==*other_->cxx) Py_RETURN_TRUE; else Py_RETURN_FALSE;
-    case Py_NE:
-      if (*self->cxx==*other_->cxx) Py_RETURN_FALSE; else Py_RETURN_TRUE;
-    default:
-      Py_INCREF(Py_NotImplemented);
-      return Py_NotImplemented;
-  }
-  BOB_CATCH_MEMBER("cannot compare EMPCATrainer objects", 0)
-}
-
-
-/******************************************************************/
-/************ Variables Section ***********************************/
-/******************************************************************/
-
-
-/***** sigma_2 *****/
-static auto sigma2 = bob::extension::VariableDoc(
-  "sigma2",
-  "float",
-  "The noise sigma2 of the probabilistic model",
-  ""
-);
-PyObject* PyBobLearnEMEMPCATrainer_getSigma2(PyBobLearnEMEMPCATrainerObject* self, void*){
-  BOB_TRY
-  return Py_BuildValue("d",self->cxx->getSigma2());
-  BOB_CATCH_MEMBER("sigma2 could not be read", 0)
-}
-int PyBobLearnEMEMPCATrainer_setSigma2(PyBobLearnEMEMPCATrainerObject* self, PyObject* value, void*){
-  BOB_TRY
-
-  if(!PyBob_NumberCheck(value)){
-    PyErr_Format(PyExc_RuntimeError, "%s %s expects a float", Py_TYPE(self)->tp_name, sigma2.name());
-    return -1;
-  }
-
-  self->cxx->setSigma2(PyFloat_AS_DOUBLE(value));
-  return 0;
-  BOB_CATCH_MEMBER("sigma2 could not be set", -1)
-}
-
-
-static PyGetSetDef PyBobLearnEMEMPCATrainer_getseters[] = {
-  {
-    sigma2.name(),
-    (getter)PyBobLearnEMEMPCATrainer_getSigma2,
-    (setter)PyBobLearnEMEMPCATrainer_setSigma2,
-    sigma2.doc(),
-    0
-  },
-  {0}  // Sentinel
-};
-
-
-/******************************************************************/
-/************ Functions Section ***********************************/
-/******************************************************************/
-
-/*** initialize ***/
-static auto initialize = bob::extension::FunctionDoc(
-  "initialize",
-  "",
-  "",
-  true
-)
-.add_prototype("linear_machine, data, [rng]")
-.add_parameter("linear_machine", ":py:class:`bob.learn.linear.Machine`", "LinearMachine Object")
-.add_parameter("data", "array_like <float, 2D>", "Input data")
-.add_parameter("rng", ":py:class:`bob.core.random.mt19937`", "The Mersenne Twister mt19937 random generator used for the initialization of subspaces/arrays before the EM loop.");
-static PyObject* PyBobLearnEMEMPCATrainer_initialize(PyBobLearnEMEMPCATrainerObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  /* Parses input arguments in a single shot */
-  char** kwlist = initialize.kwlist(0);
-
-  PyBobLearnLinearMachineObject* linear_machine = 0;
-  PyBlitzArrayObject* data                      = 0;
-  PyBoostMt19937Object* rng = 0;
-
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O&|O!", kwlist, &PyBobLearnLinearMachine_Type, &linear_machine,
-                                                                 &PyBlitzArray_Converter, &data,
-                                                                 &PyBoostMt19937_Type, &rng)) return 0;
-  auto data_ = make_safe(data);
-
-  if(rng){
-    self->cxx->setRng(rng->rng);
-  }
-
-
-  self->cxx->initialize(*linear_machine->cxx, *PyBlitzArrayCxx_AsBlitz<double,2>(data));
-
-  BOB_CATCH_MEMBER("cannot perform the initialize method", 0)
-
-  Py_RETURN_NONE;
-}
-
-
-/*** e_step ***/
-static auto e_step = bob::extension::FunctionDoc(
-  "e_step",
-  "",
-  "",
-  true
-)
-.add_prototype("linear_machine,data")
-.add_parameter("linear_machine", ":py:class:`bob.learn.linear.Machine`", "LinearMachine Object")
-.add_parameter("data", "array_like <float, 2D>", "Input data");
-static PyObject* PyBobLearnEMEMPCATrainer_e_step(PyBobLearnEMEMPCATrainerObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  /* Parses input arguments in a single shot */
-  char** kwlist = e_step.kwlist(0);
-
-  PyBobLearnLinearMachineObject* linear_machine;
-  PyBlitzArrayObject* data = 0;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O&", kwlist, &PyBobLearnLinearMachine_Type, &linear_machine,
-                                                                 &PyBlitzArray_Converter, &data)) return 0;
-  auto data_ = make_safe(data);
-
-  self->cxx->eStep(*linear_machine->cxx, *PyBlitzArrayCxx_AsBlitz<double,2>(data));
-
-
-  BOB_CATCH_MEMBER("cannot perform the e_step method", 0)
-
-  Py_RETURN_NONE;
-}
-
-
-/*** m_step ***/
-static auto m_step = bob::extension::FunctionDoc(
-  "m_step",
-  "",
-  0,
-  true
-)
-.add_prototype("linear_machine,data")
-.add_parameter("linear_machine", ":py:class:`bob.learn.linear.Machine`", "LinearMachine Object")
-.add_parameter("data", "array_like <float, 2D>", "Input data");
-static PyObject* PyBobLearnEMEMPCATrainer_m_step(PyBobLearnEMEMPCATrainerObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  /* Parses input arguments in a single shot */
-  char** kwlist = m_step.kwlist(0);
-
-  PyBobLearnLinearMachineObject* linear_machine;
-  PyBlitzArrayObject* data = 0;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O&", kwlist, &PyBobLearnLinearMachine_Type, &linear_machine,
-                                                                 &PyBlitzArray_Converter, &data)) return 0;
-  auto data_ = make_safe(data);
-
-  self->cxx->mStep(*linear_machine->cxx, *PyBlitzArrayCxx_AsBlitz<double,2>(data));
-
-
-  BOB_CATCH_MEMBER("cannot perform the m_step method", 0)
-
-  Py_RETURN_NONE;
-}
-
-
-/*** computeLikelihood ***/
-static auto compute_likelihood = bob::extension::FunctionDoc(
-  "compute_likelihood",
-  "",
-  0,
-  true
-)
-.add_prototype("linear_machine")
-.add_parameter("linear_machine", ":py:class:`bob.learn.linear.Machine`", "LinearMachine Object");
-static PyObject* PyBobLearnEMEMPCATrainer_compute_likelihood(PyBobLearnEMEMPCATrainerObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  /* Parses input arguments in a single shot */
-  char** kwlist = compute_likelihood.kwlist(0);
-
-  PyBobLearnLinearMachineObject* linear_machine;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", kwlist, &PyBobLearnLinearMachine_Type, &linear_machine)) return 0;
-
-  double value = self->cxx->computeLikelihood(*linear_machine->cxx);
-  return Py_BuildValue("d", value);
-
-  BOB_CATCH_MEMBER("cannot perform the computeLikelihood method", 0)
-}
-
-
-
-static PyMethodDef PyBobLearnEMEMPCATrainer_methods[] = {
-  {
-    initialize.name(),
-    (PyCFunction)PyBobLearnEMEMPCATrainer_initialize,
-    METH_VARARGS|METH_KEYWORDS,
-    initialize.doc()
-  },
-  {
-    e_step.name(),
-    (PyCFunction)PyBobLearnEMEMPCATrainer_e_step,
-    METH_VARARGS|METH_KEYWORDS,
-    e_step.doc()
-  },
-  {
-    m_step.name(),
-    (PyCFunction)PyBobLearnEMEMPCATrainer_m_step,
-    METH_VARARGS|METH_KEYWORDS,
-    m_step.doc()
-  },
-  {
-    compute_likelihood.name(),
-    (PyCFunction)PyBobLearnEMEMPCATrainer_compute_likelihood,
-    METH_VARARGS|METH_KEYWORDS,
-    compute_likelihood.doc()
-  },
-  {0} /* Sentinel */
-};
-
-
-/******************************************************************/
-/************ Module Section **************************************/
-/******************************************************************/
-
-// Define the Gaussian type struct; will be initialized later
-PyTypeObject PyBobLearnEMEMPCATrainer_Type = {
-  PyVarObject_HEAD_INIT(0,0)
-  0
-};
-
-bool init_BobLearnEMEMPCATrainer(PyObject* module)
-{
-  // initialize the type struct
-  PyBobLearnEMEMPCATrainer_Type.tp_name = EMPCATrainer_doc.name();
-  PyBobLearnEMEMPCATrainer_Type.tp_basicsize = sizeof(PyBobLearnEMEMPCATrainerObject);
-  PyBobLearnEMEMPCATrainer_Type.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE;//Enable the class inheritance
-  PyBobLearnEMEMPCATrainer_Type.tp_doc = EMPCATrainer_doc.doc();
-
-  // set the functions
-  PyBobLearnEMEMPCATrainer_Type.tp_new = PyType_GenericNew;
-  PyBobLearnEMEMPCATrainer_Type.tp_init = reinterpret_cast<initproc>(PyBobLearnEMEMPCATrainer_init);
-  PyBobLearnEMEMPCATrainer_Type.tp_dealloc = reinterpret_cast<destructor>(PyBobLearnEMEMPCATrainer_delete);
-  PyBobLearnEMEMPCATrainer_Type.tp_richcompare = reinterpret_cast<richcmpfunc>(PyBobLearnEMEMPCATrainer_RichCompare);
-  PyBobLearnEMEMPCATrainer_Type.tp_methods = PyBobLearnEMEMPCATrainer_methods;
-  PyBobLearnEMEMPCATrainer_Type.tp_getset = PyBobLearnEMEMPCATrainer_getseters;
-  PyBobLearnEMEMPCATrainer_Type.tp_call = reinterpret_cast<ternaryfunc>(PyBobLearnEMEMPCATrainer_compute_likelihood);
-
-
-  // check that everything is fine
-  if (PyType_Ready(&PyBobLearnEMEMPCATrainer_Type) < 0) return false;
-
-  // add the type to the module
-  Py_INCREF(&PyBobLearnEMEMPCATrainer_Type);
-  return PyModule_AddObject(module, "EMPCATrainer", (PyObject*)&PyBobLearnEMEMPCATrainer_Type) >= 0;
-}
diff --git a/bob/learn/em/gaussian.cpp b/bob/learn/em/gaussian.cpp
deleted file mode 100644
index 945da0f93f76bb6546452f781c5ebedd8faddd55..0000000000000000000000000000000000000000
--- a/bob/learn/em/gaussian.cpp
+++ /dev/null
@@ -1,652 +0,0 @@
-/**
- * @author Tiago de Freitas Pereira <tiago.pereira@idiap.ch>
- * @date Fri 21 Nov 10:38:48 2013
- *
- * @brief Python API for bob::learn::em
- *
- * Copyright (C) 2011-2014 Idiap Research Institute, Martigny, Switzerland
- */
-
-#include "main.h"
-
-/******************************************************************/
-/************ Constructor Section *********************************/
-/******************************************************************/
-
-static auto Gaussian_doc = bob::extension::ClassDoc(
-  BOB_EXT_MODULE_PREFIX ".Gaussian",
-  "This class implements a multivariate diagonal Gaussian distribution"
-).add_constructor(
-  bob::extension::FunctionDoc(
-    "__init__",
-    "Constructs a new multivariate gaussian object",
-    "",
-    true
-  )
-  .add_prototype("n_inputs","")
-  .add_prototype("other","")
-  .add_prototype("hdf5","")
-  .add_prototype("","")
-
-  .add_parameter("n_inputs", "int", "Dimension of the feature vector")
-  .add_parameter("other", ":py:class:`bob.learn.em.GMMStats`", "A GMMStats object to be copied.")
-  .add_parameter("hdf5", ":py:class:`bob.io.base.HDF5File`", "An HDF5 file open for reading")
-);
-
-
-
-static int PyBobLearnEMGaussian_init_number(PyBobLearnEMGaussianObject* self, PyObject* args, PyObject* kwargs) {
-
-  char** kwlist = Gaussian_doc.kwlist(0);
-  int n_inputs=1;
-  //Parsing the input argments
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, &n_inputs))
-    return -1;
-
-  if(n_inputs < 0){
-    PyErr_Format(PyExc_TypeError, "input argument must be greater than or equal to zero");
-    Gaussian_doc.print_usage();
-    return -1;
-   }
-
-  self->cxx.reset(new bob::learn::em::Gaussian(n_inputs));
-  return 0;
-}
-
-static int PyBobLearnEMGaussian_init_copy(PyBobLearnEMGaussianObject* self, PyObject* args, PyObject* kwargs) {
-
-  char** kwlist = Gaussian_doc.kwlist(1);
-  PyBobLearnEMGaussianObject* tt;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", kwlist, &PyBobLearnEMGaussian_Type, &tt)){
-    Gaussian_doc.print_usage();
-    return -1;
-  }
-
-  self->cxx.reset(new bob::learn::em::Gaussian(*tt->cxx));
-  return 0;
-}
-
-static int PyBobLearnEMGaussian_init_hdf5(PyBobLearnEMGaussianObject* self, PyObject* args, PyObject* kwargs) {
-
-  char** kwlist = Gaussian_doc.kwlist(2);
-
-  PyBobIoHDF5FileObject* config = 0;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, &PyBobIoHDF5File_Converter, &config)){
-    Gaussian_doc.print_usage();
-    return -1;
-  }
-  auto config_ = make_safe(config);
-
-  self->cxx.reset(new bob::learn::em::Gaussian(*(config->f)));
-
-  return 0;
-}
-
-
-static int PyBobLearnEMGaussian_init(PyBobLearnEMGaussianObject* self, PyObject* args, PyObject* kwargs) {
-
-  BOB_TRY
-
-  // get the number of command line arguments
-  Py_ssize_t nargs = (args?PyTuple_Size(args):0) + (kwargs?PyDict_Size(kwargs):0);
-  if (nargs==0){
-    self->cxx.reset(new bob::learn::em::Gaussian());
-    return 0;
-  }
-
-  //Reading the input argument
-  PyObject* arg = 0;
-  if (PyTuple_Size(args))
-    arg = PyTuple_GET_ITEM(args, 0);
-  else {
-    PyObject* tmp = PyDict_Values(kwargs);
-    auto tmp_ = make_safe(tmp);
-    arg = PyList_GET_ITEM(tmp, 0);
-  }
-
-  /**If the constructor input is a number**/
-  if (PyBob_NumberCheck(arg))
-    return PyBobLearnEMGaussian_init_number(self, args, kwargs);
-  /**If the constructor input is Gaussian object**/
-  else if (PyBobLearnEMGaussian_Check(arg))
-    return PyBobLearnEMGaussian_init_copy(self, args, kwargs);
-  /**If the constructor input is a HDF5**/
-  else if (PyBobIoHDF5File_Check(arg))
-    return PyBobLearnEMGaussian_init_hdf5(self, args, kwargs);
-  else {
-      PyErr_Format(PyExc_TypeError, "invalid input argument");
-      Gaussian_doc.print_usage();
-      return -1;
-  }
-
-  BOB_CATCH_MEMBER("cannot create Gaussian", -1)
-  return 0;
-}
-
-
-
-static void PyBobLearnEMGaussian_delete(PyBobLearnEMGaussianObject* self) {
-  self->cxx.reset();
-  Py_TYPE(self)->tp_free((PyObject*)self);
-}
-
-static PyObject* PyBobLearnEMGaussian_RichCompare(PyBobLearnEMGaussianObject* self, PyObject* other, int op) {
-  BOB_TRY
-
-  if (!PyBobLearnEMGaussian_Check(other)) {
-    PyErr_Format(PyExc_TypeError, "cannot compare `%s' with `%s'", Py_TYPE(self)->tp_name, Py_TYPE(other)->tp_name);
-    return 0;
-  }
-  auto other_ = reinterpret_cast<PyBobLearnEMGaussianObject*>(other);
-  switch (op) {
-    case Py_EQ:
-      if (*self->cxx==*other_->cxx) Py_RETURN_TRUE; else Py_RETURN_FALSE;
-    case Py_NE:
-      if (*self->cxx==*other_->cxx) Py_RETURN_FALSE; else Py_RETURN_TRUE;
-    default:
-      Py_INCREF(Py_NotImplemented);
-      return Py_NotImplemented;
-  }
-  BOB_CATCH_MEMBER("cannot compare Gaussian objects", 0)
-}
-
-int PyBobLearnEMGaussian_Check(PyObject* o) {
-  return PyObject_IsInstance(o, reinterpret_cast<PyObject*>(&PyBobLearnEMGaussian_Type));
-}
-
-
-/******************************************************************/
-/************ Variables Section ***********************************/
-/******************************************************************/
-
-/***** MEAN *****/
-static auto mean = bob::extension::VariableDoc(
-  "mean",
-  "array_like <float, 1D>",
-  "Mean of the Gaussian",
-  ""
-);
-PyObject* PyBobLearnEMGaussian_getMean(PyBobLearnEMGaussianObject* self, void*){
-  BOB_TRY
-  return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getMean());
-  BOB_CATCH_MEMBER("mean could not be read", 0)
-}
-int PyBobLearnEMGaussian_setMean(PyBobLearnEMGaussianObject* self, PyObject* value, void*){
-  BOB_TRY
-  PyBlitzArrayObject* input;
-  if (!PyBlitzArray_Converter(value, &input)){
-    PyErr_Format(PyExc_RuntimeError, "%s %s expects a 1D array of floats", Py_TYPE(self)->tp_name, mean.name());
-    return -1;
-  }
-
-  // perform check on the input
-  if (input->type_num != NPY_FLOAT64){
-    PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, mean.name());
-    return -1;
-  }
-
-  if (input->ndim != 1){
-    PyErr_Format(PyExc_TypeError, "`%s' only processes 1D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, mean.name());
-    return -1;
-  }
-
-  if (input->shape[0] != (Py_ssize_t)self->cxx->getNInputs()){
-    PyErr_Format(PyExc_TypeError, "`%s' 1D `input` array should have %" PY_FORMAT_SIZE_T "d elements, not %" PY_FORMAT_SIZE_T "d for `%s`", Py_TYPE(self)->tp_name, self->cxx->getNInputs(), input->shape[0], mean.name());
-    return -1;
-  }
-
-  auto o_ = make_safe(input);
-  auto b = PyBlitzArrayCxx_AsBlitz<double,1>(input, "mean");
-  if (!b) return -1;
-  self->cxx->setMean(*b);
-  return 0;
-  BOB_CATCH_MEMBER("mean could not be set", -1)
-}
-
-/***** Variance *****/
-static auto variance = bob::extension::VariableDoc(
-  "variance",
-  "array_like <float, 1D>",
-  "Variance of the Gaussian",
-  ""
-);
-PyObject* PyBobLearnEMGaussian_getVariance(PyBobLearnEMGaussianObject* self, void*){
-  BOB_TRY
-  return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getVariance());
-  BOB_CATCH_MEMBER("variance could not be read", 0)
-}
-int PyBobLearnEMGaussian_setVariance(PyBobLearnEMGaussianObject* self, PyObject* value, void*){
-  BOB_TRY
-  PyBlitzArrayObject* input;
-  if (!PyBlitzArray_Converter(value, &input)){
-    PyErr_Format(PyExc_RuntimeError, "%s %s expects a 2D array of floats", Py_TYPE(self)->tp_name, variance.name());
-    return -1;
-  }
-  auto input_ = make_safe(input);
-
-  // perform check on the input
-  if (input->type_num != NPY_FLOAT64){
-    PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, variance.name());
-    return -1;
-  }
-
-  if (input->ndim != 1){
-    PyErr_Format(PyExc_TypeError, "`%s' only processes 1D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, variance.name());
-    return -1;
-  }
-
-  if (input->shape[0] != (Py_ssize_t)self->cxx->getNInputs()){
-    PyErr_Format(PyExc_TypeError, "`%s' 1D `input` array should have %" PY_FORMAT_SIZE_T "d elements, not %" PY_FORMAT_SIZE_T "d for `%s`", Py_TYPE(self)->tp_name, self->cxx->getNInputs(), input->shape[0], variance.name());
-    return -1;
-  }
-
-  auto b = PyBlitzArrayCxx_AsBlitz<double,1>(input, "variance");
-  if (!b) return -1;
-  self->cxx->setVariance(*b);
-  return 0;
-  BOB_CATCH_MEMBER("variance could not be set", -1)
-}
-
-
-/***** variance_thresholds *****/
-static auto variance_thresholds = bob::extension::VariableDoc(
-  "variance_thresholds",
-  "array_like <float, 1D>",
-  "The variance flooring thresholds, i.e. the minimum allowed value of variance in each dimension. ",
-  "The variance will be set to this value if an attempt is made to set it to a smaller value."
-);
-PyObject* PyBobLearnEMGaussian_getVarianceThresholds(PyBobLearnEMGaussianObject* self, void*){
-  BOB_TRY
-  return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getVarianceThresholds());
-  BOB_CATCH_MEMBER("variance_thresholds could not be read", 0)
-}
-int PyBobLearnEMGaussian_setVarianceThresholds(PyBobLearnEMGaussianObject* self, PyObject* value, void*){
-  BOB_TRY
-  PyBlitzArrayObject* input;
-  if (!PyBlitzArray_Converter(value, &input)){
-    PyErr_Format(PyExc_RuntimeError, "%s %s expects a 1D array of floats", Py_TYPE(self)->tp_name, variance_thresholds.name());
-    return -1;
-  }
-
-  auto input_ = make_safe(input);
-
-  // perform check on the input
-  if (input->type_num != NPY_FLOAT64){
-    PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, variance_thresholds.name());
-    return -1;
-  }
-
-  if (input->ndim != 1){
-    PyErr_Format(PyExc_TypeError, "`%s' only processes 1D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, variance_thresholds.name());
-    return -1;
-  }
-
-  if (input->shape[0] != (Py_ssize_t)self->cxx->getNInputs()){
-    PyErr_Format(PyExc_TypeError, "`%s' 1D `input` array should have %" PY_FORMAT_SIZE_T "d elements, not %" PY_FORMAT_SIZE_T "d for `%s`", Py_TYPE(self)->tp_name, self->cxx->getNInputs(), input->shape[0], variance_thresholds.name());
-    return -1;
-  }
-
-  auto b = PyBlitzArrayCxx_AsBlitz<double,1>(input, "variance_thresholds");
-  if (!b) return -1;
-  self->cxx->setVarianceThresholds(*b);
-  return 0;
-  BOB_CATCH_MEMBER("variance_thresholds could not be set", -1)
-}
-
-
-/***** shape *****/
-static auto shape = bob::extension::VariableDoc(
-  "shape",
-  "(int)",
-  "A tuple that represents the dimensionality of the Gaussian ``(dim,)``.",
-  ""
-);
-PyObject* PyBobLearnEMGaussian_getShape(PyBobLearnEMGaussianObject* self, void*) {
-  BOB_TRY
-  return Py_BuildValue("(i)", self->cxx->getNInputs());
-  BOB_CATCH_MEMBER("shape could not be read", 0)
-}
-
-static PyGetSetDef PyBobLearnEMGaussian_getseters[] = {
-    {
-      mean.name(),
-      (getter)PyBobLearnEMGaussian_getMean,
-      (setter)PyBobLearnEMGaussian_setMean,
-      mean.doc(),
-      0
-    },
-    {
-      variance.name(),
-      (getter)PyBobLearnEMGaussian_getVariance,
-      (setter)PyBobLearnEMGaussian_setVariance,
-      variance.doc(),
-     0
-     },
-     {
-      variance_thresholds.name(),
-      (getter)PyBobLearnEMGaussian_getVarianceThresholds,
-      (setter)PyBobLearnEMGaussian_setVarianceThresholds,
-      variance_thresholds.doc(),
-      0
-     },
-     {
-      shape.name(),
-      (getter)PyBobLearnEMGaussian_getShape,
-      0,
-      shape.doc(),
-      0
-     },
-
-    {0}  // Sentinel
-};
-
-
-/******************************************************************/
-/************ Functions Section ***********************************/
-/******************************************************************/
-
-/*** resize ***/
-static auto resize = bob::extension::FunctionDoc(
-  "resize",
-  "Set the input dimensionality, reset the mean to zero and the variance to one."
-)
-.add_prototype("input")
-.add_parameter("input", "int", "Dimensionality of the feature vector");
-static PyObject* PyBobLearnEMGaussian_resize(PyBobLearnEMGaussianObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  /* Parses input arguments in a single shot */
-  char** kwlist = resize.kwlist(0);
-
-  int input = 0;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, &input)) return 0;
-  if (input <= 0){
-    PyErr_Format(PyExc_TypeError, "input must be greater than zero");
-    resize.print_usage();
-    return 0;
-  }
-  self->cxx->setNInputs(input);
-
-  BOB_CATCH_MEMBER("cannot perform the resize method", 0)
-
-  Py_RETURN_NONE;
-}
-
-/*** log_likelihood ***/
-static auto log_likelihood = bob::extension::FunctionDoc(
-  "log_likelihood",
-  "Output the log likelihood of the sample, x. The input size is checked.",
-  ".. note:: The ``__call__`` function is an alias for this.",
-  true
-)
-.add_prototype("input","output")
-.add_parameter("input", "array_like <float, 1D>", "Input vector")
-.add_return("output","float","The log likelihood");
-static PyObject* PyBobLearnEMGaussian_loglikelihood(PyBobLearnEMGaussianObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  char** kwlist = log_likelihood.kwlist(0);
-
-  PyBlitzArrayObject* input = 0;
-
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, &PyBlitzArray_Converter, &input)) return 0;
-  //protects acquired resources through this scope
-  auto input_ = make_safe(input);
-
-  // perform check on the input
-  if (input->type_num != NPY_FLOAT64){
-    PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `input`", Py_TYPE(self)->tp_name);
-    log_likelihood.print_usage();
-    return 0;
-  }
-
-  if (input->ndim != 1){
-    PyErr_Format(PyExc_TypeError, "`%s' only processes 1D arrays of float64", Py_TYPE(self)->tp_name);
-    log_likelihood.print_usage();
-    return 0;
-  }
-
-  if (input->shape[0] != (Py_ssize_t)self->cxx->getNInputs()){
-    PyErr_Format(PyExc_TypeError, "`%s' 1D `input` array should have %" PY_FORMAT_SIZE_T "d elements, not %" PY_FORMAT_SIZE_T "d", Py_TYPE(self)->tp_name, self->cxx->getNInputs(), input->shape[0]);
-    log_likelihood.print_usage();
-    return 0;
-  }
-
-  double value = self->cxx->logLikelihood(*PyBlitzArrayCxx_AsBlitz<double,1>(input));
-  return Py_BuildValue("d", value);
-
-  BOB_CATCH_MEMBER("cannot compute the likelihood", 0)
-}
-
-
-/*** log_likelihood_ ***/
-static auto log_likelihood_ = bob::extension::FunctionDoc(
-  "log_likelihood_",
-  "Output the log likelihood given a sample. The input size is NOT checked."
-)
-.add_prototype("input","output")
-.add_parameter("input", "array_like <float, 1D>", "Input vector")
-.add_return("output","float","The log likelihood");
-static PyObject* PyBobLearnEMGaussian_loglikelihood_(PyBobLearnEMGaussianObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-  char** kwlist = log_likelihood_.kwlist(0);
-
-  PyBlitzArrayObject* input = 0;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, &PyBlitzArray_Converter, &input)) return 0;
-  //protects acquired resources through this scope
-  auto input_ = make_safe(input);
-
-  // perform check on the input
-  if (input->type_num != NPY_FLOAT64){
-    PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `input`", Py_TYPE(self)->tp_name);
-    log_likelihood.print_usage();
-    return 0;
-  }
-
-  if (input->ndim != 1){
-    PyErr_Format(PyExc_TypeError, "`%s' only processes 1D arrays of float64", Py_TYPE(self)->tp_name);
-    log_likelihood.print_usage();
-    return 0;
-  }
-
-  if (input->shape[0] != (Py_ssize_t)self->cxx->getNInputs()){
-    PyErr_Format(PyExc_TypeError, "`%s' 1D `input` array should have %" PY_FORMAT_SIZE_T "d elements, not %" PY_FORMAT_SIZE_T "d", Py_TYPE(self)->tp_name, self->cxx->getNInputs(), input->shape[0]);
-    log_likelihood.print_usage();
-    return 0;
-  }
-
-  double value = self->cxx->logLikelihood_(*PyBlitzArrayCxx_AsBlitz<double,1>(input));
-  return Py_BuildValue("d", value);
-
-  BOB_CATCH_MEMBER("cannot compute the likelihood", 0)
-}
-
-
-/*** save ***/
-static auto save = bob::extension::FunctionDoc(
-  "save",
-  "Save the configuration of the Gassian Machine to a given HDF5 file"
-)
-.add_prototype("hdf5")
-.add_parameter("hdf5", ":py:class:`bob.io.base.HDF5File`", "An HDF5 file open for writing");
-static PyObject* PyBobLearnEMGaussian_Save(PyBobLearnEMGaussianObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  // get list of arguments
-  char** kwlist = save.kwlist(0);
-  PyBobIoHDF5FileObject* hdf5;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, PyBobIoHDF5File_Converter, &hdf5)) return 0;
-
-  auto hdf5_ = make_safe(hdf5);
-
-  self->cxx->save(*hdf5->f);
-
-  BOB_CATCH_MEMBER("cannot save the data", 0)
-  Py_RETURN_NONE;
-}
-
-/*** load ***/
-static auto load = bob::extension::FunctionDoc(
-  "load",
-  "Load the configuration of the Gassian Machine to a given HDF5 file"
-)
-.add_prototype("hdf5")
-.add_parameter("hdf5", ":py:class:`bob.io.base.HDF5File`", "An HDF5 file open for reading");
-static PyObject* PyBobLearnEMGaussian_Load(PyBobLearnEMGaussianObject* self,  PyObject* args, PyObject* kwargs) {
-
-  BOB_TRY
-
-  char** kwlist = load.kwlist(0);
-  PyBobIoHDF5FileObject* hdf5;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, PyBobIoHDF5File_Converter, &hdf5)) return 0;
-
-  auto hdf5_ = make_safe(hdf5);
-  self->cxx->load(*hdf5->f);
-
-  BOB_CATCH_MEMBER("cannot load the data", 0)
-  Py_RETURN_NONE;
-}
-
-
-/*** is_similar_to ***/
-static auto is_similar_to = bob::extension::FunctionDoc(
-  "is_similar_to",
-
-  "Compares this Gaussian with the ``other`` one to be approximately the same.",
-  "The optional values ``r_epsilon`` and ``a_epsilon`` refer to the "
-  "relative and absolute precision for the ``weights``, ``biases`` and any other values internal to this machine.",
-  true
-)
-.add_prototype("other, [r_epsilon], [a_epsilon]","output")
-.add_parameter("other", ":py:class:`bob.learn.em.Gaussian`", "A gaussian to be compared.")
-.add_parameter("[r_epsilon]", "float", "Relative precision.")
-.add_parameter("[a_epsilon]", "float", "Absolute precision.")
-.add_return("output","bool","True if it is similar, otherwise false.");
-static PyObject* PyBobLearnEMGaussian_IsSimilarTo(PyBobLearnEMGaussianObject* self, PyObject* args, PyObject* kwds) {
-
-  /* Parses input arguments in a single shot */
-  char** kwlist = is_similar_to.kwlist(0);
-
-  PyBobLearnEMGaussianObject* other = 0;
-  double r_epsilon = 1.e-5;
-  double a_epsilon = 1.e-8;
-
-  if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|dd", kwlist,
-        &PyBobLearnEMGaussian_Type, &other,
-        &r_epsilon, &a_epsilon)) return 0;
-
-  if (self->cxx->is_similar_to(*other->cxx, r_epsilon, a_epsilon))
-    Py_RETURN_TRUE;
-  else
-    Py_RETURN_FALSE;
-}
-
-
-/*** set_variance_thresholds ***/
-static auto set_variance_thresholds = bob::extension::FunctionDoc(
-  "set_variance_thresholds",
-  "Set the variance flooring thresholds equal to the given threshold for all the dimensions."
-)
-.add_prototype("input")
-.add_parameter("input","float","Threshold")
-;
-static PyObject* PyBobLearnEMGaussian_SetVarianceThresholds(PyBobLearnEMGaussianObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  /* Parses input arguments in a single shot */
-  char** kwlist = set_variance_thresholds.kwlist(0);
-
-  double input = 0;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "d", kwlist, &input)) return 0;
-
-  self->cxx->setVarianceThresholds(input);
-
-  BOB_CATCH_MEMBER("cannot perform the set_variance_Thresholds method", 0)
-
-  Py_RETURN_NONE;
-}
-
-
-static PyMethodDef PyBobLearnEMGaussian_methods[] = {
-  {
-    resize.name(),
-    (PyCFunction)PyBobLearnEMGaussian_resize,
-    METH_VARARGS|METH_KEYWORDS,
-    resize.doc()
-  },
-  {
-    log_likelihood.name(),
-    (PyCFunction)PyBobLearnEMGaussian_loglikelihood,
-    METH_VARARGS|METH_KEYWORDS,
-    log_likelihood.doc()
-  },
-  {
-    log_likelihood_.name(),
-    (PyCFunction)PyBobLearnEMGaussian_loglikelihood_,
-    METH_VARARGS|METH_KEYWORDS,
-    log_likelihood_.doc()
-  },
-  {
-    save.name(),
-    (PyCFunction)PyBobLearnEMGaussian_Save,
-    METH_VARARGS|METH_KEYWORDS,
-    save.doc()
-  },
-  {
-    load.name(),
-    (PyCFunction)PyBobLearnEMGaussian_Load,
-    METH_VARARGS|METH_KEYWORDS,
-    load.doc()
-  },
-  {
-    is_similar_to.name(),
-    (PyCFunction)PyBobLearnEMGaussian_IsSimilarTo,
-    METH_VARARGS|METH_KEYWORDS,
-    is_similar_to.doc()
-  },
-  {
-    set_variance_thresholds.name(),
-    (PyCFunction)PyBobLearnEMGaussian_SetVarianceThresholds,
-    METH_VARARGS|METH_KEYWORDS,
-    set_variance_thresholds.doc()
-  },
-
-  {0} /* Sentinel */
-};
-
-
-/******************************************************************/
-/************ Module Section **************************************/
-/******************************************************************/
-
-// Define the Gaussian type struct; will be initialized later
-PyTypeObject PyBobLearnEMGaussian_Type = {
-  PyVarObject_HEAD_INIT(0,0)
-  0
-};
-
-bool init_BobLearnEMGaussian(PyObject* module)
-{
-  // initialize the type struct
-  PyBobLearnEMGaussian_Type.tp_name = Gaussian_doc.name();
-  PyBobLearnEMGaussian_Type.tp_basicsize = sizeof(PyBobLearnEMGaussianObject);
-  PyBobLearnEMGaussian_Type.tp_flags = Py_TPFLAGS_DEFAULT;
-  PyBobLearnEMGaussian_Type.tp_doc = Gaussian_doc.doc();
-
-  // set the functions
-  PyBobLearnEMGaussian_Type.tp_new = PyType_GenericNew;
-  PyBobLearnEMGaussian_Type.tp_init = reinterpret_cast<initproc>(PyBobLearnEMGaussian_init);
-  PyBobLearnEMGaussian_Type.tp_dealloc = reinterpret_cast<destructor>(PyBobLearnEMGaussian_delete);
-  PyBobLearnEMGaussian_Type.tp_richcompare = reinterpret_cast<richcmpfunc>(PyBobLearnEMGaussian_RichCompare);
-  PyBobLearnEMGaussian_Type.tp_methods = PyBobLearnEMGaussian_methods;
-  PyBobLearnEMGaussian_Type.tp_getset = PyBobLearnEMGaussian_getseters;
-  PyBobLearnEMGaussian_Type.tp_call = reinterpret_cast<ternaryfunc>(PyBobLearnEMGaussian_loglikelihood);
-
-  // check that everything is fine
-  if (PyType_Ready(&PyBobLearnEMGaussian_Type) < 0) return false;
-
-  // add the type to the module
-  Py_INCREF(&PyBobLearnEMGaussian_Type);
-  return PyModule_AddObject(module, "Gaussian", (PyObject*)&PyBobLearnEMGaussian_Type) >= 0;
-}
-
diff --git a/bob/learn/em/gmm_machine.cpp b/bob/learn/em/gmm_machine.cpp
deleted file mode 100644
index 429c3097d9471ddfbf9d26b043d23e3407062aaf..0000000000000000000000000000000000000000
--- a/bob/learn/em/gmm_machine.cpp
+++ /dev/null
@@ -1,988 +0,0 @@
-/**
- * @author Tiago de Freitas Pereira <tiago.pereira@idiap.ch>
- * @date Wed 11 Dec 18:01:00 2014
- *
- * @brief Python API for bob::learn::em
- *
- * Copyright (C) 2011-2014 Idiap Research Institute, Martigny, Switzerland
- */
-
-#include "main.h"
-
-/******************************************************************/
-/************ Constructor Section *********************************/
-/******************************************************************/
-
-static auto GMMMachine_doc = bob::extension::ClassDoc(
-  BOB_EXT_MODULE_PREFIX ".GMMMachine",
-  "This class implements the statistical model for multivariate diagonal mixture Gaussian distribution (GMM). "
-  "A GMM is defined as :math:`\\sum_{c=0}^{C} \\omega_c \\mathcal{N}(x | \\mu_c, \\sigma_c)`, where :math:`C` is the number of Gaussian components :math:`\\mu_c`, :math:`\\sigma_c` and :math:`\\omega_c` are respectively the the mean, variance and the weight of each gaussian component :math:`c`.",
-  "See Section 2.3.9 of Bishop, \"Pattern recognition and machine learning\", 2006"
-).add_constructor(
-  bob::extension::FunctionDoc(
-    "__init__",
-    "Creates a GMMMachine",
-    "",
-    true
-  )
-  .add_prototype("n_gaussians,n_inputs","")
-  .add_prototype("other","")
-  .add_prototype("hdf5","")
-  .add_prototype("","")
-
-  .add_parameter("n_gaussians", "int", "Number of gaussians")
-  .add_parameter("n_inputs", "int", "Dimension of the feature vector")
-  .add_parameter("other", ":py:class:`bob.learn.em.GMMMachine`", "A GMMMachine object to be copied.")
-  .add_parameter("hdf5", ":py:class:`bob.io.base.HDF5File`", "An HDF5 file open for reading")
-
-);
-
-
-static int PyBobLearnEMGMMMachine_init_number(PyBobLearnEMGMMMachineObject* self, PyObject* args, PyObject* kwargs) {
-
-  char** kwlist = GMMMachine_doc.kwlist(0);
-  int n_inputs    = 1;
-  int n_gaussians = 1;
-  //Parsing the input argments
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ii", kwlist, &n_gaussians, &n_inputs))
-    return -1;
-
-  if(n_gaussians < 0){
-    PyErr_Format(PyExc_TypeError, "gaussians argument must be greater than or equal to zero");
-    return -1;
-  }
-
-  if(n_inputs < 0){
-    PyErr_Format(PyExc_TypeError, "input argument must be greater than or equal to zero");
-    return -1;
-   }
-
-  self->cxx.reset(new bob::learn::em::GMMMachine(n_gaussians, n_inputs));
-  return 0;
-}
-
-
-static int PyBobLearnEMGMMMachine_init_copy(PyBobLearnEMGMMMachineObject* self, PyObject* args, PyObject* kwargs) {
-
-  char** kwlist = GMMMachine_doc.kwlist(1);
-  PyBobLearnEMGMMMachineObject* tt;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", kwlist, &PyBobLearnEMGMMMachine_Type, &tt)){
-    GMMMachine_doc.print_usage();
-    return -1;
-  }
-
-  self->cxx.reset(new bob::learn::em::GMMMachine(*tt->cxx));
-  return 0;
-}
-
-
-static int PyBobLearnEMGMMMachine_init_hdf5(PyBobLearnEMGMMMachineObject* self, PyObject* args, PyObject* kwargs) {
-
-  char** kwlist = GMMMachine_doc.kwlist(2);
-
-  PyBobIoHDF5FileObject* config = 0;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, &PyBobIoHDF5File_Converter, &config)){
-    GMMMachine_doc.print_usage();
-    return -1;
-  }
-  auto config_ = make_safe(config);
-
-  self->cxx.reset(new bob::learn::em::GMMMachine(*(config->f)));
-
-  return 0;
-}
-
-
-
-static int PyBobLearnEMGMMMachine_init(PyBobLearnEMGMMMachineObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  // get the number of command line arguments
-  int nargs = (args?PyTuple_Size(args):0) + (kwargs?PyDict_Size(kwargs):0);
-
-  switch (nargs) {
-
-    case 0: //default initializer ()
-      self->cxx.reset(new bob::learn::em::GMMMachine());
-      return 0;
-
-    case 1:{
-      //Reading the input argument
-      PyObject* arg = 0;
-      if (PyTuple_Size(args))
-        arg = PyTuple_GET_ITEM(args, 0);
-      else {
-        PyObject* tmp = PyDict_Values(kwargs);
-        auto tmp_ = make_safe(tmp);
-        arg = PyList_GET_ITEM(tmp, 0);
-      }
-
-      // If the constructor input is Gaussian object
-     if (PyBobLearnEMGMMMachine_Check(arg))
-       return PyBobLearnEMGMMMachine_init_copy(self, args, kwargs);
-      // If the constructor input is a HDF5
-     else if (PyBobIoHDF5File_Check(arg))
-       return PyBobLearnEMGMMMachine_init_hdf5(self, args, kwargs);
-    }
-    case 2:
-      return PyBobLearnEMGMMMachine_init_number(self, args, kwargs);
-    default:
-      PyErr_Format(PyExc_RuntimeError, "number of arguments mismatch - %s requires 0, 1 or 2 arguments, but you provided %d (see help)", Py_TYPE(self)->tp_name, nargs);
-      GMMMachine_doc.print_usage();
-      return -1;
-  }
-  BOB_CATCH_MEMBER("cannot create GMMMachine", -1)
-  return 0;
-}
-
-
-
-static void PyBobLearnEMGMMMachine_delete(PyBobLearnEMGMMMachineObject* self) {
-  self->cxx.reset();
-  Py_TYPE(self)->tp_free((PyObject*)self);
-}
-
-static PyObject* PyBobLearnEMGMMMachine_RichCompare(PyBobLearnEMGMMMachineObject* self, PyObject* other, int op) {
-  BOB_TRY
-
-  if (!PyBobLearnEMGMMMachine_Check(other)) {
-    PyErr_Format(PyExc_TypeError, "cannot compare `%s' with `%s'", Py_TYPE(self)->tp_name, Py_TYPE(other)->tp_name);
-    return 0;
-  }
-  auto other_ = reinterpret_cast<PyBobLearnEMGMMMachineObject*>(other);
-  switch (op) {
-    case Py_EQ:
-      if (*self->cxx==*other_->cxx) Py_RETURN_TRUE; else Py_RETURN_FALSE;
-    case Py_NE:
-      if (*self->cxx==*other_->cxx) Py_RETURN_FALSE; else Py_RETURN_TRUE;
-    default:
-      Py_INCREF(Py_NotImplemented);
-      return Py_NotImplemented;
-  }
-  BOB_CATCH_MEMBER("cannot compare GMMMachine objects", 0)
-}
-
-int PyBobLearnEMGMMMachine_Check(PyObject* o) {
-  return PyObject_IsInstance(o, reinterpret_cast<PyObject*>(&PyBobLearnEMGMMMachine_Type));
-}
-
-
-/******************************************************************/
-/************ Variables Section ***********************************/
-/******************************************************************/
-
-/***** shape *****/
-static auto shape = bob::extension::VariableDoc(
-  "shape",
-  "(int,int)",
-  "A tuple that represents the number of gaussians and dimensionality of each Gaussian ``(n_gaussians, dim)``.",
-  ""
-);
-PyObject* PyBobLearnEMGMMMachine_getShape(PyBobLearnEMGMMMachineObject* self, void*) {
-  BOB_TRY
-  return Py_BuildValue("(i,i)", self->cxx->getNGaussians(), self->cxx->getNInputs());
-  BOB_CATCH_MEMBER("shape could not be read", 0)
-}
-
-/***** MEAN *****/
-
-static auto means = bob::extension::VariableDoc(
-  "means",
-  "array_like <float, 2D>",
-  "The means of the gaussians",
-  ""
-);
-PyObject* PyBobLearnEMGMMMachine_getMeans(PyBobLearnEMGMMMachineObject* self, void*){
-  BOB_TRY
-  return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getMeans());
-  BOB_CATCH_MEMBER("means could not be read", 0)
-}
-int PyBobLearnEMGMMMachine_setMeans(PyBobLearnEMGMMMachineObject* self, PyObject* value, void*){
-  BOB_TRY
-  PyBlitzArrayObject* input;
-  if (!PyBlitzArray_Converter(value, &input)){
-    PyErr_Format(PyExc_RuntimeError, "%s %s expects a 2D array of floats", Py_TYPE(self)->tp_name, means.name());
-    return -1;
-  }
-  auto input_ = make_safe(input);
-
-  // perform check on the input
-  if (input->type_num != NPY_FLOAT64){
-    PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, means.name());
-    return -1;
-  }
-
-  if (input->ndim != 2){
-    PyErr_Format(PyExc_TypeError, "`%s' only processes 2D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, means.name());
-    return -1;
-  }
-
-  if (input->shape[1] != (Py_ssize_t)self->cxx->getNInputs() && input->shape[0] != (Py_ssize_t)self->cxx->getNGaussians()) {
-    PyErr_Format(PyExc_TypeError, "`%s' 2D `input` array should have the shape [%" PY_FORMAT_SIZE_T "d, %" PY_FORMAT_SIZE_T "d] not [%" PY_FORMAT_SIZE_T "d, %" PY_FORMAT_SIZE_T "d] for `%s`", Py_TYPE(self)->tp_name, self->cxx->getNGaussians(), self->cxx->getNInputs(), input->shape[1], input->shape[0], means.name());
-    return -1;
-  }
-
-  auto b = PyBlitzArrayCxx_AsBlitz<double,2>(input, "means");
-  if (!b) return -1;
-  self->cxx->setMeans(*b);
-  return 0;
-  BOB_CATCH_MEMBER("means could not be set", -1)
-}
-
-/***** Variance *****/
-static auto variances = bob::extension::VariableDoc(
-  "variances",
-  "array_like <float, 2D>",
-  "Variances of the gaussians",
-  ""
-);
-PyObject* PyBobLearnEMGMMMachine_getVariances(PyBobLearnEMGMMMachineObject* self, void*){
-  BOB_TRY
-  return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getVariances());
-  BOB_CATCH_MEMBER("variances could not be read", 0)
-}
-int PyBobLearnEMGMMMachine_setVariances(PyBobLearnEMGMMMachineObject* self, PyObject* value, void*){
-  BOB_TRY
-  PyBlitzArrayObject* input;
-  if (!PyBlitzArray_Converter(value, &input)){
-    PyErr_Format(PyExc_RuntimeError, "%s %s expects a 2D array of floats", Py_TYPE(self)->tp_name, variances.name());
-    return -1;
-  }
-  auto input_ = make_safe(input);
-
-  // perform check on the input
-  if (input->type_num != NPY_FLOAT64){
-    PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, variances.name());
-    return -1;
-  }
-
-  if (input->ndim != 2){
-    PyErr_Format(PyExc_TypeError, "`%s' only processes 2D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, variances.name());
-    return -1;
-  }
-
-  if (input->shape[1] != (Py_ssize_t)self->cxx->getNInputs() && input->shape[0] != (Py_ssize_t)self->cxx->getNGaussians()) {
-    PyErr_Format(PyExc_TypeError, "`%s' 2D `input` array should have the shape [%" PY_FORMAT_SIZE_T "d, %" PY_FORMAT_SIZE_T "d] not [%" PY_FORMAT_SIZE_T "d, %" PY_FORMAT_SIZE_T "d] for `%s`", Py_TYPE(self)->tp_name, self->cxx->getNGaussians(), self->cxx->getNInputs(), input->shape[1], input->shape[0], variances.name());
-    return -1;
-  }
-
-  auto b = PyBlitzArrayCxx_AsBlitz<double,2>(input, "variances");
-  if (!b) return -1;
-  self->cxx->setVariances(*b);
-  return 0;
-  BOB_CATCH_MEMBER("variances could not be set", -1)
-}
-
-/***** Weights *****/
-static auto weights = bob::extension::VariableDoc(
-  "weights",
-  "array_like <float, 1D>",
-  "The weights (also known as \"mixing coefficients\")",
-  ""
-);
-PyObject* PyBobLearnEMGMMMachine_getWeights(PyBobLearnEMGMMMachineObject* self, void*){
-  BOB_TRY
-  return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getWeights());
-  BOB_CATCH_MEMBER("weights could not be read", 0)
-}
-int PyBobLearnEMGMMMachine_setWeights(PyBobLearnEMGMMMachineObject* self, PyObject* value, void*){
-  BOB_TRY
-  PyBlitzArrayObject* input;
-  if (!PyBlitzArray_Converter(value, &input)){
-    PyErr_Format(PyExc_RuntimeError, "%s %s expects a 1D array of floats", Py_TYPE(self)->tp_name, weights.name());
-    return -1;
-  }
-  auto o_ = make_safe(input);
-
-  // perform check on the input
-  if (input->type_num != NPY_FLOAT64){
-    PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, weights.name());
-    return -1;
-  }
-
-  if (input->ndim != 1){
-    PyErr_Format(PyExc_TypeError, "`%s' only processes 1D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, weights.name());
-    return -1;
-  }
-
-  if (input->shape[0] != (Py_ssize_t)self->cxx->getNGaussians()){
-    PyErr_Format(PyExc_TypeError, "`%s' 1D `input` array should have %" PY_FORMAT_SIZE_T "d elements, not %" PY_FORMAT_SIZE_T "d for `%s`", Py_TYPE(self)->tp_name, self->cxx->getNGaussians(), input->shape[0], weights.name());
-    return -1;
-  }
-
-  auto b = PyBlitzArrayCxx_AsBlitz<double,1>(input, "weights");
-  if (!b) return -1;
-  self->cxx->setWeights(*b);
-  return 0;
-  BOB_CATCH_MEMBER("weights could not be set", -1)
-}
-
-
-/***** variance_supervector *****/
-static auto variance_supervector = bob::extension::VariableDoc(
-  "variance_supervector",
-  "array_like <float, 1D>",
-  "The variance supervector of the GMMMachine",
-  "Concatenation of the variance vectors of each Gaussian of the GMMMachine"
-);
-PyObject* PyBobLearnEMGMMMachine_getVarianceSupervector(PyBobLearnEMGMMMachineObject* self, void*){
-  BOB_TRY
-  return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getVarianceSupervector());
-  BOB_CATCH_MEMBER("variance_supervector could not be read", 0)
-}
-int PyBobLearnEMGMMMachine_setVarianceSupervector(PyBobLearnEMGMMMachineObject* self, PyObject* value, void*){
-  BOB_TRY
-  PyBlitzArrayObject* input;
-  if (!PyBlitzArray_Converter(value, &input)){
-    PyErr_Format(PyExc_RuntimeError, "%s %s expects a 1D array of floats", Py_TYPE(self)->tp_name, variance_supervector.name());
-    return -1;
-  }
-  auto o_ = make_safe(input);
-
-  // perform check on the input
-  if (input->type_num != NPY_FLOAT64){
-    PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, variance_supervector.name());
-    return -1;
-  }
-
-  if (input->ndim != 1){
-    PyErr_Format(PyExc_TypeError, "`%s' only processes 1D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, variance_supervector.name());
-    return -1;
-  }
-
-  if (input->shape[0] != (Py_ssize_t)self->cxx->getNGaussians()*(Py_ssize_t)self->cxx->getNInputs()){
-    PyErr_Format(PyExc_TypeError, "`%s' 1D `input` array should have %" PY_FORMAT_SIZE_T "d elements, not %" PY_FORMAT_SIZE_T "d for `%s`", Py_TYPE(self)->tp_name, self->cxx->getNGaussians()*(Py_ssize_t)self->cxx->getNInputs(), input->shape[0], variance_supervector.name());
-    return -1;
-  }
-
-  auto b = PyBlitzArrayCxx_AsBlitz<double,1>(input, "variance_supervector");
-  if (!b) return -1;
-  self->cxx->setVarianceSupervector(*b);
-  return 0;
-  BOB_CATCH_MEMBER("variance_supervector could not be set", -1)
-}
-
-/***** mean_supervector *****/
-static auto mean_supervector = bob::extension::VariableDoc(
-  "mean_supervector",
-  "array_like <float, 1D>",
-  "The mean supervector of the GMMMachine",
-  "Concatenation of the mean vectors of each Gaussian of the GMMMachine"
-);
-PyObject* PyBobLearnEMGMMMachine_getMeanSupervector(PyBobLearnEMGMMMachineObject* self, void*){
-  BOB_TRY
-  return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getMeanSupervector());
-  BOB_CATCH_MEMBER("mean_supervector could not be read", 0)
-}
-int PyBobLearnEMGMMMachine_setMeanSupervector(PyBobLearnEMGMMMachineObject* self, PyObject* value, void*){
-  BOB_TRY
-  PyBlitzArrayObject* input;
-  if (!PyBlitzArray_Converter(value, &input)){
-    PyErr_Format(PyExc_RuntimeError, "%s %s expects a 1D array of floats", Py_TYPE(self)->tp_name, mean_supervector.name());
-    return -1;
-  }
-  auto o_ = make_safe(input);
-
-  // perform check on the input
-  if (input->type_num != NPY_FLOAT64){
-    PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, mean_supervector.name());
-    return -1;
-  }
-
-  if (input->ndim != 1){
-    PyErr_Format(PyExc_TypeError, "`%s' only processes 1D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, mean_supervector.name());
-    return -1;
-  }
-
-  if (input->shape[0] != (Py_ssize_t)self->cxx->getNGaussians()*(Py_ssize_t)self->cxx->getNInputs()){
-    PyErr_Format(PyExc_TypeError, "`%s' 1D `input` array should have %" PY_FORMAT_SIZE_T "d elements, not %" PY_FORMAT_SIZE_T "d for `%s`", Py_TYPE(self)->tp_name, self->cxx->getNGaussians()*(Py_ssize_t)self->cxx->getNInputs(), input->shape[0], mean_supervector.name());
-    return -1;
-  }
-
-  auto b = PyBlitzArrayCxx_AsBlitz<double,1>(input, "mean_supervector");
-  if (!b) return -1;
-  self->cxx->setMeanSupervector(*b);
-  return 0;
-  BOB_CATCH_MEMBER("mean_supervector could not be set", -1)
-}
-
-
-
-/***** variance_thresholds *****/
-static auto variance_thresholds = bob::extension::VariableDoc(
-  "variance_thresholds",
-  "array_like <float, 2D>",
-  "Set the variance flooring thresholds in each dimension to the same vector for all Gaussian components if the argument is a 1D numpy arrray, and equal for all Gaussian components and dimensions if the parameter is a scalar. ",
-  ""
-);
-PyObject* PyBobLearnEMGMMMachine_getVarianceThresholds(PyBobLearnEMGMMMachineObject* self, void*){
-  BOB_TRY
-  return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getVarianceThresholds());
-  BOB_CATCH_MEMBER("variance_thresholds could not be read", 0)
-}
-int PyBobLearnEMGMMMachine_setVarianceThresholds(PyBobLearnEMGMMMachineObject* self, PyObject* value, void*){
-  BOB_TRY
-  PyBlitzArrayObject* input;
-  if (!PyBlitzArray_Converter(value, &input)){
-    PyErr_Format(PyExc_RuntimeError, "%s %s expects a 2D array of floats", Py_TYPE(self)->tp_name, variance_thresholds.name());
-    return -1;
-  }
-  auto o_ = make_safe(input);
-
-  // perform check on the input
-  if (input->type_num != NPY_FLOAT64){
-    PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, variance_thresholds.name());
-    return -1;
-  }
-
-  if (input->ndim != 2){
-    PyErr_Format(PyExc_TypeError, "`%s' only processes 2D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, variance_thresholds.name());
-    return -1;
-  }
-
-  if (input->shape[1] != (Py_ssize_t)self->cxx->getNInputs() && input->shape[0] != (Py_ssize_t)self->cxx->getNGaussians()) {
-    PyErr_Format(PyExc_TypeError, "`%s' 2D `input` array should have the shape [%" PY_FORMAT_SIZE_T "d, %" PY_FORMAT_SIZE_T "d] not [%" PY_FORMAT_SIZE_T "d, %" PY_FORMAT_SIZE_T "d] for `%s`", Py_TYPE(self)->tp_name, self->cxx->getNGaussians(), self->cxx->getNInputs(), input->shape[1], input->shape[0], variance_thresholds.name());
-    return -1;
-  }
-
-  auto b = PyBlitzArrayCxx_AsBlitz<double,2>(input, "variance_thresholds");
-  if (!b) return -1;
-  self->cxx->setVarianceThresholds(*b);
-  return 0;
-  BOB_CATCH_MEMBER("variance_thresholds could not be set", -1)
-}
-
-
-
-
-static PyGetSetDef PyBobLearnEMGMMMachine_getseters[] = {
-  {
-   shape.name(),
-   (getter)PyBobLearnEMGMMMachine_getShape,
-   0,
-   shape.doc(),
-   0
-  },
-  {
-   means.name(),
-   (getter)PyBobLearnEMGMMMachine_getMeans,
-   (setter)PyBobLearnEMGMMMachine_setMeans,
-   means.doc(),
-   0
-  },
-  {
-   variances.name(),
-   (getter)PyBobLearnEMGMMMachine_getVariances,
-   (setter)PyBobLearnEMGMMMachine_setVariances,
-   variances.doc(),
-   0
-  },
-  {
-   weights.name(),
-   (getter)PyBobLearnEMGMMMachine_getWeights,
-   (setter)PyBobLearnEMGMMMachine_setWeights,
-   weights.doc(),
-   0
-  },
-  {
-   variance_thresholds.name(),
-   (getter)PyBobLearnEMGMMMachine_getVarianceThresholds,
-   (setter)PyBobLearnEMGMMMachine_setVarianceThresholds,
-   variance_thresholds.doc(),
-   0
-  },
-  {
-   variance_supervector.name(),
-   (getter)PyBobLearnEMGMMMachine_getVarianceSupervector,
-   (setter)PyBobLearnEMGMMMachine_setVarianceSupervector,
-   variance_supervector.doc(),
-   0
-  },
-
-  {
-   mean_supervector.name(),
-   (getter)PyBobLearnEMGMMMachine_getMeanSupervector,
-   (setter)PyBobLearnEMGMMMachine_setMeanSupervector,
-   mean_supervector.doc(),
-   0
-  },
-
-  {0}  // Sentinel
-};
-
-
-/******************************************************************/
-/************ Functions Section ***********************************/
-/******************************************************************/
-
-
-/*** save ***/
-static auto save = bob::extension::FunctionDoc(
-  "save",
-  "Save the configuration of the GMMMachine to a given HDF5 file"
-)
-.add_prototype("hdf5")
-.add_parameter("hdf5", ":py:class:`bob.io.base.HDF5File`", "An HDF5 file open for writing");
-static PyObject* PyBobLearnEMGMMMachine_Save(PyBobLearnEMGMMMachineObject* self,  PyObject* args, PyObject* kwargs) {
-
-  BOB_TRY
-
-  // get list of arguments
-  char** kwlist = save.kwlist(0);
-  PyBobIoHDF5FileObject* hdf5;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, PyBobIoHDF5File_Converter, &hdf5)) return 0;
-
-  auto hdf5_ = make_safe(hdf5);
-  self->cxx->save(*hdf5->f);
-
-  BOB_CATCH_MEMBER("cannot save the data", 0)
-  Py_RETURN_NONE;
-}
-
-/*** load ***/
-static auto load = bob::extension::FunctionDoc(
-  "load",
-  "Load the configuration of the GMMMachine to a given HDF5 file"
-)
-.add_prototype("hdf5")
-.add_parameter("hdf5", ":py:class:`bob.io.base.HDF5File`", "An HDF5 file open for reading");
-static PyObject* PyBobLearnEMGMMMachine_Load(PyBobLearnEMGMMMachineObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  char** kwlist = load.kwlist(0);
-  PyBobIoHDF5FileObject* hdf5;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, PyBobIoHDF5File_Converter, &hdf5)) return 0;
-
-  auto hdf5_ = make_safe(hdf5);
-  self->cxx->load(*hdf5->f);
-
-  BOB_CATCH_MEMBER("cannot load the data", 0)
-  Py_RETURN_NONE;
-}
-
-
-/*** is_similar_to ***/
-static auto is_similar_to = bob::extension::FunctionDoc(
-  "is_similar_to",
-
-  "Compares this GMMMachine with the ``other`` one to be approximately the same.",
-  "The optional values ``r_epsilon`` and ``a_epsilon`` refer to the "
-  "relative and absolute precision for the ``weights``, ``biases`` "
-  "and any other values internal to this machine."
-)
-.add_prototype("other, [r_epsilon], [a_epsilon]","output")
-.add_parameter("other", ":py:class:`bob.learn.em.GMMMachine`", "A GMMMachine object to be compared.")
-.add_parameter("r_epsilon", "float", "Relative precision.")
-.add_parameter("a_epsilon", "float", "Absolute precision.")
-.add_return("output","bool","True if it is similar, otherwise false.");
-static PyObject* PyBobLearnEMGMMMachine_IsSimilarTo(PyBobLearnEMGMMMachineObject* self, PyObject* args, PyObject* kwds) {
-
-  /* Parses input arguments in a single shot */
-  char** kwlist = is_similar_to.kwlist(0);
-
-  //PyObject* other = 0;
-  PyBobLearnEMGMMMachineObject* other = 0;
-  double r_epsilon = 1.e-5;
-  double a_epsilon = 1.e-8;
-
-  if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|dd", kwlist,
-        &PyBobLearnEMGMMMachine_Type, &other,
-        &r_epsilon, &a_epsilon)){
-
-        is_similar_to.print_usage();
-        return 0;
-  }
-
-  if (self->cxx->is_similar_to(*other->cxx, r_epsilon, a_epsilon))
-    Py_RETURN_TRUE;
-  else
-    Py_RETURN_FALSE;
-}
-
-
-/*** resize ***/
-static auto resize = bob::extension::FunctionDoc(
-  "resize",
-  "Allocates space for the statistics and resets to zero.",
-  0,
-  true
-)
-.add_prototype("n_gaussians,n_inputs")
-.add_parameter("n_gaussians", "int", "Number of gaussians")
-.add_parameter("n_inputs", "int", "Dimensionality of the feature vector");
-static PyObject* PyBobLearnEMGMMMachine_resize(PyBobLearnEMGMMMachineObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  /* Parses input arguments in a single shot */
-  char** kwlist = resize.kwlist(0);
-
-  int n_gaussians = 0;
-  int n_inputs = 0;
-
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ii", kwlist, &n_gaussians, &n_inputs)) Py_RETURN_NONE;
-
-  if (n_gaussians <= 0){
-    PyErr_Format(PyExc_TypeError, "n_gaussians must be greater than zero");
-    resize.print_usage();
-    return 0;
-  }
-  if (n_inputs <= 0){
-    PyErr_Format(PyExc_TypeError, "n_inputs must be greater than zero");
-    resize.print_usage();
-    return 0;
-  }
-
-  self->cxx->resize(n_gaussians, n_inputs);
-
-  BOB_CATCH_MEMBER("cannot perform the resize method", 0)
-
-  Py_RETURN_NONE;
-}
-
-
-/*** log_likelihood ***/
-static auto log_likelihood = bob::extension::FunctionDoc(
-  "log_likelihood",
-  "Output the log likelihood of the sample, x, i.e. :math:`log(p(x|GMM))`. Inputs are checked.",
-  ".. note:: The ``__call__`` function is an alias for this. \n "
-  "If `input` is 2D the average along the samples will be computed (:math:`\\frac{log(p(x|GMM))}{N}`) ",
-  true
-)
-.add_prototype("input","output")
-.add_parameter("input", "array_like <float, 1D>", "Input vector")
-.add_return("output","float","The log likelihood");
-static PyObject* PyBobLearnEMGMMMachine_loglikelihood(PyBobLearnEMGMMMachineObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  char** kwlist = log_likelihood.kwlist(0);
-
-  PyBlitzArrayObject* input = 0;
-
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, &PyBlitzArray_Converter, &input)) return 0;
-  //protects acquired resources through this scope
-  auto input_ = make_safe(input);
-
-  // perform check on the input
-  if (input->type_num != NPY_FLOAT64){
-    PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `input`", Py_TYPE(self)->tp_name);
-    log_likelihood.print_usage();
-    return 0;
-  }
-
-  if (input->ndim > 2){
-    PyErr_Format(PyExc_TypeError, "`%s' only processes 1D or 2D arrays of float64", Py_TYPE(self)->tp_name);
-    log_likelihood.print_usage();
-    return 0;
-  }
-
-  int shape_index = input->ndim - 1; //Getting the index of the dimensionality (0 for 1D arrays, 1 for 2D arrays)
-
-  if (input->shape[shape_index] != (Py_ssize_t)self->cxx->getNInputs()){
-    PyErr_Format(PyExc_TypeError, "`%s' 1D `input` array should have %" PY_FORMAT_SIZE_T "d elements, not %" PY_FORMAT_SIZE_T "d", Py_TYPE(self)->tp_name, self->cxx->getNInputs(), input->shape[0]);
-    log_likelihood.print_usage();
-    return 0;
-  }
-
-  double value = 0;
-  if (input->ndim == 1)
-    value = self->cxx->logLikelihood(*PyBlitzArrayCxx_AsBlitz<double,1>(input));
-  else
-    value = self->cxx->logLikelihood(*PyBlitzArrayCxx_AsBlitz<double,2>(input));
-
-
-  return Py_BuildValue("d", value);
-  BOB_CATCH_MEMBER("cannot compute the likelihood", 0)
-}
-
-
-/*** log_likelihood_ ***/
-static auto log_likelihood_ = bob::extension::FunctionDoc(
-  "log_likelihood_",
-  "Output the log likelihood of the sample, x, i.e. :math:`log(p(x|GMM))`. Inputs are NOT checked.",
-  "",
-  true
-)
-.add_prototype("input","output")
-.add_parameter("input", "array_like <float, 1D>", "Input vector")
-.add_return("output","float","The log likelihood");
-static PyObject* PyBobLearnEMGMMMachine_loglikelihood_(PyBobLearnEMGMMMachineObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  char** kwlist = log_likelihood_.kwlist(0);
-
-  PyBlitzArrayObject* input = 0;
-
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, &PyBlitzArray_Converter, &input)) return 0;
-  //protects acquired resources through this scope
-  auto input_ = make_safe(input);
-
-  // perform check on the input
-  if (input->type_num != NPY_FLOAT64){
-    PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `input`", Py_TYPE(self)->tp_name);
-    log_likelihood.print_usage();
-    return 0;
-  }
-
-  if (input->ndim != 1){
-    PyErr_Format(PyExc_TypeError, "`%s' only processes 1D arrays of float64", Py_TYPE(self)->tp_name);
-    log_likelihood.print_usage();
-    return 0;
-  }
-
-  if (input->shape[0] != (Py_ssize_t)self->cxx->getNInputs()){
-    PyErr_Format(PyExc_TypeError, "`%s' 1D `input` array should have %" PY_FORMAT_SIZE_T "d elements, not %" PY_FORMAT_SIZE_T "d", Py_TYPE(self)->tp_name, self->cxx->getNInputs(), input->shape[0]);
-    log_likelihood.print_usage();
-    return 0;
-  }
-
-  double value = self->cxx->logLikelihood_(*PyBlitzArrayCxx_AsBlitz<double,1>(input));
-  return Py_BuildValue("d", value);
-
-  BOB_CATCH_MEMBER("cannot compute the likelihood", 0)
-}
-
-
-/*** acc_statistics ***/
-static auto acc_statistics = bob::extension::FunctionDoc(
-  "acc_statistics",
-  "Accumulate the GMM statistics (:py:class:`bob.learn.em.GMMStats`) for this sample(s). Inputs are checked.",
-  "",
-  true
-)
-.add_prototype("input,stats")
-.add_parameter("input", "array_like <float, 2D>", "Input vector")
-.add_parameter("stats", ":py:class:`bob.learn.em.GMMStats`", "Statistics of the GMM");
-static PyObject* PyBobLearnEMGMMMachine_accStatistics(PyBobLearnEMGMMMachineObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  char** kwlist = acc_statistics.kwlist(0);
-
-  PyBlitzArrayObject* input           = 0;
-  PyBobLearnEMGMMStatsObject* stats = 0;
-
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&O!", kwlist, &PyBlitzArray_Converter,&input,
-                                                                 &PyBobLearnEMGMMStats_Type, &stats))
-    return 0;
-
-  //protects acquired resources through this scope
-  auto input_ = make_safe(input);
-
-  if (input->ndim == 1)
-    self->cxx->accStatistics(*PyBlitzArrayCxx_AsBlitz<double,1>(input), *stats->cxx);
-  else
-    self->cxx->accStatistics(*PyBlitzArrayCxx_AsBlitz<double,2>(input), *stats->cxx);
-
-
-  BOB_CATCH_MEMBER("cannot accumulate the statistics", 0)
-  Py_RETURN_NONE;
-}
-
-
-/*** acc_statistics_ ***/
-static auto acc_statistics_ = bob::extension::FunctionDoc(
-  "acc_statistics_",
-  "Accumulate the GMM statistics (:py:class:`bob.learn.em.GMMStats`) for this sample(s). Inputs are NOT checked.",
-  "",
-  true
-)
-.add_prototype("input,stats")
-.add_parameter("input", "array_like <float, 2D>", "Input vector")
-.add_parameter("stats", ":py:class:`bob.learn.em.GMMStats`", "Statistics of the GMM");
-static PyObject* PyBobLearnEMGMMMachine_accStatistics_(PyBobLearnEMGMMMachineObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  char** kwlist = acc_statistics_.kwlist(0);
-
-  PyBlitzArrayObject* input = 0;
-  PyBobLearnEMGMMStatsObject* stats = 0;
-
-  if(!PyArg_ParseTupleAndKeywords(args, kwargs, "O&O!", kwlist, &PyBlitzArray_Converter,&input,
-                                                                &PyBobLearnEMGMMStats_Type, &stats))
-    return 0;
-
-  //protects acquired resources through this scope
-  auto input_ = make_safe(input);
-
-  if (input->ndim==1)
-    self->cxx->accStatistics_(*PyBlitzArrayCxx_AsBlitz<double,1>(input), *stats->cxx);
-  else
-    self->cxx->accStatistics_(*PyBlitzArrayCxx_AsBlitz<double,2>(input), *stats->cxx);
-
-  BOB_CATCH_MEMBER("cannot accumulate the statistics", 0)
-  Py_RETURN_NONE;
-}
-
-
-
-/*** set_variance_thresholds ***/
-static auto set_variance_thresholds = bob::extension::FunctionDoc(
-  "set_variance_thresholds",
-  "Set the variance flooring thresholds in each dimension to the same vector for all Gaussian components if the argument is a 1D numpy arrray, and equal for all Gaussian components and dimensions if the parameter is a scalar.",
-  "",
-  true
-)
-.add_prototype("input")
-.add_parameter("input", "float or array_like <float, 1D>", "The new variance threshold, or a vector of thresholds for all Gaussian components");
-static PyObject* PyBobLearnEMGMMMachine_setVarianceThresholds_method(PyBobLearnEMGMMMachineObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  char** kwlist = set_variance_thresholds.kwlist(0);
-
-  PyBlitzArrayObject* input_array = 0;
-  double input_number = 0;
-  if(PyArg_ParseTupleAndKeywords(args, kwargs, "d", kwlist, &input_number)){
-    self->cxx->setVarianceThresholds(input_number);
-  }
-  else if(PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, &PyBlitzArray_Converter, &input_array)) {
-    //protects acquired resources through this scope
-    auto input_ = make_safe(input_array);
-    self->cxx->setVarianceThresholds(*PyBlitzArrayCxx_AsBlitz<double,1>(input_array));
-  }
-  else
-    return 0;
-
-  // clear any error that might have been set in the functions above
-  PyErr_Clear();
-
-  BOB_CATCH_MEMBER("cannot accumulate set the variance threshold", 0)
-  Py_RETURN_NONE;
-}
-
-
-
-
-/*** get_gaussian ***/
-static auto get_gaussian = bob::extension::FunctionDoc(
-  "get_gaussian",
-  "Get the specified Gaussian (:py:class:`bob.learn.em.Gaussian`) component.",
-  ".. note:: An exception is thrown if i is out of range.",
-  true
-)
-.add_prototype("i","gaussian")
-.add_parameter("i", "int", "Index of the gaussian")
-.add_return("gaussian",":py:class:`bob.learn.em.Gaussian`","Gaussian object");
-static PyObject* PyBobLearnEMGMMMachine_get_gaussian(PyBobLearnEMGMMMachineObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  char** kwlist = get_gaussian.kwlist(0);
-
-  int i = 0;
-
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, &i)) return 0;
-
-  //Allocating the correspondent python object
-  PyBobLearnEMGaussianObject* retval =
-    (PyBobLearnEMGaussianObject*)PyBobLearnEMGaussian_Type.tp_alloc(&PyBobLearnEMGaussian_Type, 0);
-
-  retval->cxx = self->cxx->getGaussian(i);
-
-  return Py_BuildValue("N",retval);
-
-  BOB_CATCH_MEMBER("cannot compute the likelihood", 0)
-}
-
-
-
-static PyMethodDef PyBobLearnEMGMMMachine_methods[] = {
-  {
-    save.name(),
-    (PyCFunction)PyBobLearnEMGMMMachine_Save,
-    METH_VARARGS|METH_KEYWORDS,
-    save.doc()
-  },
-  {
-    load.name(),
-    (PyCFunction)PyBobLearnEMGMMMachine_Load,
-    METH_VARARGS|METH_KEYWORDS,
-    load.doc()
-  },
-  {
-    is_similar_to.name(),
-    (PyCFunction)PyBobLearnEMGMMMachine_IsSimilarTo,
-    METH_VARARGS|METH_KEYWORDS,
-    is_similar_to.doc()
-  },
-  {
-    resize.name(),
-    (PyCFunction)PyBobLearnEMGMMMachine_resize,
-    METH_VARARGS|METH_KEYWORDS,
-    resize.doc()
-  },
-  {
-    log_likelihood.name(),
-    (PyCFunction)PyBobLearnEMGMMMachine_loglikelihood,
-    METH_VARARGS|METH_KEYWORDS,
-    log_likelihood.doc()
-  },
-  {
-    log_likelihood_.name(),
-    (PyCFunction)PyBobLearnEMGMMMachine_loglikelihood_,
-    METH_VARARGS|METH_KEYWORDS,
-    log_likelihood_.doc()
-  },
-  {
-    acc_statistics.name(),
-    (PyCFunction)PyBobLearnEMGMMMachine_accStatistics,
-    METH_VARARGS|METH_KEYWORDS,
-    acc_statistics.doc()
-  },
-  {
-    acc_statistics_.name(),
-    (PyCFunction)PyBobLearnEMGMMMachine_accStatistics_,
-    METH_VARARGS|METH_KEYWORDS,
-    acc_statistics_.doc()
-  },
-
-  {
-    get_gaussian.name(),
-    (PyCFunction)PyBobLearnEMGMMMachine_get_gaussian,
-    METH_VARARGS|METH_KEYWORDS,
-    get_gaussian.doc()
-  },
-
-  {
-    set_variance_thresholds.name(),
-    (PyCFunction)PyBobLearnEMGMMMachine_setVarianceThresholds_method,
-    METH_VARARGS|METH_KEYWORDS,
-    set_variance_thresholds.doc()
-  },
-
-  {0} /* Sentinel */
-};
-
-
-/******************************************************************/
-/************ Module Section **************************************/
-/******************************************************************/
-
-// Define the Gaussian type struct; will be initialized later
-PyTypeObject PyBobLearnEMGMMMachine_Type = {
-  PyVarObject_HEAD_INIT(0,0)
-  0
-};
-
-bool init_BobLearnEMGMMMachine(PyObject* module)
-{
-  // initialize the type struct
-  PyBobLearnEMGMMMachine_Type.tp_name = GMMMachine_doc.name();
-  PyBobLearnEMGMMMachine_Type.tp_basicsize = sizeof(PyBobLearnEMGMMMachineObject);
-  PyBobLearnEMGMMMachine_Type.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE;
-  PyBobLearnEMGMMMachine_Type.tp_doc = GMMMachine_doc.doc();
-
-  // set the functions
-  PyBobLearnEMGMMMachine_Type.tp_new = PyType_GenericNew;
-  PyBobLearnEMGMMMachine_Type.tp_init = reinterpret_cast<initproc>(PyBobLearnEMGMMMachine_init);
-  PyBobLearnEMGMMMachine_Type.tp_dealloc = reinterpret_cast<destructor>(PyBobLearnEMGMMMachine_delete);
-  PyBobLearnEMGMMMachine_Type.tp_richcompare = reinterpret_cast<richcmpfunc>(PyBobLearnEMGMMMachine_RichCompare);
-  PyBobLearnEMGMMMachine_Type.tp_methods = PyBobLearnEMGMMMachine_methods;
-  PyBobLearnEMGMMMachine_Type.tp_getset = PyBobLearnEMGMMMachine_getseters;
-  PyBobLearnEMGMMMachine_Type.tp_call = reinterpret_cast<ternaryfunc>(PyBobLearnEMGMMMachine_loglikelihood);
-
-
-  // check that everything is fine
-  if (PyType_Ready(&PyBobLearnEMGMMMachine_Type) < 0) return false;
-
-  // add the type to the module
-  Py_INCREF(&PyBobLearnEMGMMMachine_Type);
-  return PyModule_AddObject(module, "GMMMachine", (PyObject*)&PyBobLearnEMGMMMachine_Type) >= 0;
-}
diff --git a/bob/learn/em/gmm_stats.cpp b/bob/learn/em/gmm_stats.cpp
deleted file mode 100644
index 32011ca86aeccf112af86f4a9c3b37abdad5881b..0000000000000000000000000000000000000000
--- a/bob/learn/em/gmm_stats.cpp
+++ /dev/null
@@ -1,673 +0,0 @@
-/**
- * @author Tiago de Freitas Pereira <tiago.pereira@idiap.ch>
- * @date Wed 03 Dec 14:38:48 2014
- *
- * @brief Python API for bob::learn::em
- *
- * Copyright (C) 2011-2014 Idiap Research Institute, Martigny, Switzerland
- */
-
-#include "main.h"
-
-/******************************************************************/
-/************ Constructor Section *********************************/
-/******************************************************************/
-
-static auto GMMStats_doc = bob::extension::ClassDoc(
-  BOB_EXT_MODULE_PREFIX ".GMMStats",
-  "A container for GMM statistics",
-  "With respect to [Reynolds2000]_ the class computes: \n\n"
-  "* Eq (8) is :py:class:`bob.learn.em.GMMStats.n`: :math:`n_i=\\sum\\limits_{t=1}^T Pr(i | x_t)`\n\n"
-  "* Eq (9) is :py:class:`bob.learn.em.GMMStats.sum_px`:  :math:`E_i(x)=\\frac{1}{n(i)}\\sum\\limits_{t=1}^T Pr(i | x_t)x_t`\n\n"
-  "* Eq (10) is :py:class:`bob.learn.em.GMMStats.sum_pxx`: :math:`E_i(x^2)=\\frac{1}{n(i)}\\sum\\limits_{t=1}^T Pr(i | x_t)x_t^2`\n\n"
-).add_constructor(
-  bob::extension::FunctionDoc(
-    "__init__",
-    "A container for GMM statistics.",
-    "",
-    true
-  )
-  .add_prototype("n_gaussians,n_inputs","")
-  .add_prototype("other","")
-  .add_prototype("hdf5","")
-  .add_prototype("","")
-
-  .add_parameter("n_gaussians", "int", "Number of gaussians")
-  .add_parameter("n_inputs", "int", "Dimension of the feature vector")
-  .add_parameter("other", ":py:class:`bob.learn.em.GMMStats`", "A GMMStats object to be copied.")
-  .add_parameter("hdf5", ":py:class:`bob.io.base.HDF5File`", "An HDF5 file open for reading")
-
-);
-
-
-static int PyBobLearnEMGMMStats_init_number(PyBobLearnEMGMMStatsObject* self, PyObject* args, PyObject* kwargs) {
-
-  char** kwlist = GMMStats_doc.kwlist(0);
-  int n_inputs    = 1;
-  int n_gaussians = 1;
-  //Parsing the input argments
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ii", kwlist, &n_gaussians, &n_inputs))
-    return -1;
-
-  if(n_gaussians < 0){
-    PyErr_Format(PyExc_TypeError, "gaussians argument must be greater than or equal to zero");
-    GMMStats_doc.print_usage();
-    return -1;
-  }
-
-  if(n_inputs < 0){
-    PyErr_Format(PyExc_TypeError, "input argument must be greater than or equal to zero");
-    GMMStats_doc.print_usage();
-    return -1;
-   }
-
-  self->cxx.reset(new bob::learn::em::GMMStats(n_gaussians, n_inputs));
-  return 0;
-}
-
-
-static int PyBobLearnEMGMMStats_init_copy(PyBobLearnEMGMMStatsObject* self, PyObject* args, PyObject* kwargs) {
-
-  char** kwlist = GMMStats_doc.kwlist(1);
-  PyBobLearnEMGMMStatsObject* tt;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", kwlist, &PyBobLearnEMGMMStats_Type, &tt)){
-    GMMStats_doc.print_usage();
-    return -1;
-  }
-
-  self->cxx.reset(new bob::learn::em::GMMStats(*tt->cxx));
-  return 0;
-}
-
-
-static int PyBobLearnEMGMMStats_init_hdf5(PyBobLearnEMGMMStatsObject* self, PyObject* args, PyObject* kwargs) {
-
-  char** kwlist = GMMStats_doc.kwlist(2);
-
-  PyBobIoHDF5FileObject* config = 0;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, &PyBobIoHDF5File_Converter, &config)){
-    GMMStats_doc.print_usage();
-    return -1;
-  }
-  auto config_ = make_safe(config);
-  self->cxx.reset(new bob::learn::em::GMMStats(*(config->f)));
-
-  return 0;
-}
-
-
-
-static int PyBobLearnEMGMMStats_init(PyBobLearnEMGMMStatsObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  // get the number of command line arguments
-  int nargs = (args?PyTuple_Size(args):0) + (kwargs?PyDict_Size(kwargs):0);
-
-  switch (nargs) {
-
-    case 0: //default initializer ()
-      self->cxx.reset(new bob::learn::em::GMMStats());
-      return 0;
-
-    case 1:{
-      //Reading the input argument
-      PyObject* arg = 0;
-      if (PyTuple_Size(args))
-        arg = PyTuple_GET_ITEM(args, 0);
-      else {
-        PyObject* tmp = PyDict_Values(kwargs);
-        auto tmp_ = make_safe(tmp);
-        arg = PyList_GET_ITEM(tmp, 0);
-      }
-
-      /**If the constructor input is Gaussian object**/
-     if (PyBobLearnEMGMMStats_Check(arg))
-       return PyBobLearnEMGMMStats_init_copy(self, args, kwargs);
-      /**If the constructor input is a HDF5**/
-     else if (PyBobIoHDF5File_Check(arg))
-       return PyBobLearnEMGMMStats_init_hdf5(self, args, kwargs);
-    }
-    case 2:
-      return PyBobLearnEMGMMStats_init_number(self, args, kwargs);
-    default:
-      PyErr_Format(PyExc_RuntimeError, "number of arguments mismatch - %s requires 0, 1 or 2 arguments, but you provided %d (see help)", Py_TYPE(self)->tp_name, nargs);
-      GMMStats_doc.print_usage();
-      return -1;
-  }
-  BOB_CATCH_MEMBER("cannot create GMMStats", -1)
-  return 0;
-}
-
-
-
-static void PyBobLearnEMGMMStats_delete(PyBobLearnEMGMMStatsObject* self) {
-  self->cxx.reset();
-  Py_TYPE(self)->tp_free((PyObject*)self);
-}
-
-static PyObject* PyBobLearnEMGMMStats_RichCompare(PyBobLearnEMGMMStatsObject* self, PyObject* other, int op) {
-  BOB_TRY
-
-  if (!PyBobLearnEMGMMStats_Check(other)) {
-    PyErr_Format(PyExc_TypeError, "cannot compare `%s' with `%s'", Py_TYPE(self)->tp_name, Py_TYPE(other)->tp_name);
-    return 0;
-  }
-  auto other_ = reinterpret_cast<PyBobLearnEMGMMStatsObject*>(other);
-  switch (op) {
-    case Py_EQ:
-      if (*self->cxx==*other_->cxx) Py_RETURN_TRUE; else Py_RETURN_FALSE;
-    case Py_NE:
-      if (*self->cxx==*other_->cxx) Py_RETURN_FALSE; else Py_RETURN_TRUE;
-    default:
-      Py_INCREF(Py_NotImplemented);
-      return Py_NotImplemented;
-  }
-  BOB_CATCH_MEMBER("cannot compare GMMStats objects", 0)
-}
-
-int PyBobLearnEMGMMStats_Check(PyObject* o) {
-  return PyObject_IsInstance(o, reinterpret_cast<PyObject*>(&PyBobLearnEMGMMStats_Type));
-}
-
-
-/******************************************************************/
-/************ Variables Section ***********************************/
-/******************************************************************/
-
-/***** n *****/
-static auto n = bob::extension::VariableDoc(
-  "n",
-  "array_like <float, 1D>",
-  "For each Gaussian, the accumulated sum of responsibilities, i.e. the sum of :math:`P(gaussian_i|x)`"
-);
-PyObject* PyBobLearnEMGMMStats_getN(PyBobLearnEMGMMStatsObject* self, void*){
-  BOB_TRY
-  return PyBlitzArrayCxx_AsConstNumpy(self->cxx->n);
-  BOB_CATCH_MEMBER("n could not be read", 0)
-}
-int PyBobLearnEMGMMStats_setN(PyBobLearnEMGMMStatsObject* self, PyObject* value, void*){
-  BOB_TRY
-  PyBlitzArrayObject* input;
-  if (!PyBlitzArray_Converter(value, &input)){
-    PyErr_Format(PyExc_RuntimeError, "%s %s expects a 1D array of floats", Py_TYPE(self)->tp_name, n.name());
-    return -1;
-  }
-  auto o_ = make_safe(input);
-
-  // perform check on the input
-  if (input->type_num != NPY_FLOAT64){
-    PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, n.name());
-    return -1;
-  }
-
-  if (input->ndim != 1){
-    PyErr_Format(PyExc_TypeError, "`%s' only processes 1D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, n.name());
-    return -1;
-  }
-
-  if (input->shape[0] != (Py_ssize_t)self->cxx->n.extent(0)){
-    PyErr_Format(PyExc_TypeError, "`%s' 1D `input` array should have %" PY_FORMAT_SIZE_T "d elements, not %" PY_FORMAT_SIZE_T "d for `%s`", Py_TYPE(self)->tp_name, (Py_ssize_t)self->cxx->n.extent(0), input->shape[0], n.name());
-    return -1;
-  }
-
-  auto b = PyBlitzArrayCxx_AsBlitz<double,1>(input, "n");
-  if (!b) return -1;
-  self->cxx->n = *b;
-  return 0;
-  BOB_CATCH_MEMBER("n could not be set", -1)
-}
-
-
-/***** sum_px *****/
-static auto sum_px = bob::extension::VariableDoc(
-  "sum_px",
-  "array_like <float, 2D>",
-  "For each Gaussian, the accumulated sum of responsibility times the sample"
-);
-PyObject* PyBobLearnEMGMMStats_getSum_px(PyBobLearnEMGMMStatsObject* self, void*){
-  BOB_TRY
-  return PyBlitzArrayCxx_AsConstNumpy(self->cxx->sumPx);
-  BOB_CATCH_MEMBER("sum_px could not be read", 0)
-}
-int PyBobLearnEMGMMStats_setSum_px(PyBobLearnEMGMMStatsObject* self, PyObject* value, void*){
-  BOB_TRY
-  PyBlitzArrayObject* input;
-  if (!PyBlitzArray_Converter(value, &input)){
-    PyErr_Format(PyExc_RuntimeError, "%s %s expects a 2D array of floats", Py_TYPE(self)->tp_name, sum_px.name());
-    return -1;
-  }
-  auto o_ = make_safe(input);
-
-  // perform check on the input
-  if (input->type_num != NPY_FLOAT64){
-    PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, sum_px.name());
-    return -1;
-  }
-
-  if (input->ndim != 2){
-    PyErr_Format(PyExc_TypeError, "`%s' only processes 2D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, sum_px.name());
-    return -1;
-  }
-
-  if (input->shape[1] != (Py_ssize_t)self->cxx->sumPx.extent(1) && input->shape[0] != (Py_ssize_t)self->cxx->sumPx.extent(0)) {
-    PyErr_Format(PyExc_TypeError, "`%s' 2D `input` array should have the shape [%" PY_FORMAT_SIZE_T "d, %" PY_FORMAT_SIZE_T "d] not [%" PY_FORMAT_SIZE_T "d, %" PY_FORMAT_SIZE_T "d] for `%s`", Py_TYPE(self)->tp_name, (Py_ssize_t)self->cxx->sumPx.extent(1), (Py_ssize_t)self->cxx->sumPx.extent(0), (Py_ssize_t)input->shape[1], (Py_ssize_t)input->shape[0], sum_px.name());
-    return -1;
-  }
-
-  auto b = PyBlitzArrayCxx_AsBlitz<double,2>(input, "sum_px");
-  if (!b) return -1;
-  self->cxx->sumPx = *b;
-  return 0;
-  BOB_CATCH_MEMBER("sum_px could not be set", -1)
-}
-
-
-/***** sum_pxx *****/
-static auto sum_pxx = bob::extension::VariableDoc(
-  "sum_pxx",
-  "array_like <float, 2D>",
-  "For each Gaussian, the accumulated sum of responsibility times the sample squared"
-);
-PyObject* PyBobLearnEMGMMStats_getSum_pxx(PyBobLearnEMGMMStatsObject* self, void*){
-  BOB_TRY
-  return PyBlitzArrayCxx_AsConstNumpy(self->cxx->sumPxx);
-  BOB_CATCH_MEMBER("sum_pxx could not be read", 0)
-}
-int PyBobLearnEMGMMStats_setSum_pxx(PyBobLearnEMGMMStatsObject* self, PyObject* value, void*){
-  BOB_TRY
-  PyBlitzArrayObject* input;
-  if (!PyBlitzArray_Converter(value, &input)){
-    PyErr_Format(PyExc_RuntimeError, "%s %s expects a 2D array of floats", Py_TYPE(self)->tp_name, sum_pxx.name());
-    return -1;
-  }
-  auto o_ = make_safe(input);
-
-  // perform check on the input
-  if (input->type_num != NPY_FLOAT64){
-    PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, sum_pxx.name());
-    return -1;
-  }
-
-  if (input->ndim != 2){
-    PyErr_Format(PyExc_TypeError, "`%s' only processes 2D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, sum_pxx.name());
-    return -1;
-  }
-
-  if (input->shape[1] != (Py_ssize_t)self->cxx->sumPxx.extent(1) && input->shape[0] != (Py_ssize_t)self->cxx->sumPxx.extent(0)) {
-    PyErr_Format(PyExc_TypeError, "`%s' 2D `input` array should have the shape [%" PY_FORMAT_SIZE_T "d, %" PY_FORMAT_SIZE_T "d] not [%" PY_FORMAT_SIZE_T "d, %" PY_FORMAT_SIZE_T "d] for `%s`", Py_TYPE(self)->tp_name, (Py_ssize_t)self->cxx->sumPxx.extent(1), (Py_ssize_t)self->cxx->sumPxx.extent(0), (Py_ssize_t)input->shape[1], (Py_ssize_t)input->shape[0], sum_pxx.name());
-    return -1;
-  }
-
-  auto b = PyBlitzArrayCxx_AsBlitz<double,2>(input, "sum_pxx");
-  if (!b) return -1;
-  self->cxx->sumPxx = *b;
-  return 0;
-  BOB_CATCH_MEMBER("sum_pxx could not be set", -1)
-}
-
-
-/***** t *****/
-static auto t = bob::extension::VariableDoc(
-  "t",
-  "int",
-  "The number of samples"
-);
-PyObject* PyBobLearnEMGMMStats_getT(PyBobLearnEMGMMStatsObject* self, void*){
-  BOB_TRY
-  return Py_BuildValue("i", self->cxx->T);
-  BOB_CATCH_MEMBER("t could not be read", 0)
-}
-int PyBobLearnEMGMMStats_setT(PyBobLearnEMGMMStatsObject* self, PyObject* value, void*){
-  BOB_TRY
-
-  if (!PyInt_Check(value)){
-    PyErr_Format(PyExc_RuntimeError, "%s %s expects an int", Py_TYPE(self)->tp_name, t.name());
-    return -1;
-  }
-
-  if (PyInt_AS_LONG(value) < 0){
-    PyErr_Format(PyExc_TypeError, "t must be greater than or equal to zero");
-    return -1;
-  }
-
-  self->cxx->T = PyInt_AS_LONG(value);
-  BOB_CATCH_MEMBER("t could not be set", -1)
-  return 0;
-}
-
-
-/***** log_likelihood *****/
-static auto log_likelihood = bob::extension::VariableDoc(
-  "log_likelihood",
-  "float",
-  "The accumulated log likelihood of all samples"
-);
-PyObject* PyBobLearnEMGMMStats_getLog_likelihood(PyBobLearnEMGMMStatsObject* self, void*){
-  BOB_TRY
-  return Py_BuildValue("d", self->cxx->log_likelihood);
-  BOB_CATCH_MEMBER("log_likelihood could not be read", 0)
-}
-int PyBobLearnEMGMMStats_setLog_likelihood(PyBobLearnEMGMMStatsObject* self, PyObject* value, void*){
-  BOB_TRY
-
-  if (!PyBob_NumberCheck(value)){
-    PyErr_Format(PyExc_RuntimeError, "%s %s expects an double", Py_TYPE(self)->tp_name, t.name());
-    return -1;
-  }
-
-  self->cxx->log_likelihood = PyFloat_AsDouble(value);
-  return 0;
-  BOB_CATCH_MEMBER("log_likelihood could not be set", -1)
-}
-
-
-/***** shape *****/
-static auto shape = bob::extension::VariableDoc(
-  "shape",
-  "(int,int)",
-  "A tuple that represents the number of gaussians and dimensionality of each Gaussian ``(n_gaussians, dim)``.",
-  ""
-);
-PyObject* PyBobLearnEMGMMStats_getShape(PyBobLearnEMGMMStatsObject* self, void*) {
-  BOB_TRY
-  return Py_BuildValue("(i,i)", self->cxx->sumPx.shape()[0], self->cxx->sumPx.shape()[1]);
-  BOB_CATCH_MEMBER("shape could not be read", 0)
-}
-
-
-
-static PyGetSetDef PyBobLearnEMGMMStats_getseters[] = {
-  {
-    n.name(),
-    (getter)PyBobLearnEMGMMStats_getN,
-    (setter)PyBobLearnEMGMMStats_setN,
-    n.doc(),
-    0
-  },
-  {
-    sum_px.name(),
-    (getter)PyBobLearnEMGMMStats_getSum_px,
-    (setter)PyBobLearnEMGMMStats_setSum_px,
-    sum_px.doc(),
-    0
-  },
-  {
-    sum_pxx.name(),
-    (getter)PyBobLearnEMGMMStats_getSum_pxx,
-    (setter)PyBobLearnEMGMMStats_setSum_pxx,
-    sum_pxx.doc(),
-    0
-  },
-  {
-    t.name(),
-    (getter)PyBobLearnEMGMMStats_getT,
-    (setter)PyBobLearnEMGMMStats_setT,
-    t.doc(),
-    0
-  },
-  {
-    log_likelihood.name(),
-    (getter)PyBobLearnEMGMMStats_getLog_likelihood,
-    (setter)PyBobLearnEMGMMStats_setLog_likelihood,
-    log_likelihood.doc(),
-    0
-  },
-  {
-   shape.name(),
-   (getter)PyBobLearnEMGMMStats_getShape,
-   0,
-   shape.doc(),
-   0
-  },
-
-
-  {0}  // Sentinel
-};
-
-
-/******************************************************************/
-/************ Functions Section ***********************************/
-/******************************************************************/
-
-
-/*** save ***/
-static auto save = bob::extension::FunctionDoc(
-  "save",
-  "Save the configuration of the GMMStats to a given HDF5 file"
-)
-.add_prototype("hdf5")
-.add_parameter("hdf5", ":py:class:`bob.io.base.HDF5File`", "An HDF5 file open for writing");
-static PyObject* PyBobLearnEMGMMStats_Save(PyBobLearnEMGMMStatsObject* self,  PyObject* args, PyObject* kwargs) {
-
-  BOB_TRY
-
-  // get list of arguments
-  char** kwlist = save.kwlist(0);
-  PyBobIoHDF5FileObject* hdf5;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, PyBobIoHDF5File_Converter, &hdf5)) return 0;
-
-  auto hdf5_ = make_safe(hdf5);
-  self->cxx->save(*hdf5->f);
-
-  BOB_CATCH_MEMBER("cannot save the data", 0)
-  Py_RETURN_NONE;
-}
-
-/*** load ***/
-static auto load = bob::extension::FunctionDoc(
-  "load",
-  "Load the configuration of the GMMStats to a given HDF5 file"
-)
-.add_prototype("hdf5")
-.add_parameter("hdf5", ":py:class:`bob.io.base.HDF5File`", "An HDF5 file open for reading");
-static PyObject* PyBobLearnEMGMMStats_Load(PyBobLearnEMGMMStatsObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  char** kwlist = load.kwlist(0);
-  PyBobIoHDF5FileObject* hdf5;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, PyBobIoHDF5File_Converter, &hdf5)) return 0;
-
-  auto hdf5_ = make_safe(hdf5);
-  self->cxx->load(*hdf5->f);
-
-  BOB_CATCH_MEMBER("cannot load the data", 0)
-  Py_RETURN_NONE;
-}
-
-
-/*** is_similar_to ***/
-static auto is_similar_to = bob::extension::FunctionDoc(
-  "is_similar_to",
-
-  "Compares this GMMStats with the ``other`` one to be approximately the same.",
-  "The optional values ``r_epsilon`` and ``a_epsilon`` refer to the "
-  "relative and absolute precision for the ``weights``, ``biases`` "
-  "and any other values internal to this machine."
-)
-.add_prototype("other, [r_epsilon], [a_epsilon]","output")
-.add_parameter("other", ":py:class:`bob.learn.em.GMMStats`", "A GMMStats object to be compared.")
-.add_parameter("r_epsilon", "float", "Relative precision.")
-.add_parameter("a_epsilon", "float", "Absolute precision.")
-.add_return("output","bool","True if it is similar, otherwise false.");
-static PyObject* PyBobLearnEMGMMStats_IsSimilarTo(PyBobLearnEMGMMStatsObject* self, PyObject* args, PyObject* kwds) {
-
-  /* Parses input arguments in a single shot */
-  char** kwlist = is_similar_to.kwlist(0);
-
-  //PyObject* other = 0;
-  PyBobLearnEMGMMStatsObject* other = 0;
-  double r_epsilon = 1.e-5;
-  double a_epsilon = 1.e-8;
-
-  if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|dd", kwlist,
-        &PyBobLearnEMGMMStats_Type, &other,
-        &r_epsilon, &a_epsilon)){
-
-        is_similar_to.print_usage();
-        return 0;
-  }
-
-  if (self->cxx->is_similar_to(*other->cxx, r_epsilon, a_epsilon))
-    Py_RETURN_TRUE;
-  else
-    Py_RETURN_FALSE;
-}
-
-
-/*** resize ***/
-static auto resize = bob::extension::FunctionDoc(
-  "resize",
-  "Allocates space for the statistics and resets to zero.",
-  0,
-  true
-)
-.add_prototype("n_gaussians,n_inputs")
-.add_parameter("n_gaussians", "int", "Number of gaussians")
-.add_parameter("n_inputs", "int", "Dimensionality of the feature vector");
-static PyObject* PyBobLearnEMGMMStats_resize(PyBobLearnEMGMMStatsObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  /* Parses input arguments in a single shot */
-  char** kwlist = resize.kwlist(0);
-
-  int n_gaussians = 0;
-  int n_inputs = 0;
-
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ii", kwlist, &n_gaussians, &n_inputs)) return 0;
-
-  if (n_gaussians <= 0){
-    PyErr_Format(PyExc_TypeError, "n_gaussians must be greater than zero");
-    resize.print_usage();
-    return 0;
-  }
-  if (n_inputs <= 0){
-    PyErr_Format(PyExc_TypeError, "n_inputs must be greater than zero");
-    resize.print_usage();
-    return 0;
-  }
-
-
-  self->cxx->resize(n_gaussians, n_inputs);
-
-  BOB_CATCH_MEMBER("cannot perform the resize method", 0)
-
-  Py_RETURN_NONE;
-}
-
-
-/*** init ***/
-static auto init = bob::extension::FunctionDoc(
-  "init",
-  " Resets statistics to zero."
-)
-.add_prototype("");
-static PyObject* PyBobLearnEMGMMStats_init_method(PyBobLearnEMGMMStatsObject* self) {
-  BOB_TRY
-
-  self->cxx->init();
-
-  BOB_CATCH_MEMBER("cannot perform the init method", 0)
-
-  Py_RETURN_NONE;
-}
-
-
-
-static PyMethodDef PyBobLearnEMGMMStats_methods[] = {
-  {
-    save.name(),
-    (PyCFunction)PyBobLearnEMGMMStats_Save,
-    METH_VARARGS|METH_KEYWORDS,
-    save.doc()
-  },
-  {
-    load.name(),
-    (PyCFunction)PyBobLearnEMGMMStats_Load,
-    METH_VARARGS|METH_KEYWORDS,
-    load.doc()
-  },
-  {
-    is_similar_to.name(),
-    (PyCFunction)PyBobLearnEMGMMStats_IsSimilarTo,
-    METH_VARARGS|METH_KEYWORDS,
-    is_similar_to.doc()
-  },
-  {
-    resize.name(),
-    (PyCFunction)PyBobLearnEMGMMStats_resize,
-    METH_VARARGS|METH_KEYWORDS,
-    resize.doc()
-  },
-  {
-    init.name(),
-    (PyCFunction)PyBobLearnEMGMMStats_init_method,
-    METH_NOARGS,
-    init.doc()
-  },
-
-  {0} /* Sentinel */
-};
-
-
-/******************************************************************/
-/************ Operators *******************************************/
-/******************************************************************/
-
-static PyBobLearnEMGMMStatsObject* PyBobLearnEMGMMStats_inplaceadd(PyBobLearnEMGMMStatsObject* self, PyObject* other) {
-  BOB_TRY
-
-  if (!PyBobLearnEMGMMStats_Check(other)){
-    PyErr_Format(PyExc_TypeError, "expected bob.learn.em.GMMStats object");
-    return 0;
-  }
-
-  auto other_ = reinterpret_cast<PyBobLearnEMGMMStatsObject*>(other);
-
-  self->cxx->operator+=(*other_->cxx);
-
-  BOB_CATCH_MEMBER("it was not possible to process the operator +=", 0)
-
-  Py_INCREF(self);
-  return self;
-}
-
-static PyNumberMethods PyBobLearnEMGMMStats_operators = {0};
-
-/******************************************************************/
-/************ Module Section **************************************/
-/******************************************************************/
-
-// Define the Gaussian type struct; will be initialized later
-PyTypeObject PyBobLearnEMGMMStats_Type = {
-  PyVarObject_HEAD_INIT(0,0)
-  0
-};
-
-bool init_BobLearnEMGMMStats(PyObject* module)
-{
-  // initialize the type struct
-  PyBobLearnEMGMMStats_Type.tp_name = GMMStats_doc.name();
-  PyBobLearnEMGMMStats_Type.tp_basicsize = sizeof(PyBobLearnEMGMMStatsObject);
-  PyBobLearnEMGMMStats_Type.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE;
-  PyBobLearnEMGMMStats_Type.tp_doc = GMMStats_doc.doc();
-
-  // set the functions
-  PyBobLearnEMGMMStats_Type.tp_new = PyType_GenericNew;
-  PyBobLearnEMGMMStats_Type.tp_init = reinterpret_cast<initproc>(PyBobLearnEMGMMStats_init);
-  PyBobLearnEMGMMStats_Type.tp_dealloc = reinterpret_cast<destructor>(PyBobLearnEMGMMStats_delete);
-  PyBobLearnEMGMMStats_Type.tp_richcompare = reinterpret_cast<richcmpfunc>(PyBobLearnEMGMMStats_RichCompare);
-  PyBobLearnEMGMMStats_Type.tp_methods = PyBobLearnEMGMMStats_methods;
-  PyBobLearnEMGMMStats_Type.tp_getset = PyBobLearnEMGMMStats_getseters;
-  PyBobLearnEMGMMStats_Type.tp_call = 0;
-  PyBobLearnEMGMMStats_Type.tp_as_number = &PyBobLearnEMGMMStats_operators;
-
-  //set operators
-  PyBobLearnEMGMMStats_operators.nb_inplace_add = reinterpret_cast<binaryfunc>(PyBobLearnEMGMMStats_inplaceadd);
-
-  // check that everything is fine
-  if (PyType_Ready(&PyBobLearnEMGMMStats_Type) < 0) return false;
-
-  // add the type to the module
-  Py_INCREF(&PyBobLearnEMGMMStats_Type);
-  return PyModule_AddObject(module, "GMMStats", (PyObject*)&PyBobLearnEMGMMStats_Type) >= 0;
-}
diff --git a/bob/learn/em/include/bob.learn.em/EMPCATrainer.h b/bob/learn/em/include/bob.learn.em/EMPCATrainer.h
deleted file mode 100644
index 90153c2adaeece5b179598316ecb7e766bb4850b..0000000000000000000000000000000000000000
--- a/bob/learn/em/include/bob.learn.em/EMPCATrainer.h
+++ /dev/null
@@ -1,200 +0,0 @@
-/**
- * @date Tue Oct 11 12:18:23 2011 +0200
- * @author Laurent El Shafey <Laurent.El-Shafey@idiap.ch>
- *
- * @brief Expectation Maximization Algorithm for Principal Component
- * Analysis
- *
- * Copyright (C) Idiap Research Institute, Martigny, Switzerland
- */
-
-#ifndef BOB_LEARN_EM_EMPCA_TRAINER_H
-#define BOB_LEARN_EM_EMPCA_TRAINER_H
-
-#include <bob.learn.linear/machine.h>
-#include <blitz/array.h>
-
-namespace bob { namespace learn { namespace em {
-
-/**
- * @brief Trains a linear machine using an Expectation-Maximization algorithm
- * on the given dataset.\n
- * References:\n
- *  1. "Probabilistic Principal Component Analysis",
- *     Michael Tipping and Christopher Bishop,
- *     Journal of the Royal Statistical Society,
- *      Series B, 61, Part 3, pp. 611–622\n
- *  2. "EM Algorithms for PCA and SPCA",
- *     Sam Roweis, Neural Information Processing Systems 10 (NIPS'97),
- *     pp.626-632 (Sensible Principal Component Analysis part)\n
- *
- * Notations used are the ones from reference 1.\n
- * The probabilistic model is given by: \f$t = W x + \mu + \epsilon\f$\n
- *  - \f$t\f$ is the observed data (dimension \f$f\f$)\n
- *  - \f$W\f$ is a  projection matrix (dimension \f$f \times d\f$)\n
- *  - \f$x\f$ is the projected data (dimension \f$d < f\f$)\n
- *  - \f$\mu\f$ is the mean of the data (dimension \f$f\f$)\n
- *  - \f$\epsilon\f$ is the noise of the data (dimension \f$f\f$)
- *      Gaussian with zero-mean and covariance matrix \f$\sigma^2 Id\f$
- */
-class EMPCATrainer
-{
-  public: //api
-    /**
-     * @brief Initializes a new EM PCA trainer. The training stage will place the
-     * resulting components in the linear machine and set it up to
-     * extract the variable means automatically.
-     */
-    EMPCATrainer(bool compute_likelihood=true);
-
-    /**
-     * @brief Copy constructor
-     */
-    EMPCATrainer(const EMPCATrainer& other);
-
-    /**
-     * @brief (virtual) Destructor
-     */
-    virtual ~EMPCATrainer();
-
-    /**
-     * @brief Assignment operator
-     */
-    EMPCATrainer& operator=(const EMPCATrainer& other);
-
-    /**
-     * @brief Equal to
-     */
-    bool operator==(const EMPCATrainer& b) const;
-
-    /**
-     * @brief Not equal to
-     */
-    bool operator!=(const EMPCATrainer& b) const;
-
-    /**
-     * @brief Similar to
-     */
-    bool is_similar_to(const EMPCATrainer& b, const double r_epsilon=1e-5,
-      const double a_epsilon=1e-8) const;
-
-    /**
-     * @brief This methods performs some initialization before the EM loop.
-     */
-    virtual void initialize(bob::learn::linear::Machine& machine,
-      const blitz::Array<double,2>& ar);
-
-    /**
-     * @brief Calculates and saves statistics across the dataset, and saves
-     * these as m_z_{first,second}_order.
-     *
-     * The statistics will be used in the mStep() that follows.
-     */
-    virtual void eStep(bob::learn::linear::Machine& machine,
-      const blitz::Array<double,2>& ar);
-
-    /**
-     * @brief Performs a maximization step to update the parameters of the
-     * factor analysis model.
-     */
-    virtual void mStep(bob::learn::linear::Machine& machine,
-       const blitz::Array<double,2>& ar);
-
-    /**
-     * @brief Computes the average log likelihood using the current estimates
-     * of the latent variables.
-     */
-    virtual double computeLikelihood(bob::learn::linear::Machine& machine);
-
-    /**
-     * @brief Sets \f$\sigma^2\f$ (Mostly for test purpose)
-     */
-    void setSigma2(double sigma2) { m_sigma2 = sigma2; }
-
-    /**
-     * @brief Gets \f$\sigma^2\f$ (Mostly for test purpose)
-     */
-    double getSigma2() const { return m_sigma2; }
-
-    /**
-     * @brief Sets the Random Number Generator
-     */
-    void setRng(const boost::shared_ptr<boost::mt19937> rng)
-    { m_rng = rng; }
-
-    /**
-     * @brief Gets the Random Number Generator
-     */
-    const boost::shared_ptr<boost::mt19937> getRng() const
-    { return m_rng; }
-
-
-  private: //representation
-
-    bool m_compute_likelihood;
-    boost::shared_ptr<boost::mt19937> m_rng;
-
-    blitz::Array<double,2> m_S; /// Covariance of the training data (required only if we need to compute the log likelihood)
-    blitz::Array<double,2> m_z_first_order; /// Current mean of the \f$z_{n}\f$ latent variable
-    blitz::Array<double,3> m_z_second_order; /// Current covariance of the \f$z_{n}\f$ latent variable
-    blitz::Array<double,2> m_inW; /// The matrix product \f$W^T W\f$
-    blitz::Array<double,2> m_invM; /// The matrix \f$inv(M)\f$, where \f$M = W^T W + \sigma^2 Id\f$
-    double m_sigma2; /// The variance \f$sigma^2\f$ of the noise epsilon of the probabilistic model
-    double m_f_log2pi; /// The constant \f$n_{features} log(2*\pi)\f$ used during the likelihood computation
-
-    // Working arrays
-    mutable blitz::Array<double,2> m_tmp_dxf; /// size dimensionality x n_features
-    mutable blitz::Array<double,1> m_tmp_d; /// size dimensionality
-    mutable blitz::Array<double,1> m_tmp_f; /// size n_features
-    mutable blitz::Array<double,2> m_tmp_dxd_1; /// size dimensionality x dimensionality
-    mutable blitz::Array<double,2> m_tmp_dxd_2; /// size dimensionality x dimensionality
-    mutable blitz::Array<double,2> m_tmp_fxd_1; /// size n_features x dimensionality
-    mutable blitz::Array<double,2> m_tmp_fxd_2; /// size n_features x dimensionality
-    mutable blitz::Array<double,2> m_tmp_fxf_1; /// size n_features x n_features
-    mutable blitz::Array<double,2> m_tmp_fxf_2; /// size n_features x n_features
-
-
-    /**
-     * @brief Initializes/resizes the (array) members
-     */
-    void initMembers(const bob::learn::linear::Machine& machine,
-      const blitz::Array<double,2>& ar);
-    /**
-     * @brief Computes the mean and the variance (if required) of the training
-     * data
-     */
-    void computeMeanVariance(bob::learn::linear::Machine& machine,
-      const blitz::Array<double,2>& ar);
-    /**
-     * @brief Random initialization of \f$W\f$ and \f$sigma^2\f$.
-     * W is the projection matrix (from the LinearMachine)
-     */
-    void initRandomWSigma2(bob::learn::linear::Machine& machine);
-    /**
-     * @brief Computes the product \f$W^T W\f$.
-     * \f$W\f$ is the projection matrix (from the LinearMachine)
-     */
-    void computeWtW(bob::learn::linear::Machine& machine);
-    /**
-     * @brief Computes the inverse of \f$M\f$ matrix, where
-     *   \f$M = W^T W + \sigma^2 Id\f$.
-     *   \f$W\f$ is the projection matrix (from the LinearMachine)
-     */
-    void computeInvM();
-    /**
-     * @brief M-Step (part 1): Computes the new estimate of \f$W\f$ using the
-     * new estimated statistics.
-     */
-    void updateW(bob::learn::linear::Machine& machine,
-       const blitz::Array<double,2>& ar);
-    /**
-     * @brief M-Step (part 2): Computes the new estimate of \f$\sigma^2\f$ using
-     * the new estimated statistics.
-     */
-    void updateSigma2(bob::learn::linear::Machine& machine,
-       const blitz::Array<double,2>& ar);
-};
-
-} } } // namespaces
-
-#endif /* BOB_LEARN_EM_EMPCA_TRAINER_H */
diff --git a/bob/learn/em/include/bob.learn.em/FABase.h b/bob/learn/em/include/bob.learn.em/FABase.h
deleted file mode 100644
index 3b521989e96e50c27518a7929249abd6f15d9414..0000000000000000000000000000000000000000
--- a/bob/learn/em/include/bob.learn.em/FABase.h
+++ /dev/null
@@ -1,293 +0,0 @@
-/**
- * @date Tue Jan 27 15:51:15 2015 +0200
- * @author Laurent El Shafey <Laurent.El-Shafey@idiap.ch>
- * @author Tiago de Freitas Pereira <tiago.pereira@idiap.ch>
- *
- * @brief A base class for Factor Analysis
- *
- * Copyright (C) Idiap Research Institute, Martigny, Switzerland
- */
-
-#ifndef BOB_LEARN_EM_FABASE_H
-#define BOB_LEARN_EM_FABASE_H
-
-#include <stdexcept>
-
-#include <bob.learn.em/GMMMachine.h>
-#include <boost/shared_ptr.hpp>
-
-namespace bob { namespace learn { namespace em {
-
-/**
- * @brief A FA Base class which contains U, V and D matrices
- * TODO: add a reference to the journal articles
- */
-class FABase
-{
-  public:
-    /**
-     * @brief Default constructor. Builds an otherwise invalid 0 x 0 FABase
-     * The Universal Background Model and the matrices U, V and diag(d) are
-     * not initialized.
-     */
-    FABase();
-
-    /**
-     * @brief Constructor. Builds a new FABase.
-     * The Universal Background Model and the matrices U, V and diag(d) are
-     * not initialized.
-     *
-     * @param ubm The Universal Background Model
-     * @param ru size of U (CD x ru)
-     * @param rv size of U (CD x rv)
-     * @warning ru and rv SHOULD BE  >= 1. Just set U/V/D to zero if you want
-     *   to ignore one subspace. This is the case for ISV.
-     */
-    FABase(const boost::shared_ptr<bob::learn::em::GMMMachine> ubm, const size_t ru=1, const size_t rv=1);
-
-    /**
-     * @brief Copy constructor
-     */
-    FABase(const FABase& other);
-
-    /**
-     * @brief Just to virtualise the destructor
-     */
-    virtual ~FABase();
-
-    /**
-     * @brief Assigns from a different JFA machine
-     */
-    FABase& operator=(const FABase &other);
-
-    /**
-     * @brief Equal to
-     */
-    bool operator==(const FABase& b) const;
-
-    /**
-     * @brief Not equal to
-     */
-    bool operator!=(const FABase& b) const;
-
-    /**
-     * @brief Similar to
-     */
-    bool is_similar_to(const FABase& b, const double r_epsilon=1e-5,
-      const double a_epsilon=1e-8) const;
-
-    /**
-     * @brief Returns the UBM
-     */
-    const boost::shared_ptr<bob::learn::em::GMMMachine> getUbm() const
-    { return m_ubm; }
-
-    /**
-     * @brief Returns the U matrix
-     */
-    const blitz::Array<double,2>& getU() const
-    { return m_U; }
-
-    /**
-     * @brief Returns the V matrix
-     */
-    const blitz::Array<double,2>& getV() const
-    { return m_V; }
-
-    /**
-     * @brief Returns the diagonal matrix diag(d) (as a 1D vector)
-     */
-    const blitz::Array<double,1>& getD() const
-    { return m_d; }
-
-    /**
-     * @brief Returns the UBM mean supervector (as a 1D vector)
-     */
-    const blitz::Array<double,1>& getUbmMean() const
-    { return m_cache_mean; }
-
-    /**
-     * @brief Returns the UBM variance supervector (as a 1D vector)
-     */
-    const blitz::Array<double,1>& getUbmVariance() const
-    { return m_cache_sigma; }
-
-    /**
-     * @brief Returns the number of Gaussian components C
-     * @warning An exception is thrown if no Universal Background Model has
-     *   been set yet.
-     */
-    const size_t getNGaussians() const
-    { if(!m_ubm) throw std::runtime_error("No UBM was set in the JFA machine.");
-      return m_ubm->getNGaussians(); }
-
-    /**
-     * @brief Returns the feature dimensionality D
-     * @warning An exception is thrown if no Universal Background Model has
-     *   been set yet.
-     */
-    const size_t getNInputs() const
-    { if(!m_ubm) throw std::runtime_error("No UBM was set in the JFA machine.");
-      return m_ubm->getNInputs(); }
-
-    /**
-     * @brief Returns the supervector length CD
-     * (CxD: Number of Gaussian components by the feature dimensionality)
-     * @warning An exception is thrown if no Universal Background Model has
-     *   been set yet.
-     */
-    const size_t getSupervectorLength() const
-    { if(!m_ubm) throw std::runtime_error("No UBM was set in the JFA machine.");
-      return m_ubm->getNInputs()*m_ubm->getNGaussians(); }
-
-    /**
-     * @brief Returns the size/rank ru of the U matrix
-     */
-    const size_t getDimRu() const
-    { return m_ru; }
-
-    /**
-     * @brief Returns the size/rank rv of the V matrix
-     */
-    const size_t getDimRv() const
-    { return m_rv; }
-
-    /**
-     * @brief Resets the dimensionality of the subspace U and V
-     * U and V are hence uninitialized.
-     */
-    void resize(const size_t ru, const size_t rv);
-
-    /**
-     * @brief Resets the dimensionality of the subspace U and V,
-     * assuming that no UBM has yet been set
-     * U and V are hence uninitialized.
-     */
-    void resize(const size_t ru, const size_t rv, const size_t cd);
-
-    /**
-     * @brief Returns the U matrix in order to update it
-     * @warning Should only be used by the trainer for efficiency reason,
-     *   or for testing purpose.
-     */
-    blitz::Array<double,2>& updateU()
-    { return m_U; }
-
-    /**
-     * @brief Returns the V matrix in order to update it
-     * @warning Should only be used by the trainer for efficiency reason,
-     *   or for testing purpose.
-     */
-    blitz::Array<double,2>& updateV()
-    { return m_V; }
-
-    /**
-     * @brief Returns the diagonal matrix diag(d) (as a 1D vector) in order
-     * to update it
-     * @warning Should only be used by the trainer for efficiency reason,
-     *   or for testing purpose.
-     */
-    blitz::Array<double,1>& updateD()
-    { return m_d; }
-
-
-    /**
-     * @brief Sets (the mean supervector of) the Universal Background Model
-     * U, V and d are uninitialized in case of dimensions update (C or D)
-     */
-    void setUbm(const boost::shared_ptr<bob::learn::em::GMMMachine> ubm);
-
-    /**
-     * @brief Sets the U matrix
-     */
-    void setU(const blitz::Array<double,2>& U);
-
-    /**
-     * @brief Sets the V matrix
-     */
-    void setV(const blitz::Array<double,2>& V);
-
-    /**
-     * @brief Sets the diagonal matrix diag(d)
-     * (a 1D vector is expected as an argument)
-     */
-    void setD(const blitz::Array<double,1>& d);
-
-
-    /**
-     * @brief Estimates x from the GMM statistics considering the LPT
-     * assumption, that is the latent session variable x is approximated
-     * using the UBM
-     */
-    void estimateX(const bob::learn::em::GMMStats& gmm_stats, blitz::Array<double,1>& x) const;
-
-    /**
-     * @brief Compute and put U^{T}.Sigma^{-1} matrix in cache
-     * @warning Should only be used by the trainer for efficiency reason,
-     *   or for testing purpose.
-     */
-    void updateCacheUbmUVD();
-
-
-  private:
-    /**
-     * @brief Update cache arrays/variables
-     */
-    void updateCache();
-    /**
-     * @brief Put GMM mean/variance supervector in cache
-     */
-    void updateCacheUbm();
-    /**
-     * @brief Resize working arrays
-     */
-    void resizeTmp();
-    /**
-     * @brief Computes (Id + U^T.Sigma^-1.U.N_{i,h}.U)^-1 =
-     *   (Id + sum_{c=1..C} N_{i,h}.U_{c}^T.Sigma_{c}^-1.U_{c})^-1
-     */
-    void computeIdPlusUSProdInv(const bob::learn::em::GMMStats& gmm_stats,
-      blitz::Array<double,2>& out) const;
-    /**
-     * @brief Computes Fn_x = sum_{sessions h}(N*(o - m))
-     * (Normalised first order statistics)
-     */
-    void computeFn_x(const bob::learn::em::GMMStats& gmm_stats,
-      blitz::Array<double,1>& out) const;
-    /**
-     * @brief Estimates the value of x from the passed arguments
-     * (IdPlusUSProdInv and Fn_x), considering the LPT assumption
-     */
-    void estimateX(const blitz::Array<double,2>& IdPlusUSProdInv,
-      const blitz::Array<double,1>& Fn_x, blitz::Array<double,1>& x) const;
-
-
-    // UBM
-    boost::shared_ptr<bob::learn::em::GMMMachine> m_ubm;
-
-    // dimensionality
-    size_t m_ru; // size of U (CD x ru)
-    size_t m_rv; // size of V (CD x rv)
-
-    // U, V, D matrices
-    // D is assumed to be diagonal, and only the diagonal is stored
-    blitz::Array<double,2> m_U;
-    blitz::Array<double,2> m_V;
-    blitz::Array<double,1> m_d;
-
-    // Vectors/Matrices precomputed in cache
-    blitz::Array<double,1> m_cache_mean;
-    blitz::Array<double,1> m_cache_sigma;
-    blitz::Array<double,2> m_cache_UtSigmaInv;
-
-    mutable blitz::Array<double,2> m_tmp_IdPlusUSProdInv;
-    mutable blitz::Array<double,1> m_tmp_Fn_x;
-    mutable blitz::Array<double,1> m_tmp_ru;
-    mutable blitz::Array<double,2> m_tmp_ruD;
-    mutable blitz::Array<double,2> m_tmp_ruru;
-};
-
-
-} } } // namespaces
-
-#endif // BOB_LEARN_EM_FABASE_H
diff --git a/bob/learn/em/include/bob.learn.em/FABaseTrainer.h b/bob/learn/em/include/bob.learn.em/FABaseTrainer.h
deleted file mode 100644
index 037b4f2a35ab7d2237b0291a7fa75631a5008aa5..0000000000000000000000000000000000000000
--- a/bob/learn/em/include/bob.learn.em/FABaseTrainer.h
+++ /dev/null
@@ -1,350 +0,0 @@
-/**
- * @date Sat Jan 31 17:16:17 2015 +0200
- * @author Laurent El Shafey <Laurent.El-Shafey@idiap.ch>
- * @author Tiago de Freitas Pereira <tiago.pereira@idiap.ch>
- *
- * @brief FABaseTrainer functions
- *
- * Copyright (C) Idiap Research Institute, Martigny, Switzerland
- */
-
-#ifndef BOB_LEARN_EM_FABASETRAINER_H
-#define BOB_LEARN_EM_FABASETRAINER_H
-
-#include <blitz/array.h>
-#include <bob.learn.em/GMMStats.h>
-#include <bob.learn.em/JFAMachine.h>
-#include <vector>
-
-#include <map>
-#include <string>
-#include <bob.core/array_copy.h>
-#include <boost/shared_ptr.hpp>
-#include <boost/random.hpp>
-#include <bob.core/logging.h>
-
-namespace bob { namespace learn { namespace em {
-
-class FABaseTrainer
-{
-  public:
-    /**
-     * @brief Constructor
-     */
-    FABaseTrainer();
-
-    /**
-     * @brief Copy constructor
-     */
-    FABaseTrainer(const FABaseTrainer& other);
-
-    /**
-     * @brief Destructor
-     */
-    ~FABaseTrainer();
-
-    /**
-     * @brief Check that the dimensionality of the statistics match.
-     */
-    void checkStatistics(const bob::learn::em::FABase& m,
-      const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& stats);
-
-    /**
-     * @brief Initialize the dimensionality, the UBM, the sums of the
-     * statistics and the number of identities.
-     */
-    void initUbmNidSumStatistics(const bob::learn::em::FABase& m,
-      const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& stats);
-
-    /**
-     * @brief Precomputes the sums of the zeroth order statistics over the
-     * sessions for each client
-     */
-    void precomputeSumStatisticsN(const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& stats);
-    /**
-     * @brief Precomputes the sums of the first order statistics over the
-     * sessions for each client
-     */
-    void precomputeSumStatisticsF(const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& stats);
-
-    /**
-     * @brief Initializes (allocates and sets to zero) the x, y, z speaker
-     * factors
-     */
-    void initializeXYZ(const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& stats);
-
-    /**
-     * @brief Resets the x, y, z speaker factors to zero values
-     */
-    void resetXYZ();
-
-
-    /**** Y and V functions ****/
-    /**
-     * @brief Computes Vt * diag(sigma)^-1
-     */
-    void computeVtSigmaInv(const bob::learn::em::FABase& m);
-    /**
-     * @brief Computes Vt_{c} * diag(sigma)^-1 * V_{c} for each Gaussian c
-     */
-    void computeVProd(const bob::learn::em::FABase& m);
-    /**
-     * @brief Computes (I+Vt*diag(sigma)^-1*Ni*V)^-1 which occurs in the y
-     * estimation for the given person
-     */
-    void computeIdPlusVProd_i(const size_t id);
-    /**
-     * @brief Computes sum_{sessions h}(N_{i,h}*(o_{i,h} - m - D*z_{i} - U*x_{i,h})
-     * which occurs in the y estimation of the given person
-     */
-    void computeFn_y_i(const bob::learn::em::FABase& m,
-      const std::vector<boost::shared_ptr<bob::learn::em::GMMStats> >& stats,
-      const size_t id);
-    /**
-     * @brief Updates y_i (of the current person) and the accumulators to
-     * compute V with the cache values m_cache_IdPlusVprod_i, m_VtSigmaInv and
-     * m_cache_Fn_y_i
-     */
-    void updateY_i(const size_t id);
-    /**
-     * @brief Updates y and the accumulators to compute V
-     */
-    void updateY(const bob::learn::em::FABase& m,
-      const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& stats);
-    /**
-     * @brief Computes the accumulators m_acc_V_A1 and m_acc_V_A2 for V
-     * V = A2 * A1^-1
-     */
-    void computeAccumulatorsV(const bob::learn::em::FABase& m,
-      const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& stats);
-    /**
-     * @brief Updates V from the accumulators m_acc_V_A1 and m_acc_V_A2
-     */
-    void updateV(blitz::Array<double,2>& V);
-
-
-    /**** X and U functions ****/
-    /**
-     * @brief Computes Ut * diag(sigma)^-1
-     */
-    void computeUtSigmaInv(const bob::learn::em::FABase& m);
-    /**
-     * @brief Computes Ut_{c} * diag(sigma)^-1 * U_{c} for each Gaussian c
-     */
-    void computeUProd(const bob::learn::em::FABase& m);
-    /**
-     * @brief Computes (I+Ut*diag(sigma)^-1*Ni*U)^-1 which occurs in the x
-     * estimation
-     */
-    void computeIdPlusUProd_ih(const boost::shared_ptr<bob::learn::em::GMMStats>& stats);
-    /**
-     * @brief Computes sum_{sessions h}(N_{i,h}*(o_{i,h} - m - D*z_{i} - U*x_{i,h})
-     * which occurs in the y estimation of the given person
-     */
-    void computeFn_x_ih(const bob::learn::em::FABase& m,
-      const boost::shared_ptr<bob::learn::em::GMMStats>& stats, const size_t id);
-    /**
-     * @brief Updates x_ih (of the current person/session) and the
-     * accumulators to compute U with the cache values m_cache_IdPlusVprod_i,
-     * m_VtSigmaInv and m_cache_Fn_y_i
-     */
-    void updateX_ih(const size_t id, const size_t h);
-    /**
-     * @brief Updates x
-     */
-    void updateX(const bob::learn::em::FABase& m,
-      const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& stats);
-    /**
-     * @brief Computes the accumulators m_acc_U_A1 and m_acc_U_A2 for U
-     * U = A2 * A1^-1
-     */
-    void computeAccumulatorsU(const bob::learn::em::FABase& m,
-      const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& stats);
-    /**
-     * @brief Updates U from the accumulators m_acc_U_A1 and m_acc_U_A2
-     */
-    void updateU(blitz::Array<double,2>& U);
-
-
-    /**** z and D functions ****/
-    /**
-     * @brief Computes diag(D) * diag(sigma)^-1
-     */
-    void computeDtSigmaInv(const bob::learn::em::FABase& m);
-    /**
-     * @brief Computes Dt_{c} * diag(sigma)^-1 * D_{c} for each Gaussian c
-     */
-    void computeDProd(const bob::learn::em::FABase& m);
-    /**
-     * @brief Computes (I+diag(d)t*diag(sigma)^-1*Ni*diag(d))^-1 which occurs
-     * in the z estimation for the given person
-     */
-    void computeIdPlusDProd_i(const size_t id);
-    /**
-     * @brief Computes sum_{sessions h}(N_{i,h}*(o_{i,h} - m - V*y_{i} - U*x_{i,h})
-     * which occurs in the y estimation of the given person
-     */
-    void computeFn_z_i(const bob::learn::em::FABase& m,
-      const std::vector<boost::shared_ptr<bob::learn::em::GMMStats> >& stats, const size_t id);
-    /**
-     * @brief Updates z_i (of the current person) and the accumulators to
-     * compute D with the cache values m_cache_IdPlusDProd_i, m_VtSigmaInv
-     * and m_cache_Fn_z_i
-     */
-    void updateZ_i(const size_t id);
-    /**
-     * @brief Updates z and the accumulators to compute D
-     */
-    void updateZ(const bob::learn::em::FABase& m,
-      const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& stats);
-    /**
-     * @brief Computes the accumulators m_acc_D_A1 and m_acc_D_A2 for d
-     * d = A2 * A1^-1
-     */
-    void computeAccumulatorsD(const bob::learn::em::FABase& m,
-      const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& stats);
-    /**
-     * @brief Updates d from the accumulators m_acc_D_A1 and m_acc_D_A2
-     */
-    void updateD(blitz::Array<double,1>& d);
-
-
-    /**
-     * @brief Get the zeroth order statistics
-     */
-    const std::vector<blitz::Array<double,1> >& getNacc() const
-    { return m_Nacc; }
-    /**
-     * @brief Get the first order statistics
-     */
-    const std::vector<blitz::Array<double,1> >& getFacc() const
-    { return m_Facc; }
-    /**
-     * @brief Get the x speaker factors
-     */
-    const std::vector<blitz::Array<double,2> >& getX() const
-    { return m_x; }
-    /**
-     * @brief Get the y speaker factors
-     */
-    const std::vector<blitz::Array<double,1> >& getY() const
-    { return m_y; }
-    /**
-     * @brief Get the z speaker factors
-     */
-    const std::vector<blitz::Array<double,1> >& getZ() const
-    { return m_z; }
-    /**
-     * @brief Set the x speaker factors
-     */
-    void setX(const std::vector<blitz::Array<double,2> >& X)
-    { m_x = X; }
-    /**
-     * @brief Set the y speaker factors
-     */
-    void setY(const std::vector<blitz::Array<double,1> >& y)
-    { m_y = y; }
-    /**
-     * @brief Set the z speaker factors
-     */
-    void setZ(const std::vector<blitz::Array<double,1> >& z)
-    { m_z = z; }
-
-    /**
-     * @brief Initializes the cache to process the given statistics
-     */
-    void initCache();
-
-    /**
-     * @brief Getters for the accumulators
-     */
-    const blitz::Array<double,3>& getAccVA1() const
-    { return m_acc_V_A1; }
-    const blitz::Array<double,2>& getAccVA2() const
-    { return m_acc_V_A2; }
-    const blitz::Array<double,3>& getAccUA1() const
-    { return m_acc_U_A1; }
-    const blitz::Array<double,2>& getAccUA2() const
-    { return m_acc_U_A2; }
-    const blitz::Array<double,1>& getAccDA1() const
-    { return m_acc_D_A1; }
-    const blitz::Array<double,1>& getAccDA2() const
-    { return m_acc_D_A2; }
-
-    /**
-     * @brief Setters for the accumulators, Very useful if the e-Step needs
-     * to be parallelized.
-     */
-    void setAccVA1(const blitz::Array<double,3>& acc)
-    { bob::core::array::assertSameShape(acc, m_acc_V_A1);
-      m_acc_V_A1 = acc; }
-    void setAccVA2(const blitz::Array<double,2>& acc)
-    { bob::core::array::assertSameShape(acc, m_acc_V_A2);
-      m_acc_V_A2 = acc; }
-    void setAccUA1(const blitz::Array<double,3>& acc)
-    { bob::core::array::assertSameShape(acc, m_acc_U_A1);
-      m_acc_U_A1 = acc; }
-    void setAccUA2(const blitz::Array<double,2>& acc)
-    { bob::core::array::assertSameShape(acc, m_acc_U_A2);
-      m_acc_U_A2 = acc; }
-    void setAccDA1(const blitz::Array<double,1>& acc)
-    { bob::core::array::assertSameShape(acc, m_acc_D_A1);
-      m_acc_D_A1 = acc; }
-    void setAccDA2(const blitz::Array<double,1>& acc)
-    { bob::core::array::assertSameShape(acc, m_acc_D_A2);
-      m_acc_D_A2 = acc; }
-
-
-  private:
-    size_t m_Nid; // Number of identities
-    size_t m_dim_C; // Number of Gaussian components of the UBM GMM
-    size_t m_dim_D; // Dimensionality of the feature space
-    size_t m_dim_ru; // Rank of the U subspace
-    size_t m_dim_rv; // Rank of the V subspace
-
-    std::vector<blitz::Array<double,2> > m_x; // matrix x of speaker factors for eigenchannels U, for each client
-    std::vector<blitz::Array<double,1> > m_y; // vector y of spealer factors for eigenvoices V, for each client
-    std::vector<blitz::Array<double,1> > m_z; // vector z of spealer factors for eigenvoices Z, for each client
-
-    std::vector<blitz::Array<double,1> > m_Nacc; // Sum of the zeroth order statistics over the sessions for each client, dimension C
-    std::vector<blitz::Array<double,1> > m_Facc; // Sum of the first order statistics over the sessions for each client, dimension CD
-
-    // Accumulators for the M-step
-    blitz::Array<double,3> m_acc_V_A1;
-    blitz::Array<double,2> m_acc_V_A2;
-    blitz::Array<double,3> m_acc_U_A1;
-    blitz::Array<double,2> m_acc_U_A2;
-    blitz::Array<double,1> m_acc_D_A1;
-    blitz::Array<double,1> m_acc_D_A2;
-
-    // Cache/Precomputation
-    blitz::Array<double,2> m_cache_VtSigmaInv; // Vt * diag(sigma)^-1
-    blitz::Array<double,3> m_cache_VProd; // first dimension is the Gaussian id
-    blitz::Array<double,2> m_cache_IdPlusVProd_i;
-    blitz::Array<double,1> m_cache_Fn_y_i;
-
-    blitz::Array<double,2> m_cache_UtSigmaInv; // Ut * diag(sigma)^-1
-    blitz::Array<double,3> m_cache_UProd; // first dimension is the Gaussian id
-    blitz::Array<double,2> m_cache_IdPlusUProd_ih;
-    blitz::Array<double,1> m_cache_Fn_x_ih;
-
-    blitz::Array<double,1> m_cache_DtSigmaInv; // Dt * diag(sigma)^-1
-    blitz::Array<double,1> m_cache_DProd; // supervector length dimension
-    blitz::Array<double,1> m_cache_IdPlusDProd_i;
-    blitz::Array<double,1> m_cache_Fn_z_i;
-
-    // Working arrays
-    mutable blitz::Array<double,2> m_tmp_ruru;
-    mutable blitz::Array<double,2> m_tmp_ruD;
-    mutable blitz::Array<double,2> m_tmp_rvrv;
-    mutable blitz::Array<double,2> m_tmp_rvD;
-    mutable blitz::Array<double,1> m_tmp_rv;
-    mutable blitz::Array<double,1> m_tmp_ru;
-    mutable blitz::Array<double,1> m_tmp_CD;
-    mutable blitz::Array<double,1> m_tmp_CD_b;
-};
-
-
-} } } // namespaces
-
-#endif /* BOB_LEARN_EM_FABASETRAINER_H */
diff --git a/bob/learn/em/include/bob.learn.em/GMMBaseTrainer.h b/bob/learn/em/include/bob.learn.em/GMMBaseTrainer.h
deleted file mode 100644
index 9160c6b0a9b34b6480445c5175637de3970fd5c0..0000000000000000000000000000000000000000
--- a/bob/learn/em/include/bob.learn.em/GMMBaseTrainer.h
+++ /dev/null
@@ -1,161 +0,0 @@
-/**
- * @date Tue May 10 11:35:58 2011 +0200
- * @author Francois Moulin <Francois.Moulin@idiap.ch>
- *
- * @brief This class implements the E-step of the expectation-maximisation algorithm for a GMM Machine.
- * @details See Section 9.2.2 of Bishop, "Pattern recognition and machine learning", 2006
- *
- * Copyright (C) Idiap Research Institute, Martigny, Switzerland
- */
-
-#ifndef BOB_LEARN_EM_GMMBASETRAINER_H
-#define BOB_LEARN_EM_GMMBASETRAINER_H
-
-#include <bob.learn.em/GMMMachine.h>
-#include <bob.learn.em/GMMStats.h>
-#include <limits>
-
-namespace bob { namespace learn { namespace em {
-
-/**
- * @brief This class implements the E-step of the expectation-maximisation
- * algorithm for a GMM Machine.
- * @details See Section 9.2.2 of Bishop,
- *   "Pattern recognition and machine learning", 2006
- */
-class GMMBaseTrainer
-{
-  public:
-    /**
-     * @brief Default constructor
-     */
-    GMMBaseTrainer(const bool update_means=true,
-                   const bool update_variances=false,
-                   const bool update_weights=false,
-                   const double mean_var_update_responsibilities_threshold = std::numeric_limits<double>::epsilon());
-
-    /**
-     * @brief Copy constructor
-     */
-    GMMBaseTrainer(const GMMBaseTrainer& other);
-
-    /**
-     * @brief Destructor
-     */
-    virtual ~GMMBaseTrainer();
-
-    /**
-     * @brief Initialization before the EM steps
-     */
-    void initialize(bob::learn::em::GMMMachine& gmm);
-
-    /**
-     * @brief Calculates and saves statistics across the dataset,
-     * and saves these as m_ss. Calculates the average
-     * log likelihood of the observations given the GMM,
-     * and returns this in average_log_likelihood.
-     *
-     * The statistics, m_ss, will be used in the mStep() that follows.
-     * Implements EMTrainer::eStep(double &)
-     */
-     void eStep(bob::learn::em::GMMMachine& gmm,
-      const blitz::Array<double,2>& data);
-
-    /**
-     * @brief Computes the likelihood using current estimates of the latent
-     * variables
-     */
-    double computeLikelihood(bob::learn::em::GMMMachine& gmm);
-
-
-    /**
-     * @brief Assigns from a different GMMBaseTrainer
-     */
-    GMMBaseTrainer& operator=(const GMMBaseTrainer &other);
-
-    /**
-     * @brief Equal to
-     */
-    bool operator==(const GMMBaseTrainer& b) const;
-
-    /**
-     * @brief Not equal to
-     */
-    bool operator!=(const GMMBaseTrainer& b) const;
-
-    /**
-     * @brief Similar to
-     */
-    bool is_similar_to(const GMMBaseTrainer& b, const double r_epsilon=1e-5,
-      const double a_epsilon=1e-8) const;
-
-    /**
-     * @brief Returns the internal GMM statistics. Useful to parallelize the
-     * E-step
-     */
-    const boost::shared_ptr<bob::learn::em::GMMStats> getGMMStats() const
-    { return m_ss; }
-
-    /**
-     * @brief Sets the internal GMM statistics. Useful to parallelize the
-     * E-step
-     */
-    void setGMMStats(boost::shared_ptr<bob::learn::em::GMMStats> stats);
-
-    /**
-     * update means on each iteration
-     */
-    bool getUpdateMeans()
-    {return m_update_means;}
-
-    /**
-     * update variances on each iteration
-     */
-    bool getUpdateVariances()
-    {return m_update_variances;}
-
-
-    bool getUpdateWeights()
-    {return m_update_weights;}
-
-
-    double getMeanVarUpdateResponsibilitiesThreshold()
-    {return m_mean_var_update_responsibilities_threshold;}
-
-
-  private:
-
-    /**
-     * These are the sufficient statistics, calculated during the
-     * E-step and used during the M-step
-     */
-    boost::shared_ptr<bob::learn::em::GMMStats> m_ss;
-
-
-    /**
-     * update means on each iteration
-     */
-    bool m_update_means;
-
-    /**
-     * update variances on each iteration
-     */
-    bool m_update_variances;
-
-    /**
-     * update weights on each iteration
-     */
-    bool m_update_weights;
-
-    /**
-     * threshold over the responsibilities of the Gaussians
-     * Equations 9.24, 9.25 of Bishop, "Pattern recognition and machine learning", 2006
-     * require a division by the responsibilities, which might be equal to zero
-     * because of numerical issue. This threshold is used to avoid such divisions.
-     */
-    double m_mean_var_update_responsibilities_threshold;
-};
-
-} } } // namespaces
-
-#endif // BOB_LEARN_EM_GMMBASETRAINER_H
diff --git a/bob/learn/em/include/bob.learn.em/GMMMachine.h b/bob/learn/em/include/bob.learn.em/GMMMachine.h
deleted file mode 100644
index 0a46f822b22acce84d9faa3914729a1e48b7d115..0000000000000000000000000000000000000000
--- a/bob/learn/em/include/bob.learn.em/GMMMachine.h
+++ /dev/null
@@ -1,380 +0,0 @@
-/**
- * @date Tue May 10 11:35:58 2011 +0200
- * @author Francois Moulin <Francois.Moulin@idiap.ch>
- * @author Laurent El Shafey <Laurent.El-Shafey@idiap.ch>
- *
- * @brief This class implements a multivariate diagonal Gaussian distribution.
- * @details See Section 2.3.9 of Bishop, "Pattern recognition and machine learning", 2006
- *
- * Copyright (C) Idiap Research Institute, Martigny, Switzerland
- */
-
-#ifndef BOB_LEARN_EM_GMMMACHINE_H
-#define BOB_LEARN_EM_GMMMACHINE_H
-
-#include <bob.learn.em/Gaussian.h>
-#include <bob.learn.em/GMMStats.h>
-#include <bob.io.base/HDF5File.h>
-#include <iostream>
-#include <boost/shared_ptr.hpp>
-#include <vector>
-
-namespace bob { namespace learn { namespace em {
-
-/**
- * @brief This class implements a multivariate diagonal Gaussian distribution.
- * @details See Section 2.3.9 of Bishop, "Pattern recognition and machine learning", 2006
- */
-class GMMMachine
-{
-  public:
-    /**
-     * Default constructor
-     */
-    GMMMachine();
-
-    /**
-     * Constructor
-     * @param[in] n_gaussians  The number of Gaussian components
-     * @param[in] n_inputs     The feature dimensionality
-     */
-    GMMMachine(const size_t n_gaussians, const size_t n_inputs);
-
-    /**
-     * Copy constructor
-     * (Needed because the GMM points to its constituent Gaussian members)
-     */
-    GMMMachine(const GMMMachine& other);
-
-    /**
-     * Constructor from a Configuration
-     */
-    GMMMachine(bob::io::base::HDF5File& config);
-
-    /**
-     * Assignment
-     */
-    GMMMachine& operator=(const GMMMachine &other);
-
-    /**
-     * Equal to
-     */
-    bool operator==(const GMMMachine& b) const;
-
-    /**
-     * Not equal to
-     */
-    bool operator!=(const GMMMachine& b) const;
-
-    /**
-     * @brief Similar to
-     */
-    bool is_similar_to(const GMMMachine& b, const double r_epsilon=1e-5,
-      const double a_epsilon=1e-8) const;
-
-    /**
-     * Destructor
-     */
-    virtual ~GMMMachine();
-
-
-    /**
-     * Reset the input dimensionality, and the number of Gaussian components.
-     * Initialises the weights to uniform distribution.
-     * @param n_gaussians The number of Gaussian components
-     * @param n_inputs    The feature dimensionality
-     */
-    void resize(const size_t n_gaussians, const size_t n_inputs);
-
-
-    /////////////////////////
-    // Getters
-    ////////////////////////
-
-    /**
-     * Get number of inputs
-     */
-    size_t getNInputs() const
-    { return m_n_inputs; }
-
-    /**
-     * Get the weights ("mixing coefficients") of the Gaussian components
-     */
-    const blitz::Array<double,1>& getWeights() const
-    { return m_weights; }
-
-    /**
-     * Get the logarithm of the weights of the Gaussian components
-     */
-    inline const blitz::Array<double,1>& getLogWeights() const
-    { return m_cache_log_weights; }
-
-
-    /**
-     * Get the means
-     */    
-    const blitz::Array<double,2> getMeans() const;
-    
-    /**
-     * Get the mean supervector
-     */
-    void getMeanSupervector(blitz::Array<double,1> &mean_supervector) const;
-    
-     /**
-     * Returns a const reference to the supervector (Put in cache)
-     */
-    const blitz::Array<double,1>& getMeanSupervector() const;
-        
-    /**
-     * Get the variances
-     */
-    const blitz::Array<double,2> getVariances() const;
-    
-    /**
-     * Returns a const reference to the supervector (Put in cache)
-     */
-    const blitz::Array<double,1>& getVarianceSupervector() const;
-    
-
-    /**
-     * Get the variance flooring thresholds for each Gaussian in each dimension
-     */
-    const blitz::Array<double,2> getVarianceThresholds() const;
-
-
-
-    ///////////////////////
-    // Setters
-    ///////////////////////
-
-    /**
-     * Set the weights
-     */
-    void setWeights(const blitz::Array<double,1> &weights);
-
-    /**
-     * Set the means
-     */
-    void setMeans(const blitz::Array<double,2> &means);
-    /**
-     * Set the means from a supervector
-     */
-    void setMeanSupervector(const blitz::Array<double,1> &mean_supervector);
-
-    /**
-     * Set the variances
-     */
-    void setVariances(const blitz::Array<double,2> &variances);
-    /**
-     * Set the variances from a supervector
-     */
-    void setVarianceSupervector(const blitz::Array<double,1> &variance_supervector);
-
-    /**
-     * Set the variance flooring thresholds in each dimension
-     */
-    void setVarianceThresholds(const double value);
-    /**
-     * Set the variance flooring thresholds in each dimension
-     * (equal for all Gaussian components)
-     */
-    void setVarianceThresholds(blitz::Array<double,1> variance_thresholds);
-    /**
-     * Set the variance flooring thresholds for each Gaussian in each dimension
-     */
-    void setVarianceThresholds(const blitz::Array<double,2> &variance_thresholds);
-
-
-    ////////////////
-    // Methods
-    /////////////////
-
-    /**
-     * Get the weights in order to be updated
-     * ("mixing coefficients") of the Gaussian components
-     * @warning Only trainers should use this function for efficiency reason
-     */
-    inline blitz::Array<double,1>& updateWeights()
-    { return m_weights; }
-
-
-    /**
-     * Update the log of the weights in cache
-     * @warning Should be used by trainer only when using updateWeights()
-     */
-    void recomputeLogWeights() const;
-
-
-
-    /**
-     * Output the log likelihood of the sample, x, i.e. log(p(x|GMMMachine))
-     * @param[in]  x                                 The sample
-     * @param[out] log_weighted_gaussian_likelihoods For each Gaussian, i: log(weight_i*p(x|Gaussian_i))
-     * @return     The GMMMachine log likelihood, i.e. log(p(x|GMMMachine))
-     * Dimensions of the parameters are checked
-     */
-    double logLikelihood(const blitz::Array<double, 1> &x, blitz::Array<double,1> &log_weighted_gaussian_likelihoods) const;
-
-    /**
-     * Output the log likelihood of the sample, x, i.e. log(p(x|GMMMachine))
-     * @param[in]  x                                 The sample
-     * @param[out] log_weighted_gaussian_likelihoods For each Gaussian, i: log(weight_i*p(x|Gaussian_i))
-     * @return     The GMMMachine log likelihood, i.e. log(p(x|GMMMachine))
-     * @warning Dimensions of the parameters are not checked
-     */
-    double logLikelihood_(const blitz::Array<double, 1> &x, blitz::Array<double,1> &log_weighted_gaussian_likelihoods) const;
-
-    /**
-     * Output the log likelihood of the sample, x, i.e. log(p(x|GMM))
-     * @param[in]  x The sample
-     * Dimension of the input is checked
-     */
-    double logLikelihood(const blitz::Array<double, 1> &x) const;
-
-
-    /**
-     * Output the averaged log likelihood of a set of samples, x, i.e. log(p(x|GMM))
-     * @param[in]  x The sample
-     * Dimension of the input is checked
-     */
-    double logLikelihood(const blitz::Array<double, 2> &x) const;
-
-
-    /**
-     * Output the log likelihood of the sample, x, i.e. log(p(x|GMM))
-     * @param[in]  x The sample
-     * @warning Dimension of the input is not checked
-     */
-    double logLikelihood_(const blitz::Array<double, 1> &x) const;
-
-    /**
-     * Accumulates the GMM statistics over a set of samples.
-     * @see bool accStatistics(const blitz::Array<double,1> &x, GMMStats stats)
-     * Dimensions of the parameters are checked
-     */
-    void accStatistics(const blitz::Array<double,2>& input, GMMStats &stats) const;
-
-    /**
-     * Accumulates the GMM statistics over a set of samples.
-     * @see bool accStatistics(const blitz::Array<double,1> &x, GMMStats stats)
-     * @warning Dimensions of the parameters are not checked
-     */
-    void accStatistics_(const blitz::Array<double,2>& input, GMMStats &stats) const;
-
-    /**
-     * Accumulate the GMM statistics for this sample.
-     *
-     * @param[in]  x     The current sample
-     * @param[out] stats The accumulated statistics
-     * Dimensions of the parameters are checked
-     */
-    void accStatistics(const blitz::Array<double,1> &x, GMMStats &stats) const;
-
-    /**
-     * Accumulate the GMM statistics for this sample.
-     *
-     * @param[in]  x     The current sample
-     * @param[out] stats The accumulated statistics
-     * @warning Dimensions of the parameters are not checked
-     */
-    void accStatistics_(const blitz::Array<double,1> &x, GMMStats &stats) const;
-
-
-    /**
-     * Get a pointer to a particular Gaussian component
-     * @param[in] i The index of the Gaussian component
-     * @return A smart pointer to the i'th Gaussian component
-     *         if it exists, otherwise throws an exception
-     */
-    boost::shared_ptr<bob::learn::em::Gaussian> getGaussian(const size_t i);
-
-
-    /**
-     * Return the number of Gaussian components
-     */
-    inline size_t getNGaussians() const
-    { return m_n_gaussians; }
-
-    /**
-     * Save to a Configuration
-     */
-    void save(bob::io::base::HDF5File& config) const;
-
-    /**
-     * Load from a Configuration
-     */
-    void load(bob::io::base::HDF5File& config);
-
-    /**
-     * Load/Reload mean/variance supervector in cache
-     */
-    void reloadCacheSupervectors() const;
-
-    friend std::ostream& operator<<(std::ostream& os, const GMMMachine& machine);
-
-
-  private:
-    /**
-     * Copy another GMMMachine
-     */
-    void copy(const GMMMachine&);
-
-    /**
-     * The number of Gaussian components
-     */
-    size_t m_n_gaussians;
-
-    /**
-     * The feature dimensionality
-     */
-    size_t m_n_inputs;
-
-    /**
-     * The Gaussian components
-     */
-    std::vector<boost::shared_ptr<Gaussian> > m_gaussians;
-
-    /**
-     * The weights (also known as "mixing coefficients")
-     */
-    blitz::Array<double,1> m_weights;
-
-    /**
-     * Update the mean and variance supervectors
-     * in cache (into a 1D blitz array)
-     */
-    void updateCacheSupervectors() const;
-
-    /**
-     * Initialise the cache members (allocate arrays)
-     */
-    void initCache() const;
-
-    /**
-     * Accumulate the GMM statistics for this sample.
-     * Called by accStatistics() and accStatistics_()
-     *
-     * @param[in]  x     The current sample
-     * @param[out] stats The accumulated statistics
-     * @param[in]  log_likelihood  The current log_likelihood
-     * @warning Dimensions of the parameters are not checked
-     */
-    void accStatisticsInternal(const blitz::Array<double,1> &x,
-      GMMStats &stats, const double log_likelihood) const;
-
-
-    /// Some cache arrays to avoid re-allocation when computing log-likelihoods
-    mutable blitz::Array<double,1> m_cache_log_weights;
-    mutable blitz::Array<double,1> m_cache_log_weighted_gaussian_likelihoods;
-    mutable blitz::Array<double,1> m_cache_P;
-    mutable blitz::Array<double,2> m_cache_Px;
-
-    mutable blitz::Array<double,1> m_cache_mean_supervector;
-    mutable blitz::Array<double,1> m_cache_variance_supervector;
-    mutable bool m_cache_supervector;
-
-};
-
-} } } // namespaces
-
-#endif // BOB_LEARN_EM_GMMMACHINE_H
diff --git a/bob/learn/em/include/bob.learn.em/GMMStats.h b/bob/learn/em/include/bob.learn.em/GMMStats.h
deleted file mode 100644
index af56f05ce1d6a4285d4bd3d43f0ac7b9561214a9..0000000000000000000000000000000000000000
--- a/bob/learn/em/include/bob.learn.em/GMMStats.h
+++ /dev/null
@@ -1,141 +0,0 @@
-/**
- * @date Tue May 10 11:35:58 2011 +0200
- * @author Francois Moulin <Francois.Moulin@idiap.ch>
- * @author Laurent El Shafey <Laurent.El-Shafey@idiap.ch>
- *
- * Copyright (C) Idiap Research Institute, Martigny, Switzerland
- */
-
-#ifndef BOB_LEARN_EM_GMMSTATS_H
-#define BOB_LEARN_EM_GMMSTATS_H
-
-#include <blitz/array.h>
-#include <bob.io.base/HDF5File.h>
-
-namespace bob { namespace learn { namespace em {
-
-/**
- * @brief A container for GMM statistics.
- * @see GMMMachine
- *
- * With respect to Reynolds, "Speaker Verification Using Adapted
- * Gaussian Mixture Models", DSP, 2000:
- * Eq (8) is n(i)
- * Eq (9) is sumPx(i) / n(i)
- * Eq (10) is sumPxx(i) / n(i)
- */
-class GMMStats {
-  public:
-
-    /**
-     * Default constructor.
-     */
-    GMMStats();
-
-    /**
-     * Constructor.
-     * @param n_gaussians Number of Gaussians in the mixture model.
-     * @param n_inputs    Feature dimensionality.
-     */
-    GMMStats(const size_t n_gaussians, const size_t n_inputs);
-
-    /**
-     * Copy constructor
-     */
-    GMMStats(const GMMStats& other);
-
-    /**
-     * Constructor (from a Configuration)
-     */
-    GMMStats(bob::io::base::HDF5File& config);
-
-    /**
-     * Assigment
-     */
-    GMMStats& operator=(const GMMStats& other);
-
-    /**
-     * Equal to
-     */
-    bool operator==(const GMMStats& b) const;
-
-    /**
-     * Not Equal to
-     */
-    bool operator!=(const GMMStats& b) const;
-
-    /**
-     * @brief Similar to
-     */
-    bool is_similar_to(const GMMStats& b, const double r_epsilon=1e-5,
-      const double a_epsilon=1e-8) const;
-
-    /**
-     * Updates a GMMStats with another GMMStats
-     */
-    void operator+=(const GMMStats& b);
-
-    /**
-     * Destructor
-     */
-    ~GMMStats();
-
-    /**
-     * Allocates space for the statistics and resets to zero.
-     * @param n_gaussians Number of Gaussians in the mixture model.
-     * @param n_inputs    Feature dimensionality.
-     */
-    void resize(const size_t n_gaussians, const size_t n_inputs);
-
-    /**
-     * Resets statistics to zero.
-     */
-    void init();
-
-    /**
-     * The accumulated log likelihood of all samples
-     */
-    double log_likelihood;
-
-    /**
-     * The accumulated number of samples
-     */
-    size_t T;
-
-    /**
-     * For each Gaussian, the accumulated sum of responsibilities, i.e. the sum of P(gaussian_i|x)
-     */
-    blitz::Array<double,1> n;
-
-    /**
-     * For each Gaussian, the accumulated sum of responsibility times the sample
-     */
-    blitz::Array<double,2> sumPx;
-
-    /**
-     * For each Gaussian, the accumulated sum of responsibility times the sample squared
-     */
-    blitz::Array<double,2> sumPxx;
-
-    /**
-     * Save to a Configuration
-     */
-    void save(bob::io::base::HDF5File& config) const;
-
-    /**
-     * Load from a Configuration
-     */
-    void load(bob::io::base::HDF5File& config);
-
-    friend std::ostream& operator<<(std::ostream& os, const GMMStats& g);
-
-  private:
-    /**
-     * Copy another GMMStats
-     */
-    void copy(const GMMStats&);
-};
-
-} } } // namespaces
-
-#endif // BOB_LEARN_EM_GMMSTATS_H
diff --git a/bob/learn/em/include/bob.learn.em/Gaussian.h b/bob/learn/em/include/bob.learn.em/Gaussian.h
deleted file mode 100644
index 023f0080b1d2fe7a56c18e114b748d0ac629fcd6..0000000000000000000000000000000000000000
--- a/bob/learn/em/include/bob.learn.em/Gaussian.h
+++ /dev/null
@@ -1,247 +0,0 @@
-/**
- * @date Tue May 10 11:35:58 2011 +0200
- * @author Francois Moulin <Francois.Moulin@idiap.ch>
- * @author Laurent El Shafey <Laurent.El-Shafey@idiap.ch>
- *
- * Copyright (C) Idiap Research Institute, Martigny, Switzerland
- */
-
-#ifndef BOB_LEARN_EM_GAUSSIAN_H
-#define BOB_LEARN_EM_GAUSSIAN_H
-
-#include <bob.io.base/HDF5File.h>
-#include <blitz/array.h>
-#include <limits>
-
-namespace bob { namespace learn { namespace em {
-
-/**
- * @brief This class implements a multivariate diagonal Gaussian distribution.
- */
-class Gaussian
-{
-  public:
-    /**
-     * Default constructor
-     */
-    Gaussian();
-
-    /**
-     * Constructor
-     * @param[in] n_inputs The feature dimensionality
-     */
-    Gaussian(const size_t n_inputs);
-
-    /**
-     * Destructor
-     */
-    virtual ~Gaussian();
-
-    /**
-     * Copy constructor
-     */
-    Gaussian(const Gaussian& other);
-
-    /**
-     * Constructs from a configuration file
-     */
-    Gaussian(bob::io::base::HDF5File& config);
-
-    /**
-     * Assignment
-     */
-    Gaussian& operator=(const Gaussian &other);
-
-    /**
-     * Equal to
-     */
-    bool operator==(const Gaussian& b) const;
-    /**
-     * Not equal to
-     */
-    bool operator!=(const Gaussian& b) const;
-    /**
-     * @brief Similar to
-     */
-    bool is_similar_to(const Gaussian& b, const double r_epsilon=1e-5,
-      const double a_epsilon=1e-8) const;
-
-    /**
-     * Set the input dimensionality, reset the mean to zero
-     * and the variance to one.
-     * @see resize()
-     * @param n_inputs The feature dimensionality
-     * @warning The mean and variance are not initialized
-     */
-    void setNInputs(const size_t n_inputs);
-
-    /**
-     * Get the input dimensionality
-     */
-    size_t getNInputs() const
-    { return m_n_inputs; }
-
-    /**
-     * Set the input dimensionality, reset the mean to zero
-     * and the variance to one.
-     * @see setNInputs()
-     * @param n_inputs The feature dimensionality
-     */
-    void resize(const size_t n_inputs);
-
-    /**
-     * Get the mean
-     */
-    inline const blitz::Array<double,1>& getMean() const
-    { return m_mean; }
-
-    /**
-     * Get the mean in order to be updated
-     * @warning Only trainers should use this function for efficiency reason
-     */
-    inline blitz::Array<double,1>& updateMean()
-    { return m_mean; }
-
-    /**
-     * Set the mean
-     */
-    void setMean(const blitz::Array<double,1> &mean);
-
-    /**
-     * Get the variance (the diagonal of the covariance matrix)
-     */
-    inline const blitz::Array<double,1>& getVariance() const
-    { return m_variance; }
-
-    /**
-     * Get the variance in order to be updated
-     * @warning Only trainers should use this function for efficiency reason
-     */
-    inline blitz::Array<double,1>& updateVariance()
-    { return m_variance; }
-
-    /**
-     * Set the variance
-     */
-    void setVariance(const blitz::Array<double,1> &variance);
-
-    /**
-     * Get the variance flooring thresholds
-     */
-    const blitz::Array<double,1>& getVarianceThresholds() const
-    { return m_variance_thresholds; }
-
-    /**
-     * Get the variance thresholds in order to be updated
-     * @warning Only trainers should use this function for efficiency reason
-     */
-    inline blitz::Array<double,1>& updateVarianceThreshods()
-    { return m_variance_thresholds; }
-
-    /**
-     * Set the variance flooring thresholds
-     */
-    void setVarianceThresholds(const blitz::Array<double,1> &variance_thresholds);
-
-    /**
-     * Set the variance flooring thresholds
-     */
-    void setVarianceThresholds(const double value);
-
-    /**
-     * Apply the variance flooring thresholds
-     * This method is called when using setVarianceThresholds()
-     * @warning It is only useful when using updateVarianceThreshods(),
-     * and should mostly be done by trainers
-     */
-    void applyVarianceThresholds();
-
-    /**
-     * Output the log likelihood of the sample, x
-     * @param x The data sample (feature vector)
-     */
-    double logLikelihood(const blitz::Array<double,1>& x) const;
-
-    /**
-     * Output the log likelihood of the sample, x
-     * @param x The data sample (feature vector)
-     * @warning The input is NOT checked
-     */
-    double logLikelihood_(const blitz::Array<double,1>& x) const;
-
-    /**
-     * Saves to a Configuration
-     */
-    void save(bob::io::base::HDF5File& config) const;
-
-    /**
-     * Loads from a Configuration
-     */
-    void load(bob::io::base::HDF5File& config);
-
-    /**
-     * Prints a Gaussian in the output stream
-     */
-    friend std::ostream& operator<<(std::ostream& os, const bob::learn::em::Gaussian& g);
-
-
-  private:
-    /**
-     * Copies another Gaussian
-     */
-    void copy(const Gaussian& other);
-
-    /**
-     * Computes n_inputs * log(2*pi)
-     */
-    void preComputeNLog2Pi();
-
-    /**
-     * Computes and stores the value of g_norm,
-     * to later speed up evaluation of logLikelihood()
-     * Note: g_norm is defined as follows:
-     * log(Gaussian pdf) = log(1/((2pi)^(k/2)(det)^(1/2)) * exp(...))
-     *                   = -1/2 * g_norm * (...)
-     */
-    void preComputeConstants();
-
-    /**
-     * The mean vector of the Gaussian
-     */
-    blitz::Array<double,1> m_mean;
-
-    /**
-     * The diagonal of the covariance matrix (assumed to be diagonal)
-     */
-    blitz::Array<double,1> m_variance;
-
-    /**
-     * The variance flooring thresholds, i.e. the minimum allowed
-     * value of variance in each dimension.
-     * The variance will be set to this value if an attempt is made
-     * to set it to a smaller value.
-     */
-    blitz::Array<double,1> m_variance_thresholds;
-
-    /**
-     * A constant that depends only on the feature dimensionality
-     * m_n_log2pi = n_inputs * log(2*pi) (used to compute m_gnorm)
-     */
-    double m_n_log2pi;
-
-    /**
-     * A constant that depends only on the feature dimensionality
-     * (m_n_inputs) and the variance
-     * @see bool preComputeConstants()
-     */
-    double m_g_norm;
-
-    /**
-     * The number of inputs (feature dimensionality)
-     */
-    size_t m_n_inputs;
-};
-
-} } } // namespaces
-
-#endif // BOB_LEARN_EM_GAUSSIAN_H
diff --git a/bob/learn/em/include/bob.learn.em/ISVBase.h b/bob/learn/em/include/bob.learn.em/ISVBase.h
deleted file mode 100644
index 477d1972e44a59e6f8d38705256e71162ab46353..0000000000000000000000000000000000000000
--- a/bob/learn/em/include/bob.learn.em/ISVBase.h
+++ /dev/null
@@ -1,228 +0,0 @@
-/**
- * @date Tue Jan 27 16:02:00 2015 +0200
- * @author Laurent El Shafey <Laurent.El-Shafey@idiap.ch>
- * @author Tiago de Freitas Pereira <tiago.pereira@idiap.ch>
- *
- * @brief A base class for Joint Factor Analysis-like machines
- *
- * Copyright (C) Idiap Research Institute, Martigny, Switzerland
- */
-
-#ifndef BOB_LEARN_EM_ISVBASE_H
-#define BOB_LEARN_EM_ISVBASE_H
-
-#include <stdexcept>
-
-#include <bob.learn.em/GMMMachine.h>
-#include <bob.learn.em/FABase.h>
-
-#include <bob.io.base/HDF5File.h>
-#include <boost/shared_ptr.hpp>
-
-namespace bob { namespace learn { namespace em {
-
-
-/**
- * @brief An ISV Base class which contains U and D matrices
- * TODO: add a reference to the journal articles
- */
-class ISVBase
-{
-  public:
-    /**
-     * @brief Default constructor. Builds an otherwise invalid 0 x 0 ISVBase
-     * The Universal Background Model and the matrices U, V and diag(d) are
-     * not initialized.
-     */
-    ISVBase();
-
-    /**
-     * @brief Constructor. Builds a new ISVBase.
-     * The Universal Background Model and the matrices U, V and diag(d) are
-     * not initialized.
-     *
-     * @param ubm The Universal Background Model
-     * @param ru size of U (CD x ru)
-     * @warning ru SHOULD BE >= 1.
-     */
-    ISVBase(const boost::shared_ptr<bob::learn::em::GMMMachine> ubm, const size_t ru=1);
-
-    /**
-     * @brief Copy constructor
-     */
-    ISVBase(const ISVBase& other);
-
-    /**
-     * @deprecated Starts a new JFAMachine from an existing Configuration object.
-     */
-    ISVBase(bob::io::base::HDF5File& config);
-
-    /**
-     * @brief Just to virtualise the destructor
-     */
-    virtual ~ISVBase();
-
-    /**
-     * @brief Assigns from a different JFA machine
-     */
-    ISVBase& operator=(const ISVBase &other);
-
-    /**
-     * @brief Equal to
-     */
-    bool operator==(const ISVBase& b) const
-    { return m_base.operator==(b.m_base); }
-
-    /**
-     * @brief Not equal to
-     */
-    bool operator!=(const ISVBase& b) const
-    { return m_base.operator!=(b.m_base); }
-
-    /**
-     * @brief Similar to
-     */
-    bool is_similar_to(const ISVBase& b, const double r_epsilon=1e-5,
-      const double a_epsilon=1e-8) const
-    { return m_base.is_similar_to(b.m_base, r_epsilon, a_epsilon); }
-
-    /**
-     * @brief Saves machine to an HDF5 file
-     */
-    void save(bob::io::base::HDF5File& config) const;
-
-    /**
-     * @brief Loads data from an existing configuration object. Resets
-     * the current state.
-     */
-    void load(bob::io::base::HDF5File& config);
-
-    /**
-     * @brief Returns the UBM
-     */
-    const boost::shared_ptr<bob::learn::em::GMMMachine> getUbm() const
-    { return m_base.getUbm(); }
-
-    /**
-     * @brief Returns the U matrix
-     */
-    const blitz::Array<double,2>& getU() const
-    { return m_base.getU(); }
-
-    /**
-     * @brief Returns the diagonal matrix diag(d) (as a 1D vector)
-     */
-    const blitz::Array<double,1>& getD() const
-    { return m_base.getD(); }
-
-    /**
-     * @brief Returns the number of Gaussian components C
-     * @warning An exception is thrown if no Universal Background Model has
-     *   been set yet.
-     */
-    const size_t getNGaussians() const
-    { return m_base.getNGaussians(); }
-
-    /**
-     * @brief Returns the feature dimensionality D
-     * @warning An exception is thrown if no Universal Background Model has
-     *   been set yet.
-     */
-    const size_t getNInputs() const
-    { return m_base.getNInputs(); }
-
-    /**
-     * @brief Returns the supervector length CD
-     * (CxD: Number of Gaussian components by the feature dimensionality)
-     * @warning An exception is thrown if no Universal Background Model has
-     *   been set yet.
-     */
-    const size_t getSupervectorLength() const
-    { return m_base.getSupervectorLength(); }
-
-    /**
-     * @brief Returns the size/rank ru of the U matrix
-     */
-    const size_t getDimRu() const
-    { return m_base.getDimRu(); }
-
-    /**
-     * @brief Resets the dimensionality of the subspace U
-     * U is hence uninitialized.
-     */
-    void resize(const size_t ru)
-    { m_base.resize(ru, 1);
-      blitz::Array<double,2>& V = m_base.updateV();
-      V = 0;
-     }
-
-    /**
-     * @brief Returns the U matrix in order to update it
-     * @warning Should only be used by the trainer for efficiency reason,
-     *   or for testing purpose.
-     */
-    blitz::Array<double,2>& updateU()
-    { return m_base.updateU(); }
-
-    /**
-     * @brief Returns the diagonal matrix diag(d) (as a 1D vector) in order
-     * to update it
-     * @warning Should only be used by the trainer for efficiency reason,
-     *   or for testing purpose.
-     */
-    blitz::Array<double,1>& updateD()
-    { return m_base.updateD(); }
-
-
-    /**
-     * @brief Sets (the mean supervector of) the Universal Background Model
-     * U, V and d are uninitialized in case of dimensions update (C or D)
-     */
-    void setUbm(const boost::shared_ptr<bob::learn::em::GMMMachine> ubm)
-    { m_base.setUbm(ubm); }
-
-    /**
-     * @brief Sets the U matrix
-     */
-    void setU(const blitz::Array<double,2>& U)
-    { m_base.setU(U); }
-
-    /**
-     * @brief Sets the diagonal matrix diag(d)
-     * (a 1D vector is expected as an argument)
-     */
-    void setD(const blitz::Array<double,1>& d)
-    { m_base.setD(d); }
-
-    /**
-     * @brief Estimates x from the GMM statistics considering the LPT
-     * assumption, that is the latent session variable x is approximated
-     * using the UBM
-     */
-    void estimateX(const bob::learn::em::GMMStats& gmm_stats, blitz::Array<double,1>& x) const
-    { m_base.estimateX(gmm_stats, x); }
-
-    /**
-     * @brief Precompute (put U^{T}.Sigma^{-1} matrix in cache)
-     * @warning Should only be used by the trainer for efficiency reason,
-     *   or for testing purpose.
-     */
-    void precompute()
-    { m_base.updateCacheUbmUVD(); }
-
-    /**
-     * @brief Returns the FABase member
-     */
-    const bob::learn::em::FABase& getBase() const
-    { return m_base; }
-
-
-  private:
-    // FABase
-    bob::learn::em::FABase m_base;
-};
-
-
-} } } // namespaces
-
-#endif // BOB_LEARN_EM_JFABASE_H
diff --git a/bob/learn/em/include/bob.learn.em/ISVMachine.h b/bob/learn/em/include/bob.learn.em/ISVMachine.h
deleted file mode 100644
index f51f4dbf008638e91cd3cc850ba0f9f9e6cff172..0000000000000000000000000000000000000000
--- a/bob/learn/em/include/bob.learn.em/ISVMachine.h
+++ /dev/null
@@ -1,237 +0,0 @@
-/**
- * @date Tue Jan 27 16:06:00 2015 +0200
- * @author Laurent El Shafey <Laurent.El-Shafey@idiap.ch>
- * @author Tiago de Freitas Pereira <tiago.pereira@idiap.ch>
- *
- * @brief A base class for Joint Factor Analysis-like machines
- *
- * Copyright (C) Idiap Research Institute, Martigny, Switzerland
- */
-
-#ifndef BOB_LEARN_EM_ISVMACHINE_H
-#define BOB_LEARN_EM_ISVMACHINE_H
-
-#include <stdexcept>
-
-#include <bob.learn.em/ISVBase.h>
-#include <bob.learn.em/GMMMachine.h>
-#include <bob.learn.em/LinearScoring.h>
-
-#include <bob.io.base/HDF5File.h>
-#include <boost/shared_ptr.hpp>
-
-namespace bob { namespace learn { namespace em {
-
-
-/**
- * @brief A ISVMachine which is associated to a ISVBase that contains
- *   U D matrices.
- * TODO: add a reference to the journal articles
- */
-class ISVMachine
-{
-  public:
-    /**
-     * @brief Default constructor. Builds an otherwise invalid 0 x 0 ISVMachine
-     * The Universal Background Model and the matrices U, V and diag(d) are
-     * not initialized.
-     */
-    ISVMachine();
-
-    /**
-     * @brief Constructor. Builds a new ISVMachine.
-     *
-     * @param isv_base The ISVBase associated with this machine
-     */
-    ISVMachine(const boost::shared_ptr<bob::learn::em::ISVBase> isv_base);
-
-    /**
-     * @brief Copy constructor
-     */
-    ISVMachine(const ISVMachine& other);
-
-    /**
-     * @brief Starts a new ISVMachine from an existing Configuration object.
-     */
-    ISVMachine(bob::io::base::HDF5File& config);
-
-    /**
-     * @brief Just to virtualise the destructor
-     */
-    virtual ~ISVMachine();
-
-    /**
-     * @brief Assigns from a different ISV machine
-     */
-    ISVMachine& operator=(const ISVMachine &other);
-
-    /**
-     * @brief Equal to
-     */
-    bool operator==(const ISVMachine& b) const;
-
-    /**
-     * @brief Not equal to
-     */
-    bool operator!=(const ISVMachine& b) const;
-
-    /**
-     * @brief Similar to
-     */
-    bool is_similar_to(const ISVMachine& b, const double r_epsilon=1e-5,
-      const double a_epsilon=1e-8) const;
-
-    /**
-     * @brief Saves machine to an HDF5 file
-     */
-    void save(bob::io::base::HDF5File& config) const;
-
-    /**
-     * @brief Loads data from an existing configuration object. Resets
-     * the current state.
-     */
-    void load(bob::io::base::HDF5File& config);
-
-
-    /**
-     * @brief Returns the number of Gaussian components C
-     * @warning An exception is thrown if no Universal Background Model has
-     *   been set yet.
-     */
-    const size_t getNGaussians() const
-    { return m_isv_base->getNGaussians(); }
-
-    /**
-     * @brief Returns the feature dimensionality D
-     * @warning An exception is thrown if no Universal Background Model has
-     *   been set yet.
-     */
-    const size_t getNInputs() const
-    { return m_isv_base->getNInputs(); }
-
-    /**
-     * @brief Returns the supervector length CD
-     * (CxD: Number of Gaussian components by the feature dimensionality)
-     * @warning An exception is thrown if no Universal Background Model has
-     *   been set yet.
-     */
-    const size_t getSupervectorLength() const
-    { return m_isv_base->getSupervectorLength(); }
-
-    /**
-     * @brief Returns the size/rank ru of the U matrix
-     */
-    const size_t getDimRu() const
-    { return m_isv_base->getDimRu(); }
-
-    /**
-     * @brief Returns the x session factor
-     */
-    const blitz::Array<double,1>& getX() const
-    { return m_cache_x; }
-
-    /**
-     * @brief Returns the z speaker factor
-     */
-    const blitz::Array<double,1>& getZ() const
-    { return m_z; }
-
-    /**
-     * @brief Returns the z speaker factors in order to update it
-     */
-    blitz::Array<double,1>& updateZ()
-    { return m_z; }
-
-    /**
-     * @brief Returns the V matrix
-     */
-    void setZ(const blitz::Array<double,1>& z);
-
-
-    /**
-     * @brief Sets the session variable
-     */
-    void setX(const blitz::Array<double,1>& x);
-
-
-    /**
-     * @brief Returns the ISVBase
-     */
-    const boost::shared_ptr<bob::learn::em::ISVBase> getISVBase() const
-    { return m_isv_base; }
-
-    /**
-     * @brief Sets the ISVBase
-     */
-    void setISVBase(const boost::shared_ptr<bob::learn::em::ISVBase> isv_base);
-
-
-    /**
-     * @brief Estimates x from the GMM statistics considering the LPT
-     * assumption, that is the latent session variable x is approximated
-     * using the UBM
-     */
-    void estimateX(const bob::learn::em::GMMStats& gmm_stats, blitz::Array<double,1>& x) const
-    { m_isv_base->estimateX(gmm_stats, x); }
-    /**
-     * @brief Estimates Ux from the GMM statistics considering the LPT
-     * assumption, that is the latent session variable x is approximated
-     * using the UBM
-     */
-    void estimateUx(const bob::learn::em::GMMStats& gmm_stats, blitz::Array<double,1>& Ux);
-
-   /**
-    * @brief Execute the machine
-    *
-    * @param input input data used by the machine
-    * @warning Inputs are checked
-    * @return score value computed by the machine
-    */
-    double forward(const bob::learn::em::GMMStats& input);
-    /**
-     * @brief Computes a score for the given UBM statistics and given the
-     * Ux vector
-     */
-    double forward(const bob::learn::em::GMMStats& gmm_stats,
-      const blitz::Array<double,1>& Ux);
-
-    /**
-     * @brief Execute the machine
-     *
-     * @param input input data used by the machine
-     * @warning Inputs are NOT checked
-     * @return score value computed by the machine
-     */
-    double forward_(const bob::learn::em::GMMStats& input);
-
-  private:
-    /**
-     * @brief Resize latent variable according to the ISVBase
-     */
-    void resize();
-    /**
-     * @ Update cache
-     */
-    void updateCache();
-    /**
-     * @brief Resize working arrays
-     */
-    void resizeTmp();
-
-    // UBM
-    boost::shared_ptr<bob::learn::em::ISVBase> m_isv_base;
-
-    // y and z vectors/factors learned during the enrollment procedure
-    blitz::Array<double,1> m_z;
-
-    // cache
-    blitz::Array<double,1> m_cache_mDz;
-    mutable blitz::Array<double,1> m_cache_x;
-
-    // x vector/factor in cache when computing scores
-    mutable blitz::Array<double,1> m_tmp_Ux;
-};
-
-} } } // namespaces
-
-#endif // BOB_LEARN_EM_ISVMACHINE_H
diff --git a/bob/learn/em/include/bob.learn.em/ISVTrainer.h b/bob/learn/em/include/bob.learn.em/ISVTrainer.h
deleted file mode 100644
index ae8f0087aa8fe9526719bc8c49d750fc291e2a9b..0000000000000000000000000000000000000000
--- a/bob/learn/em/include/bob.learn.em/ISVTrainer.h
+++ /dev/null
@@ -1,166 +0,0 @@
-/**
- * @date Tue Jul 19 12:16:17 2011 +0200
- * @author Laurent El Shafey <Laurent.El-Shafey@idiap.ch>
- *
- * @brief JFA functions
- *
- * Copyright (C) Idiap Research Institute, Martigny, Switzerland
- */
-
-#ifndef BOB_LEARN_EM_ISVTRAINER_H
-#define BOB_LEARN_EM_ISVTRAINER_H
-
-#include <blitz/array.h>
-#include <bob.learn.em/GMMStats.h>
-#include <bob.learn.em/FABaseTrainer.h>
-#include <bob.learn.em/ISVMachine.h>
-#include <vector>
-
-#include <map>
-#include <string>
-#include <bob.core/array_copy.h>
-#include <boost/shared_ptr.hpp>
-#include <boost/random.hpp>
-#include <bob.core/logging.h>
-
-namespace bob { namespace learn { namespace em {
-
-class ISVTrainer
-{
-  public:
-    /**
-     * @brief Constructor
-     */
-    ISVTrainer(const double relevance_factor=4.);
-
-    /**
-     * @brief Copy onstructor
-     */
-    ISVTrainer(const ISVTrainer& other);
-
-    /**
-     * @brief Destructor
-     */
-    virtual ~ISVTrainer();
-
-    /**
-     * @brief Assignment operator
-     */
-    ISVTrainer& operator=(const ISVTrainer& other);
-
-    /**
-     * @brief Equal to
-     */
-    bool operator==(const ISVTrainer& b) const;
-
-    /**
-     * @brief Not equal to
-     */
-    bool operator!=(const ISVTrainer& b) const;
-
-    /**
-     * @brief Similar to
-     */
-    bool is_similar_to(const ISVTrainer& b, const double r_epsilon=1e-5,
-      const double a_epsilon=1e-8) const;
-
-    /**
-     * @brief This methods performs some initialization before the EM loop.
-     */
-    virtual void initialize(bob::learn::em::ISVBase& machine,
-      const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& ar);
-
-    /**
-     * @brief Calculates and saves statistics across the dataset
-     * The statistics will be used in the mStep() that follows.
-     */
-    virtual void eStep(bob::learn::em::ISVBase& machine,
-      const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& ar);
-
-    /**
-     * @brief Performs a maximization step to update the parameters of the
-     * factor analysis model.
-     */
-    virtual void mStep(bob::learn::em::ISVBase& machine);
-
-    /**
-     * @brief Computes the average log likelihood using the current estimates
-     * of the latent variables.
-     */
-    virtual double computeLikelihood(bob::learn::em::ISVBase& machine);
-
-    /**
-     * @brief Enrol a client
-     */
-    void enroll(bob::learn::em::ISVMachine& machine,
-      const std::vector<boost::shared_ptr<bob::learn::em::GMMStats> >& features,
-      const size_t n_iter);
-
-    /**
-     * @brief Get the x speaker factors
-     */
-    const std::vector<blitz::Array<double,2> >& getX() const
-    { return m_base_trainer.getX(); }
-    /**
-     * @brief Get the z speaker factors
-     */
-    const std::vector<blitz::Array<double,1> >& getZ() const
-    { return m_base_trainer.getZ(); }
-    /**
-     * @brief Set the x speaker factors
-     */
-    void setX(const std::vector<blitz::Array<double,2> >& X)
-    { m_base_trainer.setX(X); }
-    /**
-     * @brief Set the z speaker factors
-     */
-    void setZ(const std::vector<blitz::Array<double,1> >& z)
-    { m_base_trainer.setZ(z); }
-
-    /**
-     * @brief Getters for the accumulators
-     */
-    const blitz::Array<double,3>& getAccUA1() const
-    { return m_base_trainer.getAccUA1(); }
-    const blitz::Array<double,2>& getAccUA2() const
-    { return m_base_trainer.getAccUA2(); }
-
-    /**
-     * @brief Setters for the accumulators, Very useful if the e-Step needs
-     * to be parallelized.
-     */
-    void setAccUA1(const blitz::Array<double,3>& acc)
-    { m_base_trainer.setAccUA1(acc); }
-    void setAccUA2(const blitz::Array<double,2>& acc)
-    { m_base_trainer.setAccUA2(acc); }
-
-    /**
-     * @brief Sets the Random Number Generator
-     */
-    void setRng(const boost::shared_ptr<boost::mt19937> rng)
-    { m_rng = rng; }
-
-    /**
-     * @brief Gets the Random Number Generator
-     */
-    const boost::shared_ptr<boost::mt19937> getRng() const
-    { return m_rng; }
-
-
-  private:
-    /**
-     * @brief Initialize D to sqrt(ubm_var/relevance_factor)
-     */
-    void initializeD(bob::learn::em::ISVBase& machine) const;
-
-    // Attributes
-    bob::learn::em::FABaseTrainer m_base_trainer;
-
-    double m_relevance_factor;
-
-    boost::shared_ptr<boost::mt19937> m_rng; ///< The random number generator for the inialization};
-};
-
-} } } // namespaces
-
-#endif /* BOB_LEARN_EM_ISVTRAINER_H */
diff --git a/bob/learn/em/include/bob.learn.em/IVectorMachine.h b/bob/learn/em/include/bob.learn.em/IVectorMachine.h
deleted file mode 100644
index 59c67493a44bfb048feb27dba39b3d18ed0b90bc..0000000000000000000000000000000000000000
--- a/bob/learn/em/include/bob.learn.em/IVectorMachine.h
+++ /dev/null
@@ -1,274 +0,0 @@
-/**
- * @date Sat Mar 30 20:55:00 2013 +0200
- * @author Laurent El Shafey <Laurent.El-Shafey@idiap.ch>
- *
- * Copyright (C) Idiap Research Institute, Martigny, Switzerland
- */
-
-#ifndef BOB_LEARN_EM_IVECTOR_MACHINE_H
-#define BOB_LEARN_EM_IVECTOR_MACHINE_H
-
-#include <blitz/array.h>
-#include <bob.learn.em/GMMMachine.h>
-#include <bob.learn.em/GMMStats.h>
-#include <bob.io.base/HDF5File.h>
-
-namespace bob { namespace learn { namespace em {
-
-/**
- * @brief An IVectorMachine consists of a Total Variability subspace \f$T\f$
- *   and allows the extraction of IVector\n
- * Reference:\n
- * "Front-End Factor Analysis For Speaker Verification",
- *    N. Dehak, P. Kenny, R. Dehak, P. Dumouchel, P. Ouellet,
- *   IEEE Trans. on Audio, Speech and Language Processing
- */
-class IVectorMachine
-{
-  public:
-    /**
-     * @brief Default constructor. Builds an IVectorMachine.
-     * The Universal Background Model and the matrices \f$T\f$ and
-     * \f$diag(\Sigma)\f$ are not initialized.
-     */
-    IVectorMachine();
-
-    /**
-     * @brief Constructor. Builds a new IVectorMachine.
-     * The Universal Background Model and the matrices \f$T\f$ and
-     * \f$diag(\Sigma)\f$ are not initialized.
-     *
-     * @param ubm The Universal Background Model
-     * @param rt size of \f$T\f$ (CD x rt)
-     * @param variance_threshold variance flooring threshold for the
-     *   \f$\Sigma\f$ (diagonal) matrix
-     * @warning rt SHOULD BE >= 1.
-     */
-    IVectorMachine(const boost::shared_ptr<bob::learn::em::GMMMachine> ubm,
-      const size_t rt=1, const double variance_threshold=1e-10);
-
-    /**
-     * @brief Copy constructor
-     */
-    IVectorMachine(const IVectorMachine& other);
-
-    /**
-     * @brief Starts a new IVectorMachine from an existing Configuration object.
-     */
-    IVectorMachine(bob::io::base::HDF5File& config);
-
-    /**
-     * @brief Destructor
-     */
-    virtual ~IVectorMachine();
-
-    /**
-     * @brief Assigns from a different IVectorMachine
-     */
-    IVectorMachine& operator=(const IVectorMachine &other);
-
-    /**
-     * @brief Equal to
-     */
-    bool operator==(const IVectorMachine& b) const;
-
-    /**
-     * @brief Not equal to
-     */
-    bool operator!=(const IVectorMachine& b) const;
-
-    /**
-     * @brief Similar to
-     */
-    bool is_similar_to(const IVectorMachine& b, const double r_epsilon=1e-5,
-      const double a_epsilon=1e-8) const;
-
-    /**
-     * @brief Saves model to an HDF5 file
-     */
-    void save(bob::io::base::HDF5File& config) const;
-
-    /**
-     * @brief Loads data from an existing configuration object. Resets
-     * the current state.
-     */
-    void load(bob::io::base::HDF5File& config);
-
-    /**
-     * @brief Returns the UBM
-     */
-    const boost::shared_ptr<bob::learn::em::GMMMachine> getUbm() const
-    { return m_ubm; }
-
-    /**
-     * @brief Returns the \f$T\f$ matrix
-     */
-    const blitz::Array<double,2>& getT() const
-    { return m_T; }
-
-    /**
-     * @brief Returns the \f$\Sigma\f$ (diagonal) matrix as a 1D array
-     */
-    const blitz::Array<double,1>& getSigma() const
-    { return m_sigma; }
-
-    /**
-     * @brief Gets the variance flooring threshold
-     */
-    const double getVarianceThreshold() const
-    { return m_variance_threshold; }
-
-    /**
-     * @brief Returns the number of Gaussian components C.
-     * @warning An exception is thrown if no Universal Background Model has
-     *   been set yet.
-     */
-    const size_t getNGaussians() const
-    { return m_ubm->getNGaussians(); }
-
-    /**
-     * @brief Returns the feature dimensionality D.
-     * @warning An exception is thrown if no Universal Background Model has
-     *   been set yet.
-     */
-    const size_t getNInputs() const
-    { return m_ubm->getNInputs(); }
-
-    /**
-     * @brief Returns the supervector length CD.
-     * (CxD: Number of Gaussian components by the feature dimensionality)
-     * @warning An exception is thrown if no Universal Background Model has
-     *   been set yet.
-     */
-    const size_t getSupervectorLength() const
-    { return m_ubm->getNGaussians()*m_ubm->getNInputs(); }
-
-    /**
-     * @brief Returns the size/rank rt of the \f$T\f$ matrix
-     */
-    const size_t getDimRt() const
-    { return m_rt; }
-
-    /**
-     * @brief Resets the dimensionality of the subspace \f$T\f$.
-     * \f$T\f$ is hence uninitialized.
-     */
-    void resize(const size_t rt);
-
-    /**
-     * @brief Returns the \f$T\f$ matrix in order to update it.
-     * @warning Should only be used by the trainer for efficiency reason,
-     *   or for testing purpose.
-     */
-    blitz::Array<double,2>& updateT()
-    { return m_T; }
-
-    /**
-     * @brief Returns the \f$\Sigma\f$ (diagonal) matrix in order to update it.
-     * @warning Should only be used by the trainer for efficiency reason,
-     *   or for testing purpose.
-     */
-    blitz::Array<double,1>& updateSigma()
-    { return m_sigma; }
-
-    /**
-     * @brief Sets (the mean supervector of) the Universal Background Model.
-     * \f$T\f$ and \f$\Sigma\f$ are uninitialized in case of dimensions update (C or D)
-     */
-    void setUbm(const boost::shared_ptr<bob::learn::em::GMMMachine> ubm);
-
-    /**
-     * @brief Sets the \f$T\f$ matrix
-     */
-    void setT(const blitz::Array<double,2>& T);
-
-    /**
-     * @brief Sets the \f$\Sigma\f$ (diagonal) matrix
-     */
-    void setSigma(const blitz::Array<double,1>& sigma);
-
-    /**
-     * @brief Set the variance flooring threshold
-     */
-    void setVarianceThreshold(const double value);
-
-    /**
-     * @brief Update arrays in cache
-     * @warning It is only useful when using updateT() or updateSigma()
-     * and should mostly be done by trainers
-     */
-    void precompute();
-
-    /**
-     * @brief Computes \f$(Id + \sum_{c=1}^{C} N_{i,j,c} T^{T} \Sigma_{c}^{-1} T)\f$
-     * @warning No check is perform
-     */
-    void computeIdTtSigmaInvT(const bob::learn::em::GMMStats& input, blitz::Array<double,2>& output) const;
-
-    /**
-     * @brief Computes \f$T^{T} \Sigma^{-1} \sum_{c=1}^{C} (F_c - N_c ubmmean_{c})\f$
-     * @warning No check is perform
-     */
-    void computeTtSigmaInvFnorm(const bob::learn::em::GMMStats& input, blitz::Array<double,1>& output) const;
-
-    /**
-     * @brief Extracts an ivector from the input GMM statistics
-     *
-     * @param input GMM statistics to be used by the machine
-     * @param output I-vector computed by the machine
-     */
-    void forward(const bob::learn::em::GMMStats& input, blitz::Array<double,1>& output) const;
-
-    /**
-     * @brief Extracts an ivector from the input GMM statistics
-     *
-     * @param input GMM statistics to be used by the machine
-     * @param output I-vector computed by the machine
-     * @warning Inputs are NOT checked
-     */
-    void forward_(const bob::learn::em::GMMStats& input, blitz::Array<double,1>& output) const;
-
-  private:
-    /**
-     * @brief Apply the variance flooring thresholds.
-     * This method is called when using setVarianceThresholds()
-     */
-    void applyVarianceThreshold();
-
-    /**
-     * @brief Resize cache
-     */
-    void resizeCache();
-    /**
-     * @brief Resize working arrays
-     */
-    void resizeTmp();
-    /**
-     * @brief Resize cache and working arrays before updating cache
-     */
-    void resizePrecompute();
-
-    // UBM
-    boost::shared_ptr<bob::learn::em::GMMMachine> m_ubm;
-
-    // dimensionality
-    size_t m_rt; ///< size of \f$T\f$ (CD x rt)
-
-    ///< \f$T\f$ and \f$Sigma\f$ matrices.
-    ///< \f$Sigma\f$ is assumed to be diagonal, and only the diagonal is stored
-    blitz::Array<double,2> m_T; ///< The total variability matrix \f$T\f$
-    blitz::Array<double,1> m_sigma; ///< The diagonal covariance matrix \f$\Sigma\f$
-    double m_variance_threshold; ///< The variance flooring threshold
-
-    blitz::Array<double,3> m_cache_Tct_sigmacInv;
-    blitz::Array<double,3> m_cache_Tct_sigmacInv_Tc;
-
-    mutable blitz::Array<double,1> m_tmp_d;
-    mutable blitz::Array<double,1> m_tmp_t1;
-    mutable blitz::Array<double,1> m_tmp_t2;
-    mutable blitz::Array<double,2> m_tmp_tt;
-};
-
-} } } // namespaces
-
-#endif // BOB_LEARN_EM_IVECTOR_MACHINE_H
diff --git a/bob/learn/em/include/bob.learn.em/IVectorTrainer.h b/bob/learn/em/include/bob.learn.em/IVectorTrainer.h
deleted file mode 100644
index 0482f1513116289ecf1242bfa20d8e4856d4c7db..0000000000000000000000000000000000000000
--- a/bob/learn/em/include/bob.learn.em/IVectorTrainer.h
+++ /dev/null
@@ -1,162 +0,0 @@
-/**
- * @date Sat Mar 30 20:55:00 2013 +0200
- * @author Laurent El Shafey <Laurent.El-Shafey@idiap.ch>
- *
- * Copyright (C) Idiap Research Institute, Martigny, Switzerland
- */
-
-#ifndef BOB_LEARN_EM_IVECTOR_TRAINER_H
-#define BOB_LEARN_EM_IVECTOR_TRAINER_H
-
-#include <blitz/array.h>
-#include <bob.learn.em/IVectorMachine.h>
-#include <bob.learn.em/GMMStats.h>
-#include <boost/shared_ptr.hpp>
-#include <vector>
-#include <bob.core/array_copy.h>
-#include <boost/random.hpp>
-
-#include <boost/random/mersenne_twister.hpp>
-
-namespace bob { namespace learn { namespace em {
-
-/**
- * @brief An IVectorTrainer to learn a Total Variability subspace \f$T\f$
- *  (and eventually a covariance matrix \f$\Sigma\f$).\n
- * Reference:\n
- * "Front-End Factor Analysis For Speaker Verification",
- *    N. Dehak, P. Kenny, R. Dehak, P. Dumouchel, P. Ouellet,
- *   IEEE Trans. on Audio, Speech and Language Processing
- */
-class IVectorTrainer
-{
-  public:
-    /**
-     * @brief Default constructor. Builds an IVectorTrainer
-     */
-    IVectorTrainer(const bool update_sigma=false);
-
-    /**
-     * @brief Copy constructor
-     */
-    IVectorTrainer(const IVectorTrainer& other);
-
-    /**
-     * @brief Destructor
-     */
-    virtual ~IVectorTrainer();
-
-    /**
-     * @brief Initialization before the EM loop
-     */
-    virtual void initialize(bob::learn::em::IVectorMachine& ivector);
-
-    /**
-     * @brief Reset the statistics accumulators
-     * to the correct size and a value of zero.
-     */
-    void resetAccumulators(const bob::learn::em::IVectorMachine& ivector);
-
-    /**
-     * @brief Calculates statistics across the dataset,
-     * and saves these as:
-     * - m_acc_Nij_wij2
-     * - m_acc_Fnormij_wij
-     * - m_acc_Nij (only if update_sigma is enabled)
-     * - m_acc_Snormij (only if update_sigma is enabled)
-     *
-     * These statistics will be used in the mStep() that follows.
-     */
-    virtual void eStep(bob::learn::em::IVectorMachine& ivector,
-      const std::vector<bob::learn::em::GMMStats>& data);
-
-    /**
-     * @brief Maximisation step: Update the Total Variability matrix \f$T\f$
-     * and \f$\Sigma\f$ if update_sigma is enabled.
-     */
-    virtual void mStep(bob::learn::em::IVectorMachine& ivector);
-
-
-    /**
-     * @brief Assigns from a different IVectorTrainer
-     */
-    IVectorTrainer& operator=(const IVectorTrainer &other);
-
-    /**
-     * @brief Equal to
-     */
-    bool operator==(const IVectorTrainer& b) const;
-
-    /**
-     * @brief Not equal to
-     */
-    bool operator!=(const IVectorTrainer& b) const;
-
-    /**
-     * @brief Similar to
-     */
-    bool is_similar_to(const IVectorTrainer& b, const double r_epsilon=1e-5,
-      const double a_epsilon=1e-8) const;
-
-    /**
-     * @brief Getters for the accumulators
-     */
-    const blitz::Array<double,3>& getAccNijWij2() const
-    { return m_acc_Nij_wij2; }
-    const blitz::Array<double,3>& getAccFnormijWij() const
-    { return m_acc_Fnormij_wij; }
-    const blitz::Array<double,1>& getAccNij() const
-    { return m_acc_Nij; }
-    const blitz::Array<double,2>& getAccSnormij() const
-    { return m_acc_Snormij; }
-
-    /**
-     * @brief Setters for the accumulators, Very useful if the e-Step needs
-     * to be parallelized.
-     */
-    void setAccNijWij2(const blitz::Array<double,3>& acc)
-    { bob::core::array::assertSameShape(acc, m_acc_Nij_wij2);
-      m_acc_Nij_wij2 = acc; }
-    void setAccFnormijWij(const blitz::Array<double,3>& acc)
-    { bob::core::array::assertSameShape(acc, m_acc_Fnormij_wij);
-      m_acc_Fnormij_wij = acc; }
-    void setAccNij(const blitz::Array<double,1>& acc)
-    { bob::core::array::assertSameShape(acc, m_acc_Nij);
-      m_acc_Nij = acc; }
-    void setAccSnormij(const blitz::Array<double,2>& acc)
-    { bob::core::array::assertSameShape(acc, m_acc_Snormij);
-      m_acc_Snormij = acc; }
-
-    void setRng(boost::shared_ptr<boost::mt19937> rng){
-      m_rng = rng;
-    };
-
-  protected:
-    // Attributes
-    bool m_update_sigma;
-
-    // Acccumulators
-    blitz::Array<double,3> m_acc_Nij_wij2;
-    blitz::Array<double,3> m_acc_Fnormij_wij;
-    blitz::Array<double,1> m_acc_Nij;
-    blitz::Array<double,2> m_acc_Snormij;
-
-    // Working arrays
-    mutable blitz::Array<double,1> m_tmp_wij;
-    mutable blitz::Array<double,2> m_tmp_wij2;
-    mutable blitz::Array<double,1> m_tmp_d1;
-    mutable blitz::Array<double,1> m_tmp_t1;
-    mutable blitz::Array<double,2> m_tmp_dd1;
-    mutable blitz::Array<double,2> m_tmp_dt1;
-    mutable blitz::Array<double,2> m_tmp_tt1;
-    mutable blitz::Array<double,2> m_tmp_tt2;
-
-    /**
-     * @brief The random number generator for the inialization
-     */
-    boost::shared_ptr<boost::mt19937> m_rng;
-};
-
-} } } // namespaces
-
-#endif // BOB_LEARN_EM_IVECTOR_TRAINER_H
diff --git a/bob/learn/em/include/bob.learn.em/JFABase.h b/bob/learn/em/include/bob.learn.em/JFABase.h
deleted file mode 100644
index c75cec864187c46a1648e6a7288188c1a2754929..0000000000000000000000000000000000000000
--- a/bob/learn/em/include/bob.learn.em/JFABase.h
+++ /dev/null
@@ -1,253 +0,0 @@
-/**
- * @date Tue Jan 27 15:54:00 2015 +0200
- * @author Laurent El Shafey <Laurent.El-Shafey@idiap.ch>
- * @author Tiago de Freitas Pereira <tiago.pereira@idiap.ch>
- *
- * @brief A base class for Joint Factor Analysis-like machines
- *
- * Copyright (C) Idiap Research Institute, Martigny, Switzerland
- */
-
-#ifndef BOB_LEARN_EM_JFABASE_H
-#define BOB_LEARN_EM_JFABASE_H
-
-#include <stdexcept>
-
-#include <bob.learn.em/GMMMachine.h>
-#include <bob.learn.em/FABase.h>
-//#include <bob.learn.em/LinearScoring.h>
-
-#include <bob.io.base/HDF5File.h>
-#include <boost/shared_ptr.hpp>
-
-namespace bob { namespace learn { namespace em {
-
-
-/**
- * @brief A JFA Base class which contains U, V and D matrices
- * TODO: add a reference to the journal articles
- */
-class JFABase
-{
-  public:
-    /**
-     * @brief Default constructor. Builds a 1 x 1 JFABase
-     * The Universal Background Model and the matrices U, V and diag(d) are
-     * not initialized.
-     */
-    JFABase();
-
-    /**
-     * @brief Constructor. Builds a new JFABase.
-     * The Universal Background Model and the matrices U, V and diag(d) are
-     * not initialized.
-     *
-     * @param ubm The Universal Background Model
-     * @param ru size of U (CD x ru)
-     * @param rv size of U (CD x rv)
-     * @warning ru and rv SHOULD BE  >= 1.
-     */
-    JFABase(const boost::shared_ptr<bob::learn::em::GMMMachine> ubm, const size_t ru=1, const size_t rv=1);
-
-    /**
-     * @brief Copy constructor
-     */
-    JFABase(const JFABase& other);
-
-    /**
-     * @deprecated Starts a new JFAMachine from an existing Configuration object.
-     */
-    JFABase(bob::io::base::HDF5File& config);
-
-    /**
-     * @brief Just to virtualise the destructor
-     */
-    virtual ~JFABase();
-
-    /**
-     * @brief Assigns from a different JFA machine
-     */
-    JFABase& operator=(const JFABase &other);
-
-    /**
-     * @brief Equal to
-     */
-    bool operator==(const JFABase& b) const
-    { return m_base.operator==(b.m_base); }
-
-    /**
-     * @brief Not equal to
-     */
-    bool operator!=(const JFABase& b) const
-    { return m_base.operator!=(b.m_base); }
-
-    /**
-     * @brief Similar to
-     */
-    bool is_similar_to(const JFABase& b, const double r_epsilon=1e-5,
-      const double a_epsilon=1e-8) const
-    { return m_base.is_similar_to(b.m_base, r_epsilon, a_epsilon); }
-
-    /**
-     * @brief Saves model to an HDF5 file
-     */
-    void save(bob::io::base::HDF5File& config) const;
-
-    /**
-     * @brief Loads data from an existing configuration object. Resets
-     * the current state.
-     */
-    void load(bob::io::base::HDF5File& config);
-
-    /**
-     * @brief Returns the UBM
-     */
-    const boost::shared_ptr<bob::learn::em::GMMMachine> getUbm() const
-    { return m_base.getUbm(); }
-
-    /**
-     * @brief Returns the U matrix
-     */
-    const blitz::Array<double,2>& getU() const
-    { return m_base.getU(); }
-
-    /**
-     * @brief Returns the V matrix
-     */
-    const blitz::Array<double,2>& getV() const
-    { return m_base.getV(); }
-
-    /**
-     * @brief Returns the diagonal matrix diag(d) (as a 1D vector)
-     */
-    const blitz::Array<double,1>& getD() const
-    { return m_base.getD(); }
-
-    /**
-     * @brief Returns the number of Gaussian components
-     * @warning An exception is thrown if no Universal Background Model has
-     *   been set yet.
-     */
-    const size_t getNGaussians() const
-    { return m_base.getNGaussians();}
-
-    /**
-     * @brief Returns the feature dimensionality D
-     * @warning An exception is thrown if no Universal Background Model has
-     *   been set yet.
-     */
-    const size_t getNInputs() const
-    { return m_base.getNInputs(); }
-
-    /**
-     * @brief Returns the supervector length CD
-     * (CxD: Number of Gaussian components by the feature dimensionality)
-     * @warning An exception is thrown if no Universal Background Model has
-     *   been set yet.
-     */
-    const size_t getSupervectorLength() const
-    { return m_base.getSupervectorLength(); }
-
-    /**
-     * @brief Returns the size/rank ru of the U matrix
-     */
-    const size_t getDimRu() const
-    { return m_base.getDimRu(); }
-
-    /**
-     * @brief Returns the size/rank rv of the V matrix
-     */
-    const size_t getDimRv() const
-    { return m_base.getDimRv(); }
-
-    /**
-     * @brief Resets the dimensionality of the subspace U and V
-     * U and V are hence uninitialized.
-     */
-    void resize(const size_t ru, const size_t rv)
-    { m_base.resize(ru, rv); }
-
-    /**
-     * @brief Returns the U matrix in order to update it
-     * @warning Should only be used by the trainer for efficiency reason,
-     *   or for testing purpose.
-     */
-    blitz::Array<double,2>& updateU()
-    { return m_base.updateU(); }
-
-    /**
-     * @brief Returns the V matrix in order to update it
-     * @warning Should only be used by the trainer for efficiency reason,
-     *   or for testing purpose.
-     */
-    blitz::Array<double,2>& updateV()
-    { return m_base.updateV(); }
-
-    /**
-     * @brief Returns the diagonal matrix diag(d) (as a 1D vector) in order
-     * to update it
-     * @warning Should only be used by the trainer for efficiency reason,
-     *   or for testing purpose.
-     */
-    blitz::Array<double,1>& updateD()
-    { return m_base.updateD(); }
-
-
-    /**
-     * @brief Sets (the mean supervector of) the Universal Background Model
-     * U, V and d are uninitialized in case of dimensions update (C or D)
-     */
-    void setUbm(const boost::shared_ptr<bob::learn::em::GMMMachine> ubm)
-    { m_base.setUbm(ubm); }
-
-    /**
-     * @brief Sets the U matrix
-     */
-    void setU(const blitz::Array<double,2>& U)
-    { m_base.setU(U); }
-
-    /**
-     * @brief Sets the V matrix
-     */
-    void setV(const blitz::Array<double,2>& V)
-    { m_base.setV(V); }
-
-    /**
-     * @brief Sets the diagonal matrix diag(d)
-     * (a 1D vector is expected as an argument)
-     */
-    void setD(const blitz::Array<double,1>& d)
-    { m_base.setD(d); }
-
-    /**
-     * @brief Estimates x from the GMM statistics considering the LPT
-     * assumption, that is the latent session variable x is approximated
-     * using the UBM
-     */
-    void estimateX(const bob::learn::em::GMMStats& gmm_stats, blitz::Array<double,1>& x) const
-    { m_base.estimateX(gmm_stats, x); }
-
-    /**
-     * @brief Precompute (put U^{T}.Sigma^{-1} matrix in cache)
-     * @warning Should only be used by the trainer for efficiency reason,
-     *   or for testing purpose.
-     */
-    void precompute()
-    { m_base.updateCacheUbmUVD(); }
-
-    /**
-     * @brief Returns the FABase member
-     */
-    const bob::learn::em::FABase& getBase() const
-    { return m_base; }
-
-
-  private:
-    // FABase
-    bob::learn::em::FABase m_base;
-};
-
-
-} } } // namespaces
-
-#endif // BOB_LEARN_EM_JFABASE_H
diff --git a/bob/learn/em/include/bob.learn.em/JFAMachine.h b/bob/learn/em/include/bob.learn.em/JFAMachine.h
deleted file mode 100644
index 51649fef10d089d4055b87f33ba3a0d53e861335..0000000000000000000000000000000000000000
--- a/bob/learn/em/include/bob.learn.em/JFAMachine.h
+++ /dev/null
@@ -1,254 +0,0 @@
-/**
- * @date Tue Jan 27 16:47:00 2015 +0200
- * @author Laurent El Shafey <Laurent.El-Shafey@idiap.ch>
- * @author Tiago de Freitas Pereira <tiago.pereira@idiap.ch>
- *
- * @brief A base class for Joint Factor Analysis-like machines
- *
- * Copyright (C) Idiap Research Institute, Martigny, Switzerland
- */
-
-#ifndef BOB_LEARN_EM_JFAMACHINE_H
-#define BOB_LEARN_EM_JFAMACHINE_H
-
-#include <stdexcept>
-
-#include <bob.learn.em/JFABase.h>
-#include <bob.learn.em/GMMMachine.h>
-#include <bob.learn.em/LinearScoring.h>
-
-#include <bob.io.base/HDF5File.h>
-#include <boost/shared_ptr.hpp>
-
-namespace bob { namespace learn { namespace em {
-
-
-/**
- * @brief A JFAMachine which is associated to a JFABase that contains
- *   U, V and D matrices. The JFAMachine describes the identity part
- *   (latent variables y and z)
- * TODO: add a reference to the journal articles
- */
-class JFAMachine
-{
-  public:
-    /**
-     * @brief Default constructor. Builds an otherwise invalid 0 x 0 JFAMachine
-     * The Universal Background Model and the matrices U, V and diag(d) are
-     * not initialized.
-     */
-    JFAMachine();
-
-    /**
-     * @brief Constructor. Builds a new JFAMachine.
-     *
-     * @param jfa_base The JFABase associated with this machine
-     */
-    JFAMachine(const boost::shared_ptr<bob::learn::em::JFABase> jfa_base);
-
-    /**
-     * @brief Copy constructor
-     */
-    JFAMachine(const JFAMachine& other);
-
-    /**
-     * @deprecated Starts a new JFAMachine from an existing Configuration object.
-     */
-    JFAMachine(bob::io::base::HDF5File& config);
-
-    /**
-     * @brief Just to virtualise the destructor
-     */
-    virtual ~JFAMachine();
-
-    /**
-     * @brief Assigns from a different JFA machine
-     */
-    JFAMachine& operator=(const JFAMachine &other);
-
-    /**
-     * @brief Equal to
-     */
-    bool operator==(const JFAMachine& b) const;
-
-    /**
-     * @brief Not equal to
-     */
-    bool operator!=(const JFAMachine& b) const;
-
-    /**
-     * @brief Similar to
-     */
-    bool is_similar_to(const JFAMachine& b, const double r_epsilon=1e-5,
-      const double a_epsilon=1e-8) const;
-
-    /**
-     * @brief Saves machine to an HDF5 file
-     */
-    void save(bob::io::base::HDF5File& config) const;
-
-    /**
-     * @brief Loads data from an existing configuration object. Resets
-     * the current state.
-     */
-    void load(bob::io::base::HDF5File& config);
-
-    /**
-     * @brief Returns the number of Gaussian components C
-     * @warning An exception is thrown if no Universal Background Model has
-     *   been set yet.
-     */
-    const size_t getNGaussians() const
-    { return m_jfa_base->getNGaussians(); }
-
-    /**
-     * @brief Returns the feature dimensionality D
-     * @warning An exception is thrown if no Universal Background Model has
-     *   been set yet.
-     */
-    const size_t getNInputs() const
-    { return m_jfa_base->getNInputs(); }
-
-    /**
-     * @brief Returns the supervector length CD
-     * (CxD: Number of Gaussian components by the feature dimensionality)
-     * @warning An exception is thrown if no Universal Background Model has
-     *   been set yet.
-     */
-    const size_t getSupervectorLength() const
-    { return m_jfa_base->getSupervectorLength(); }
-
-    /**
-     * @brief Returns the size/rank ru of the U matrix
-     */
-    const size_t getDimRu() const
-    { return m_jfa_base->getDimRu(); }
-
-    /**
-     * @brief Returns the size/rank rv of the V matrix
-     */
-    const size_t getDimRv() const
-    { return m_jfa_base->getDimRv(); }
-
-    /**
-     * @brief Returns the x session factor
-     */
-    const blitz::Array<double,1>& getX() const
-    { return m_cache_x; }
-
-    /**
-     * @brief Returns the y speaker factor
-     */
-    const blitz::Array<double,1>& getY() const
-    { return m_y; }
-
-    /**
-     * @brief Returns the z speaker factor
-     */
-    const blitz::Array<double,1>& getZ() const
-    { return m_z; }
-
-    /**
-     * @brief Returns the y speaker factors in order to update it
-     */
-    blitz::Array<double,1>& updateY()
-    { return m_y; }
-
-    /**
-     * @brief Returns the z speaker factors in order to update it
-     */
-    blitz::Array<double,1>& updateZ()
-    { return m_z; }
-
-    /**
-     * @brief Returns the y speaker factors
-     */
-    void setY(const blitz::Array<double,1>& y);
-
-    /**
-     * @brief Returns the V matrix
-     */
-    void setZ(const blitz::Array<double,1>& z);
-
-    /**
-     * @brief Returns the JFABase
-     */
-    const boost::shared_ptr<bob::learn::em::JFABase> getJFABase() const
-    { return m_jfa_base; }
-
-    /**
-     * @brief Sets the JFABase
-     */
-    void setJFABase(const boost::shared_ptr<bob::learn::em::JFABase> jfa_base);
-
-
-    /**
-     * @brief Estimates x from the GMM statistics considering the LPT
-     * assumption, that is the latent session variable x is approximated
-     * using the UBM
-     */
-    void estimateX(const bob::learn::em::GMMStats& gmm_stats, blitz::Array<double,1>& x) const
-    { m_jfa_base->estimateX(gmm_stats, x); }
-    /**
-     * @brief Estimates Ux from the GMM statistics considering the LPT
-     * assumption, that is the latent session variable x is approximated
-     * using the UBM
-     */
-    void estimateUx(const bob::learn::em::GMMStats& gmm_stats, blitz::Array<double,1>& Ux);
-
-   /**
-    * @brief Execute the machine
-    *
-    * @param input input data used by the machine
-    * @warning Inputs are checked
-    * @return score value computed by the machine
-    */
-    double forward(const bob::learn::em::GMMStats& input);
-    /**
-     * @brief Computes a score for the given UBM statistics and given the
-     * Ux vector
-     */
-    double forward(const bob::learn::em::GMMStats& gmm_stats,
-      const blitz::Array<double,1>& Ux);
-
-    /**
-     * @brief Execute the machine
-     *
-     * @param input input data used by the machine
-     * @param score value computed by the machine
-     * @warning Inputs are NOT checked
-     */
-    double forward_(const bob::learn::em::GMMStats& input);
-
-  private:
-    /**
-     * @brief Resize latent variable according to the JFABase
-     */
-    void resize();
-    /**
-     * @brief Resize working arrays
-     */
-    void resizeTmp();
-    /**
-     * @brief Update the cache
-     */
-    void updateCache();
-
-    // UBM
-    boost::shared_ptr<bob::learn::em::JFABase> m_jfa_base;
-
-    // y and z vectors/factors learned during the enrollment procedure
-    blitz::Array<double,1> m_y;
-    blitz::Array<double,1> m_z;
-
-    // cache
-    blitz::Array<double,1> m_cache_mVyDz;
-    mutable blitz::Array<double,1> m_cache_x;
-
-    // x vector/factor in cache when computing scores
-    mutable blitz::Array<double,1> m_tmp_Ux;
-};
-
-} } } // namespaces
-
-#endif // BOB_LEARN_EM_JFAMACHINE_H
diff --git a/bob/learn/em/include/bob.learn.em/JFATrainer.h b/bob/learn/em/include/bob.learn.em/JFATrainer.h
deleted file mode 100644
index d697442edceb4f970bb97309d36abda00ce38e23..0000000000000000000000000000000000000000
--- a/bob/learn/em/include/bob.learn.em/JFATrainer.h
+++ /dev/null
@@ -1,238 +0,0 @@
-/**
- * @date Tue Jul 19 12:16:17 2011 +0200
- * @author Laurent El Shafey <Laurent.El-Shafey@idiap.ch>
- *
- * @brief JFA functions
- *
- * Copyright (C) Idiap Research Institute, Martigny, Switzerland
- */
-
-#ifndef BOB_LEARN_EM_JFATRAINER_H
-#define BOB_LEARN_EM_JFATRAINER_H
-
-#include <blitz/array.h>
-#include <bob.learn.em/GMMStats.h>
-#include <bob.learn.em/FABaseTrainer.h>
-#include <bob.learn.em/JFAMachine.h>
-#include <vector>
-
-#include <map>
-#include <string>
-#include <bob.core/array_copy.h>
-#include <boost/shared_ptr.hpp>
-#include <boost/random.hpp>
-#include <bob.core/logging.h>
-
-namespace bob { namespace learn { namespace em {
-
-class JFATrainer
-{
-  public:
-    /**
-     * @brief Constructor
-     */
-    JFATrainer();
-
-    /**
-     * @brief Copy onstructor
-     */
-    JFATrainer(const JFATrainer& other);
-
-    /**
-     * @brief Destructor
-     */
-    virtual ~JFATrainer();
-
-    /**
-     * @brief Assignment operator
-     */
-    JFATrainer& operator=(const JFATrainer& other);
-
-    /**
-     * @brief Equal to
-     */
-    bool operator==(const JFATrainer& b) const;
-
-    /**
-     * @brief Not equal to
-     */
-    bool operator!=(const JFATrainer& b) const;
-
-    /**
-     * @brief Similar to
-     */
-    bool is_similar_to(const JFATrainer& b, const double r_epsilon=1e-5,
-      const double a_epsilon=1e-8) const;
-
-    /**
-     * @brief Sets the maximum number of EM-like iterations (for each subspace)
-     */
-    //void setMaxIterations(const size_t max_iterations)
-    //{ m_max_iterations = max_iterations; }
-
-    /**
-     * @brief Gets the maximum number of EM-like iterations (for each subspace)
-     */
-    //size_t getMaxIterations() const
-    //{ return m_max_iterations; }
-
-    /**
-     * @brief This methods performs some initialization before the EM loop.
-     */
-    virtual void initialize(bob::learn::em::JFABase& machine,
-      const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& ar);
-
-    /**
-     * @brief This methods performs the e-Step to train the first subspace V
-     */
-    virtual void eStep1(bob::learn::em::JFABase& machine,
-      const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& ar);
-    /**
-     * @brief This methods performs the m-Step to train the first subspace V
-     */
-    virtual void mStep1(bob::learn::em::JFABase& machine,
-      const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& ar);
-    /**
-     * @brief This methods performs the finalization after training the first
-     * subspace V
-     */
-    virtual void finalize1(bob::learn::em::JFABase& machine,
-      const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& ar);
-    /**
-     * @brief This methods performs the e-Step to train the second subspace U
-     */
-    virtual void eStep2(bob::learn::em::JFABase& machine,
-      const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& ar);
-    /**
-     * @brief This methods performs the m-Step to train the second subspace U
-     */
-    virtual void mStep2(bob::learn::em::JFABase& machine,
-      const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& ar);
-    /**
-     * @brief This methods performs the finalization after training the second
-     * subspace U
-     */
-    virtual void finalize2(bob::learn::em::JFABase& machine,
-      const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& ar);
-    /**
-     * @brief This methods performs the e-Step to train the third subspace d
-     */
-    virtual void eStep3(bob::learn::em::JFABase& machine,
-      const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& ar);
-    /**
-     * @brief This methods performs the m-Step to train the third subspace d
-     */
-    virtual void mStep3(bob::learn::em::JFABase& machine,
-      const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& ar);
-    /**
-     * @brief This methods performs the finalization after training the third
-     * subspace d
-     */
-    virtual void finalize3(bob::learn::em::JFABase& machine,
-      const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& ar);
-
-    /**
-     * @brief This methods performs the main loops to train the subspaces U, V and d
-     */
-    //virtual void train_loop(bob::learn::em::JFABase& machine,
-      //const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& ar);
-    /**
-     * @brief This methods trains the subspaces U, V and d
-     */
-    //virtual void train(bob::learn::em::JFABase& machine,
-      //const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& ar);
-
-    /**
-     * @brief Enrol a client
-     */
-    void enroll(bob::learn::em::JFAMachine& machine,
-      const std::vector<boost::shared_ptr<bob::learn::em::GMMStats> >& features,
-      const size_t n_iter);
-
-    /**
-     * @brief Sets the Random Number Generator
-     */
-    void setRng(const boost::shared_ptr<boost::mt19937> rng)
-    { m_rng = rng; }
-
-    /**
-     * @brief Gets the Random Number Generator
-     */
-    const boost::shared_ptr<boost::mt19937> getRng() const
-    { return m_rng; }
-
-    /**
-     * @brief Get the x speaker factors
-     */
-    const std::vector<blitz::Array<double,2> >& getX() const
-    { return m_base_trainer.getX(); }
-    /**
-     * @brief Get the y speaker factors
-     */
-    const std::vector<blitz::Array<double,1> >& getY() const
-    { return m_base_trainer.getY(); }
-    /**
-     * @brief Get the z speaker factors
-     */
-    const std::vector<blitz::Array<double,1> >& getZ() const
-    { return m_base_trainer.getZ(); }
-    /**
-     * @brief Set the x speaker factors
-     */
-    void setX(const std::vector<blitz::Array<double,2> >& X)
-    { m_base_trainer.setX(X); }
-    /**
-     * @brief Set the y speaker factors
-     */
-    void setY(const std::vector<blitz::Array<double,1> >& y)
-    { m_base_trainer.setY(y); }
-    /**
-     * @brief Set the z speaker factors
-     */
-    void setZ(const std::vector<blitz::Array<double,1> >& z)
-    { m_base_trainer.setZ(z); }
-
-    /**
-     * @brief Getters for the accumulators
-     */
-    const blitz::Array<double,3>& getAccVA1() const
-    { return m_base_trainer.getAccVA1(); }
-    const blitz::Array<double,2>& getAccVA2() const
-    { return m_base_trainer.getAccVA2(); }
-    const blitz::Array<double,3>& getAccUA1() const
-    { return m_base_trainer.getAccUA1(); }
-    const blitz::Array<double,2>& getAccUA2() const
-    { return m_base_trainer.getAccUA2(); }
-    const blitz::Array<double,1>& getAccDA1() const
-    { return m_base_trainer.getAccDA1(); }
-    const blitz::Array<double,1>& getAccDA2() const
-    { return m_base_trainer.getAccDA2(); }
-
-    /**
-     * @brief Setters for the accumulators, Very useful if the e-Step needs
-     * to be parallelized.
-     */
-    void setAccVA1(const blitz::Array<double,3>& acc)
-    { m_base_trainer.setAccVA1(acc); }
-    void setAccVA2(const blitz::Array<double,2>& acc)
-    { m_base_trainer.setAccVA2(acc); }
-    void setAccUA1(const blitz::Array<double,3>& acc)
-    { m_base_trainer.setAccUA1(acc); }
-    void setAccUA2(const blitz::Array<double,2>& acc)
-    { m_base_trainer.setAccUA2(acc); }
-    void setAccDA1(const blitz::Array<double,1>& acc)
-    { m_base_trainer.setAccDA1(acc); }
-    void setAccDA2(const blitz::Array<double,1>& acc)
-    { m_base_trainer.setAccDA2(acc); }
-
-
-  private:
-    // Attributes
-    //size_t m_max_iterations;
-    boost::shared_ptr<boost::mt19937> m_rng; ///< The random number generator for the inialization
-    bob::learn::em::FABaseTrainer m_base_trainer;
-};
-
-} } } // namespaces
-
-#endif /* BOB_LEARN_EM_JFATRAINER_H */
diff --git a/bob/learn/em/include/bob.learn.em/KMeansMachine.h b/bob/learn/em/include/bob.learn.em/KMeansMachine.h
deleted file mode 100644
index 2c8113a6a06644b7af2f6839e76106da8de930bf..0000000000000000000000000000000000000000
--- a/bob/learn/em/include/bob.learn.em/KMeansMachine.h
+++ /dev/null
@@ -1,244 +0,0 @@
-/**
- * @date Tue May 10 11:35:58 2011 +0200
- * @author Francois Moulin <Francois.Moulin@idiap.ch>
- * @author Laurent El Shafey <Laurent.El-Shafey@idiap.ch>
- *
- * Copyright (C) Idiap Research Institute, Martigny, Switzerland
- */
-#ifndef BOB_LEARN_EM_KMEANSMACHINE_H
-#define BOB_LEARN_EM_KMEANSMACHINE_H
-
-#include <blitz/array.h>
-#include <cfloat>
-
-#include <bob.io.base/HDF5File.h>
-
-namespace bob { namespace learn { namespace em {
-
-/**
- * @brief This class implements a k-means classifier.
- * @details See Section 9.1 of Bishop, "Pattern recognition and machine learning", 2006
- */
-class KMeansMachine {
-  public:
-    /**
-     * Default constructor. Builds an otherwise invalid 0 x 0 k-means
-     * machine. This is equivalent to construct a LinearMachine with two
-     * size_t parameters set to 0, as in LinearMachine(0, 0).
-     */
-    KMeansMachine();
-
-    /**
-     * Constructor
-     * @param[in] n_means  The number of means
-     * @param[in] n_inputs The feature dimensionality
-     */
-    KMeansMachine(const size_t n_means, const size_t n_inputs);
-
-    /**
-     * Builds a new machine with the given means. Each row of the means
-     * matrix should represent a mean.
-     */
-    KMeansMachine(const blitz::Array<double,2>& means);
-
-    /**
-     * Copies another machine (copy constructor)
-     */
-    KMeansMachine(const KMeansMachine& other);
-
-    /**
-     * Starts a new KMeansMachine from an existing Configuration object.
-     */
-    KMeansMachine(bob::io::base::HDF5File& config);
-
-    /**
-     * Destructor
-     */
-    virtual ~KMeansMachine();
-
-    /**
-     * Assigns from a different machine
-     */
-    KMeansMachine& operator=(const KMeansMachine& other);
-
-    /**
-     * Equal to
-     */
-    bool operator==(const KMeansMachine& b) const;
-
-    /**
-     * Not equal to
-     */
-    bool operator!=(const KMeansMachine& b) const;
-
-    /**
-     * @brief Similar to
-     */
-    bool is_similar_to(const KMeansMachine& b, const double r_epsilon=1e-5,
-      const double a_epsilon=1e-8) const;
-
-    /**
-     * Loads data from an existing configuration object. Resets the current
-     * state.
-     */
-    void load(bob::io::base::HDF5File& config);
-
-    /**
-     * Saves an existing machine to a Configuration object.
-     */
-    void save(bob::io::base::HDF5File& config) const;
-
-    /**
-     * Output the minimum (Square Euclidean) distance between the input and
-     * one of the means (overrides Machine::forward)
-     */
-    void forward(const blitz::Array<double,1>& input, double& output) const;
-
-    /**
-     * Output the minimum (Square Euclidean) distance between the input and
-     * one of the means (overrides Machine::forward_)
-     * @warning Inputs are NOT checked
-     */
-    void forward_(const blitz::Array<double,1>& input, double& output) const;
-
-
-    /**
-     * Set the means
-     */
-    void setMeans(const blitz::Array<double,2>& means);
-
-    /**
-     * Set the i'th mean
-     */
-    void setMean(const size_t i, const blitz::Array<double,1>& mean);
-
-    /**
-     * Get a mean
-     * @param[in]   i    The index of the mean
-     * @param[out] mean The mean, a 1D array, with a length equal to the number of feature dimensions.
-     */
-    const blitz::Array<double,1> getMean(const size_t i) const;
-
-    /**
-     * Get the means (i.e. a 2D array, with as many rows as means, and as
-     * many columns as feature dimensions.)
-     */
-    const blitz::Array<double,2>& getMeans() const
-    { return m_means; }
-
-     /**
-     * Get the means in order to be updated (i.e. a 2D array, with as many
-     * rows as means, and as many columns as feature dimensions.)
-     * @warning Only trainers should use this function for efficiency reasons
-     */
-    blitz::Array<double,2>& updateMeans()
-    { return m_means; }
-
-    /**
-     * Return the power of two of the (Square Euclidean) distance of the
-     * sample, x, to the i'th mean
-     * @param x The data sample (feature vector)
-     * @param i The index of the mean
-     */
-    double getDistanceFromMean(const blitz::Array<double,1>& x,
-      const size_t i) const;
-
-    /**
-     * Calculate the index of the mean that is closest
-     * (in terms of Square Euclidean distance) to the data sample, x
-     * @param x The data sample (feature vector)
-     * @param closest_mean (output) The index of the mean closest to the sample
-     * @param min_distance (output) The distance of the sample from the closest mean
-     */
-    void getClosestMean(const blitz::Array<double,1>& x,
-      size_t &closest_mean, double &min_distance) const;
-
-    /**
-     * Output the minimum (Square Euclidean) distance between the input and
-     * one of the means
-     */
-    double getMinDistance(const blitz::Array<double,1>& input) const;
-
-    /**
-     * For each mean, find the subset of the samples
-     * that is closest to that mean, and calculate
-     * 1) the variance of that subset (the cluster variance)
-     * 2) the proportion of the samples represented by that subset (the cluster weight)
-     * @param[in]  data      The data
-     * @param[out] variances The cluster variances (one row per cluster),
-     *                       with as many columns as feature dimensions.
-     * @param[out] weights   A vector of weights, one per cluster
-     */
-    void getVariancesAndWeightsForEachCluster(const blitz::Array<double,2> &data, blitz::Array<double,2>& variances, blitz::Array<double,1>& weights) const;
-    /**
-     * Methods consecutively called by getVariancesAndWeightsForEachCluster()
-     * This should help for the parallelization on several nodes by splitting the data and calling
-     * getVariancesAndWeightsForEachClusterAcc() for each split. In this case, there is a need to sum
-     * with the m_cache_means, variances, and weights variables before performing the merge on one
-     * node using getVariancesAndWeightsForEachClusterFin().
-     */
-    void getVariancesAndWeightsForEachClusterInit(blitz::Array<double,2>& variances, blitz::Array<double,1>& weights) const;
-    void getVariancesAndWeightsForEachClusterAcc(const blitz::Array<double,2> &data, blitz::Array<double,2>& variances, blitz::Array<double,1>& weights) const;
-    void getVariancesAndWeightsForEachClusterFin(blitz::Array<double,2>& variances, blitz::Array<double,1>& weights) const;
-
-    /**
-     * Get the m_cache_means array.
-     * @warning This variable should only be used in the case you want to parallelize the
-     * getVariancesAndWeightsForEachCluster() method!
-     */
-    const blitz::Array<double,2>& getCacheMeans() const
-    { return m_cache_means; }
-
-    /**
-     * Set the m_cache_means array.
-     * @warning This variable should only be used in the case you want to parallelize the
-     * getVariancesAndWeightsForEachCluster() method!
-     */
-    void setCacheMeans(const blitz::Array<double,2>& cache_means);
-
-    /**
-     * Resize the means
-     */
-    void resize(const size_t n_means, const size_t n_inputs);
-
-    /**
-     * Return the number of means
-     */
-    size_t getNMeans() const { return m_n_means; }
-
-    /**
-     * Return the number of inputs
-     */
-    size_t getNInputs() const { return m_n_inputs; }
-
-    /**
-     * Prints a KMeansMachine in the output stream
-     */
-    friend std::ostream& operator<<(std::ostream& os, const KMeansMachine& km);
-
-
-  private:
-     /**
-     * The number of means
-     */
-    size_t m_n_means;
-
-    /**
-     * The number of inputs
-     */
-    size_t m_n_inputs;
-
-    /**
-     * The means (each row is a mean)
-     */
-    blitz::Array<double,2> m_means;
-
-    /**
-     * cache to avoid re-allocation
-     */
-    mutable blitz::Array<double,2> m_cache_means;
-};
-
-} } } // namespaces
-
-#endif // BOB_LEARN_EM_KMEANSMACHINE_H
diff --git a/bob/learn/em/include/bob.learn.em/KMeansTrainer.h b/bob/learn/em/include/bob.learn.em/KMeansTrainer.h
deleted file mode 100644
index 58bf1bedaff51a3413c5b75cf52a1314e7626a52..0000000000000000000000000000000000000000
--- a/bob/learn/em/include/bob.learn.em/KMeansTrainer.h
+++ /dev/null
@@ -1,181 +0,0 @@
-/**
- * @date Tue May 10 11:35:58 2011 +0200
- * @author Francois Moulin <Francois.Moulin@idiap.ch>
- * @author Laurent El Shafey <Laurent.El-Shafey@idiap.ch>
- *
- * Copyright (C) Idiap Research Institute, Martigny, Switzerland
- */
-#ifndef BOB_LEARN_EM_KMEANSTRAINER_H
-#define BOB_LEARN_EM_KMEANSTRAINER_H
-
-#include <bob.learn.em/KMeansMachine.h>
-#include <boost/version.hpp>
-#include <boost/random/mersenne_twister.hpp>
-
-namespace bob { namespace learn { namespace em {
-
-/**
- * Trains a KMeans machine.
- * @brief This class implements the expectation-maximisation algorithm for a k-means machine.
- * @details See Section 9.1 of Bishop, "Pattern recognition and machine learning", 2006
- *          It uses a random initialisation of the means followed by the expectation-maximization algorithm
- */
-class KMeansTrainer
-{
-  public:
-    /**
-     * @brief This enumeration defines different initialization methods for
-     * K-means
-     */
-    typedef enum {
-      RANDOM=0,
-      RANDOM_NO_DUPLICATE
-#if BOOST_VERSION >= 104700
-      ,
-      KMEANS_PLUS_PLUS
-#endif
-    }
-    InitializationMethod;
-
-    /**
-     * @brief Constructor
-     */
-    KMeansTrainer(InitializationMethod=RANDOM_NO_DUPLICATE);
-
-    /**
-     * @brief Virtualize destructor
-     */
-    virtual ~KMeansTrainer() {}
-
-    /**
-     * @brief Copy constructor
-     */
-    KMeansTrainer(const KMeansTrainer& other);
-
-    /**
-     * @brief Assigns from a different machine
-     */
-    KMeansTrainer& operator=(const KMeansTrainer& other);
-
-    /**
-     * @brief Equal to
-     */
-    bool operator==(const KMeansTrainer& b) const;
-
-    /**
-     * @brief Not equal to
-     */
-    bool operator!=(const KMeansTrainer& b) const;
-
-    /**
-     * @brief The name for this trainer
-     */
-    virtual std::string name() const { return "KMeansTrainer"; }
-
-    /**
-     * @brief Initialise the means randomly.
-     * Data is split into as many chunks as there are means,
-     * then each mean is set to a random example within each chunk.
-     */
-    void initialize(bob::learn::em::KMeansMachine& kMeansMachine,
-      const blitz::Array<double,2>& sampler);
-
-    /**
-     * @brief Accumulate across the dataset:
-     * - zeroeth and first order statistics
-     * - average (Square Euclidean) distance from the closest mean
-     * Implements EMTrainer::eStep(double &)
-     */
-    void eStep(bob::learn::em::KMeansMachine& kmeans,
-      const blitz::Array<double,2>& data);
-
-    /**
-     * @brief Updates the mean based on the statistics from the E-step.
-     */
-    void mStep(bob::learn::em::KMeansMachine& kmeans);
-
-    /**
-     * @brief This functions returns the average min (Square Euclidean)
-     * distance (average distance to the closest mean)
-     */
-    double computeLikelihood(bob::learn::em::KMeansMachine& kmeans);
-
-
-    /**
-     * @brief Reset the statistics accumulators
-     * to the correct size and a value of zero.
-     */
-    void resetAccumulators(bob::learn::em::KMeansMachine& kMeansMachine);
-
-    /**
-     * @brief Sets the Random Number Generator
-     */
-    void setRng(const boost::shared_ptr<boost::mt19937> rng)
-    { m_rng = rng; }
-
-    /**
-     * @brief Gets the Random Number Generator
-     */
-    const boost::shared_ptr<boost::mt19937> getRng() const
-    { return m_rng; }
-
-    /**
-     * @brief Sets the initialization method used to generate the initial means
-     */
-    void setInitializationMethod(InitializationMethod v) { m_initialization_method = v; }
-
-    /**
-     * @brief Gets the initialization method used to generate the initial means
-     */
-    InitializationMethod getInitializationMethod() const { return m_initialization_method; }
-
-    /**
-     * @brief Returns the internal statistics. Useful to parallelize the E-step
-     */
-    const blitz::Array<double,1>& getZeroethOrderStats() const { return m_zeroethOrderStats; }
-    const blitz::Array<double,2>& getFirstOrderStats() const { return m_firstOrderStats; }
-    double getAverageMinDistance() const { return m_average_min_distance; }
-    /**
-     * @brief Sets the internal statistics. Useful to parallelize the E-step
-     */
-    void setZeroethOrderStats(const blitz::Array<double,1>& zeroethOrderStats);
-    void setFirstOrderStats(const blitz::Array<double,2>& firstOrderStats);
-    void setAverageMinDistance(const double value) { m_average_min_distance = value; }
-
-
-  private:
-
-    /**
-     * @brief The initialization method
-     * Check that there is no duplicated means during the random initialization
-     */
-    InitializationMethod m_initialization_method;
-
-    /**
-     * @brief The random number generator for the inialization
-     */
-    boost::shared_ptr<boost::mt19937> m_rng;
-
-    /**
-     * @brief Average min (Square Euclidean) distance
-     */
-    double m_average_min_distance;
-
-    /**
-     * @brief Zeroeth order statistics accumulator.
-     * The k'th value in m_zeroethOrderStats is the denominator of
-     * equation 9.4, Bishop, "Pattern recognition and machine learning", 2006
-     */
-    blitz::Array<double,1> m_zeroethOrderStats;
-
-    /**
-     * @brief First order statistics accumulator.
-     * The k'th row of m_firstOrderStats is the numerator of
-     * equation 9.4, Bishop, "Pattern recognition and machine learning", 2006
-     */
-    blitz::Array<double,2> m_firstOrderStats;
-};
-
-} } } // namespaces
-
-#endif // BOB_LEARN_EM_KMEANSTRAINER_H
diff --git a/bob/learn/em/include/bob.learn.em/LinearScoring.h b/bob/learn/em/include/bob.learn.em/LinearScoring.h
deleted file mode 100644
index 822922ceefbbd8a5069fb77f6a788cf63dd617e9..0000000000000000000000000000000000000000
--- a/bob/learn/em/include/bob.learn.em/LinearScoring.h
+++ /dev/null
@@ -1,98 +0,0 @@
-/**
- * @date Wed Jul 13 16:00:04 2011 +0200
- * @author Francois Moulin <Francois.Moulin@idiap.ch>
- *
- * Copyright (C) Idiap Research Institute, Martigny, Switzerland
- */
-#ifndef BOB_LEARN_EM_LINEARSCORING_H
-#define BOB_LEARN_EM_LINEARSCORING_H
-
-#include <blitz/array.h>
-#include <boost/shared_ptr.hpp>
-#include <vector>
-#include <bob.learn.em/GMMMachine.h>
-
-namespace bob { namespace learn { namespace em {
-
-/**
- * Compute a matrix of scores using linear scoring.
- *
- * @warning Each GMM must have the same size.
- *
- * @param models        list of mean supervector for the client models
- * @param ubm_mean      mean supervector of the world model
- * @param ubm_variance  variance supervector of the world model
- * @param test_stats    list of accumulate statistics for each test trial
- * @param test_channelOffset  list of channel offset if any (for JFA/ISA for instance)
- * @param frame_length_normalisation   perform a normalisation by the number of feature vectors
- * @param[out] scores 2D matrix of scores, <tt>scores[m, s]</tt> is the score for model @c m against statistics @c s
- * @warning the output scores matrix should have the correct size (number of models x number of test_stats)
- */
-void linearScoring(const std::vector<blitz::Array<double,1> >& models,
-                   const blitz::Array<double,1>& ubm_mean, const blitz::Array<double,1>& ubm_variance,
-                   const std::vector<boost::shared_ptr<const bob::learn::em::GMMStats> >& test_stats,
-                   const std::vector<blitz::Array<double, 1> >& test_channelOffset,
-                   const bool frame_length_normalisation,
-                   blitz::Array<double,2>& scores);
-void linearScoring(const std::vector<blitz::Array<double,1> >& models,
-                   const blitz::Array<double,1>& ubm_mean, const blitz::Array<double,1>& ubm_variance,
-                   const std::vector<boost::shared_ptr<const bob::learn::em::GMMStats> >& test_stats,
-                   const bool frame_length_normalisation,
-                   blitz::Array<double,2>& scores);
-
-/**
- * Compute a matrix of scores using linear scoring.
- *
- * @warning Each GMM must have the same size.
- *
- * @param models      list of client models as GMMMachines
- * @param ubm         world model as a GMMMachine
- * @param test_stats  list of accumulate statistics for each test trial
- * @param frame_length_normalisation   perform a normalisation by the number of feature vectors
- * @param[out] scores 2D matrix of scores, <tt>scores[m, s]</tt> is the score for model @c m against statistics @c s
- * @warning the output scores matrix should have the correct size (number of models x number of test_stats)
- */
-void linearScoring(const std::vector<boost::shared_ptr<const bob::learn::em::GMMMachine> >& models,
-                   const bob::learn::em::GMMMachine& ubm,
-                   const std::vector<boost::shared_ptr<const bob::learn::em::GMMStats> >& test_stats,
-                   const bool frame_length_normalisation,
-                   blitz::Array<double,2>& scores);
-/**
- * Compute a matrix of scores using linear scoring.
- *
- * @warning Each GMM must have the same size.
- *
- * @param models      list of client models as GMMMachines
- * @param ubm         world model as a GMMMachine
- * @param test_stats  list of accumulate statistics for each test trial
- * @param test_channelOffset  list of channel offset if any (for JFA/ISA for instance)
- * @param frame_length_normalisation   perform a normalisation by the number of feature vectors
- * @param[out] scores 2D matrix of scores, <tt>scores[m, s]</tt> is the score for model @c m against statistics @c s
- * @warning the output scores matrix should have the correct size (number of models x number of test_stats)
- */
-void linearScoring(const std::vector<boost::shared_ptr<const bob::learn::em::GMMMachine> >& models,
-                   const bob::learn::em::GMMMachine& ubm,
-                   const std::vector<boost::shared_ptr<const bob::learn::em::GMMStats> >& test_stats,
-                   const std::vector<blitz::Array<double, 1> >& test_channelOffset,
-                   const bool frame_length_normalisation,
-                   blitz::Array<double,2>& scores);
-
-/**
- * Compute a score using linear scoring.
- *
- * @param model         mean supervector for the client model
- * @param ubm_mean      mean supervector of the world model
- * @param ubm_variance  variance supervector of the world model
- * @param test_stats    accumulate statistics of the test trial
- * @param test_channelOffset  channel offset
- * @param frame_length_normalisation   perform a normalisation by the number of feature vectors
- */
-double linearScoring(const blitz::Array<double,1>& model,
-                   const blitz::Array<double,1>& ubm_mean, const blitz::Array<double,1>& ubm_variance,
-                   const bob::learn::em::GMMStats& test_stats,
-                   const blitz::Array<double,1>& test_channelOffset,
-                   const bool frame_length_normalisation);
-
-} } } // namespaces
-
-#endif // BOB_LEARN_EM_LINEARSCORING_H
diff --git a/bob/learn/em/include/bob.learn.em/MAP_GMMTrainer.h b/bob/learn/em/include/bob.learn.em/MAP_GMMTrainer.h
deleted file mode 100644
index aac7087fcd84c69334f2fe0cbfb14cfeff5a5369..0000000000000000000000000000000000000000
--- a/bob/learn/em/include/bob.learn.em/MAP_GMMTrainer.h
+++ /dev/null
@@ -1,172 +0,0 @@
-/**
- * @date Tue May 10 11:35:58 2011 +0200
- * @author Francois Moulin <Francois.Moulin@idiap.ch>
- *
- * @brief This class implements the maximum a posteriori M-step of the expectation-maximisation algorithm for a GMM Machine. The prior parameters are encoded in the form of a GMM (e.g. a universal background model). The EM algorithm thus performs GMM adaptation.
- * @details See Section 3.4 of Reynolds et al., "Speaker Verification Using Adapted Gaussian Mixture Models", Digital Signal Processing, 2000. We use a "single adaptation coefficient", alpha_i, and thus a single relevance factor, r.
- *
- * Copyright (C) Idiap Research Institute, Martigny, Switzerland
- */
-
-#ifndef BOB_LEARN_EM_MAP_GMMTRAINER_H
-#define BOB_LEARN_EM_MAP_GMMTRAINER_H
-
-#include <bob.learn.em/GMMBaseTrainer.h>
-#include <limits>
-
-namespace bob { namespace learn { namespace em {
-
-/**
- * @brief This class implements the maximum a posteriori M-step of the expectation-maximisation algorithm for a GMM Machine. The prior parameters are encoded in the form of a GMM (e.g. a universal background model). The EM algorithm thus performs GMM adaptation.
- * @details See Section 3.4 of Reynolds et al., "Speaker Verification Using Adapted Gaussian Mixture Models", Digital Signal Processing, 2000. We use a "single adaptation coefficient", alpha_i, and thus a single relevance factor, r.
- */
-class MAP_GMMTrainer
-{
-  public:
-    /**
-     * @brief Default constructor
-     */
-    MAP_GMMTrainer(
-      const bool update_means=true,
-      const bool update_variances=false,
-      const bool update_weights=false,
-      const double mean_var_update_responsibilities_threshold = std::numeric_limits<double>::epsilon(),
-      const bool reynolds_adaptation=false,
-      const double relevance_factor=4,
-      const double alpha=0.5,
-      boost::shared_ptr<bob::learn::em::GMMMachine> prior_gmm = boost::shared_ptr<bob::learn::em::GMMMachine>());
-
-    /**
-     * @brief Copy constructor
-     */
-    MAP_GMMTrainer(const MAP_GMMTrainer& other);
-
-    /**
-     * @brief Destructor
-     */
-    virtual ~MAP_GMMTrainer();
-
-    /**
-     * @brief Initialization
-     */
-    void initialize(bob::learn::em::GMMMachine& gmm);
-
-    /**
-     * @brief Assigns from a different MAP_GMMTrainer
-     */
-    MAP_GMMTrainer& operator=(const MAP_GMMTrainer &other);
-
-    /**
-     * @brief Equal to
-     */
-    bool operator==(const MAP_GMMTrainer& b) const;
-
-    /**
-     * @brief Not equal to
-     */
-    bool operator!=(const MAP_GMMTrainer& b) const;
-
-    /**
-     * @brief Similar to
-     */
-    bool is_similar_to(const MAP_GMMTrainer& b, const double r_epsilon=1e-5,
-      const double a_epsilon=1e-8) const;
-
-    /**
-     * @brief Set the GMM to use as a prior for MAP adaptation.
-     * Generally, this is a "universal background model" (UBM),
-     * also referred to as a "world model".
-     */
-    bool setPriorGMM(boost::shared_ptr<bob::learn::em::GMMMachine> prior_gmm);
-
-    /**
-     * @brief Calculates and saves statistics across the dataset,
-     * and saves these as m_ss. Calculates the average
-     * log likelihood of the observations given the GMM,
-     * and returns this in average_log_likelihood.
-     *
-     * The statistics, m_ss, will be used in the mStep() that follows.
-     * Implements EMTrainer::eStep(double &)
-     */
-     void eStep(bob::learn::em::GMMMachine& gmm,
-      const blitz::Array<double,2>& data){
-      m_gmm_base_trainer.eStep(gmm,data);
-     }
-
-
-    /**
-     * @brief Performs a maximum a posteriori (MAP) update of the GMM
-     * parameters using the accumulated statistics in m_ss and the
-     * parameters of the prior model
-     * Implements EMTrainer::mStep()
-     */
-    void mStep(bob::learn::em::GMMMachine& gmm);
-
-    /**
-     * @brief Computes the likelihood using current estimates of the latent
-     * variables
-     */
-    double computeLikelihood(bob::learn::em::GMMMachine& gmm){
-      return m_gmm_base_trainer.computeLikelihood(gmm);
-    }
-
-    bool getReynoldsAdaptation()
-    {return m_reynolds_adaptation;}
-
-    void setReynoldsAdaptation(const bool reynolds_adaptation)
-    {m_reynolds_adaptation = reynolds_adaptation;}
-
-
-    double getRelevanceFactor()
-    {return m_relevance_factor;}
-
-    void setRelevanceFactor(const double relevance_factor)
-    {m_relevance_factor = relevance_factor;}
-
-
-    double getAlpha()
-    {return m_alpha;}
-
-    void setAlpha(const double alpha)
-    {m_alpha = alpha;}
-
-    bob::learn::em::GMMBaseTrainer& base_trainer(){return m_gmm_base_trainer;}
-
-
-  protected:
-
-    /**
-     * The relevance factor for MAP adaptation, r (see Reynolds et al., \"Speaker Verification Using Adapted Gaussian Mixture Models\", Digital Signal Processing, 2000).
-     */
-    double m_relevance_factor;
-
-    /**
-    Base Trainer for the MAP algorithm. Basically implements the e-step
-    */
-    bob::learn::em::GMMBaseTrainer m_gmm_base_trainer;
-
-    /**
-     * The GMM to use as a prior for MAP adaptation.
-     * Generally, this is a "universal background model" (UBM),
-     * also referred to as a "world model"
-     */
-    boost::shared_ptr<bob::learn::em::GMMMachine> m_prior_gmm;
-
-    /**
-     * The alpha for the Torch3-like adaptation
-     */
-    double m_alpha;
-    /**
-     * Whether Torch3-like adaptation should be used or not
-     */
-    bool m_reynolds_adaptation;
-
-  private:
-    /// cache to avoid re-allocation
-    mutable blitz::Array<double,1> m_cache_alpha;
-    mutable blitz::Array<double,1> m_cache_ml_weights;
-};
-
-} } } // namespaces
-
-#endif // BOB_LEARN_EM_MAP_GMMTRAINER_H
diff --git a/bob/learn/em/include/bob.learn.em/ML_GMMTrainer.h b/bob/learn/em/include/bob.learn.em/ML_GMMTrainer.h
deleted file mode 100644
index 40f1e70cb1c3955284b628d91714a9e3901d6986..0000000000000000000000000000000000000000
--- a/bob/learn/em/include/bob.learn.em/ML_GMMTrainer.h
+++ /dev/null
@@ -1,121 +0,0 @@
-/**
- * @date Tue May 10 11:35:58 2011 +0200
- * @author Francois Moulin <Francois.Moulin@idiap.ch>
- *
- * @brief This class implements the maximum likelihood M-step of the expectation-maximisation algorithm for a GMM Machine.
- * @details See Section 9.2.2 of Bishop, "Pattern recognition and machine learning", 2006
- *
- * Copyright (C) Idiap Research Institute, Martigny, Switzerland
- */
-
-#ifndef BOB_LEARN_EM_ML_GMMTRAINER_H
-#define BOB_LEARN_EM_ML_GMMTRAINER_H
-
-#include <bob.learn.em/GMMBaseTrainer.h>
-#include <limits>
-
-namespace bob { namespace learn { namespace em {
-
-/**
- * @brief This class implements the maximum likelihood M-step of the
- *   expectation-maximisation algorithm for a GMM Machine.
- * @details See Section 9.2.2 of Bishop,
- *  "Pattern recognition and machine learning", 2006
- */
-class ML_GMMTrainer{
-  public:
-    /**
-     * @brief Default constructor
-     */
-    ML_GMMTrainer(const bool update_means=true,
-                  const bool update_variances=false,
-                  const bool update_weights=false,
-                  const double mean_var_update_responsibilities_threshold = std::numeric_limits<double>::epsilon());
-
-    /**
-     * @brief Copy constructor
-     */
-    ML_GMMTrainer(const ML_GMMTrainer& other);
-
-    /**
-     * @brief Destructor
-     */
-    virtual ~ML_GMMTrainer();
-
-    /**
-     * @brief Initialisation before the EM steps
-     */
-    void initialize(bob::learn::em::GMMMachine& gmm);
-
-    /**
-     * @brief Calculates and saves statistics across the dataset,
-     * and saves these as m_ss. Calculates the average
-     * log likelihood of the observations given the GMM,
-     * and returns this in average_log_likelihood.
-     *
-     * The statistics, m_ss, will be used in the mStep() that follows.
-     * Implements EMTrainer::eStep(double &)
-     */
-     void eStep(bob::learn::em::GMMMachine& gmm,
-      const blitz::Array<double,2>& data){
-      m_gmm_base_trainer.eStep(gmm,data);
-     }
-
-    /**
-     * @brief Performs a maximum likelihood (ML) update of the GMM parameters
-     * using the accumulated statistics in m_ss
-     * Implements EMTrainer::mStep()
-     */
-    void mStep(bob::learn::em::GMMMachine& gmm);
-
-    /**
-     * @brief Computes the likelihood using current estimates of the latent
-     * variables
-     */
-    double computeLikelihood(bob::learn::em::GMMMachine& gmm){
-      return m_gmm_base_trainer.computeLikelihood(gmm);
-    }
-
-
-    /**
-     * @brief Assigns from a different ML_GMMTrainer
-     */
-    ML_GMMTrainer& operator=(const ML_GMMTrainer &other);
-
-    /**
-     * @brief Equal to
-     */
-    bool operator==(const ML_GMMTrainer& b) const;
-
-    /**
-     * @brief Not equal to
-     */
-    bool operator!=(const ML_GMMTrainer& b) const;
-
-    /**
-     * @brief Similar to
-     */
-    bool is_similar_to(const ML_GMMTrainer& b, const double r_epsilon=1e-5,
-      const double a_epsilon=1e-8) const;
-
-
-    bob::learn::em::GMMBaseTrainer& base_trainer(){return m_gmm_base_trainer;}
-
-  protected:
-
-    /**
-    Base Trainer for the MAP algorithm. Basically implements the e-step
-    */
-    bob::learn::em::GMMBaseTrainer m_gmm_base_trainer;
-
-
-  private:
-    /**
-     * @brief Add cache to avoid re-allocation at each iteration
-     */
-    mutable blitz::Array<double,1> m_cache_ss_n_thresholded;
-};
-
-} } } // namespaces
-
-#endif // BOB_LEARN_EM_ML_GMMTRAINER_H
diff --git a/bob/learn/em/include/bob.learn.em/PLDAMachine.h b/bob/learn/em/include/bob.learn.em/PLDAMachine.h
deleted file mode 100644
index 4f6362ddc322f9ef1b601c9cbee8d6239f8f6200..0000000000000000000000000000000000000000
--- a/bob/learn/em/include/bob.learn.em/PLDAMachine.h
+++ /dev/null
@@ -1,702 +0,0 @@
-/**
- * @date Fri Oct 14 18:07:56 2011 +0200
- * @author Laurent El Shafey <Laurent.El-Shafey@idiap.ch>
- *
- * @brief Machines that implements the Probabilistic Linear Discriminant
- *   Analysis Model of Prince and Helder,
- *
- * Copyright (C) Idiap Research Institute, Martigny, Switzerland
- */
-
-#ifndef BOB_LEARN_EM_PLDAMACHINE_H
-#define BOB_LEARN_EM_PLDAMACHINE_H
-
-#include <blitz/array.h>
-#include <bob.io.base/HDF5File.h>
-#include <map>
-#include <iostream>
-#include <stdexcept>
-
-namespace bob { namespace learn { namespace em {
-
-/**
- * @brief This class is a container for the \f$F\f$, \f$G\f$ and \f$\Sigma\f$
- * matrices and the mean vector \f$\mu\f$ of a PLDA model. This also
- * precomputes useful matrices to make the model scalable.\n
- * References:\n
- * 1. 'A Scalable Formulation of Probabilistic Linear Discriminant Analysis:
- *     Applied to Face Recognition', Laurent El Shafey, Chris McCool,
- *     Roy Wallace, Sebastien Marcel, TPAMI'2013
- * 2. 'Probabilistic Linear Discriminant Analysis for Inference About
- *     Identity', Prince and Elder, ICCV'2007\n
- * 3. 'Probabilistic Models for Inference about Identity', Li, Fu, Mohammed,
- *     Elder and Prince, TPAMI'2012
- */
-class PLDABase
-{
-  public:
-    /**
-     * @brief Default constructor.\n Builds an otherwise invalid 0x0x0
-     * PLDABase.
-     */
-    PLDABase();
-    /**
-     * @brief Constructor, builds a new PLDABase.\n \f$F\f$, \f$G\f$
-     * and \f$\Sigma\f$ are initialized to the 'eye' matrix (matrix with 1's
-     * on the diagonal and 0 outside), and \f$\mu\f$ is initialized to 0.
-     *
-     * @param dim_d Dimensionality of the feature vector
-     * @param dim_f size of \f$F\f$ (dim_d x dim_f)
-     * @param dim_g size of \f$G\f$ (dim_d x dim_g)
-     * @param variance_threshold The smallest possible value of the variance
-     *                           (Ignored if set to 0.)
-     */
-    PLDABase(const size_t dim_d, const size_t dim_f,
-      const size_t dim_g, const double variance_threshold=0.);
-    /**
-     * @brief Copies another PLDABase
-     */
-    PLDABase(const PLDABase& other);
-    /**
-     * @brief Starts a new PLDABase from an existing configuration
-     * object.
-     * @param config HDF5 configuration file
-     */
-    PLDABase(bob::io::base::HDF5File& config);
-
-    /**
-     * @brief Just to virtualize the destructor
-     */
-    virtual ~PLDABase();
-
-    /**
-     * @brief Assigns from a different PLDABase
-     */
-    PLDABase& operator=(const PLDABase &other);
-
-    /**
-     * @brief Equal to.\n Even precomputed members such as \f$\alpha\f$,
-     * \f$\beta\f$ and \f$\gamma_a\f$'s are compared!
-     */
-    bool operator==(const PLDABase& b) const;
-    /**
-     * @brief Not equal to.\n Defined as the negation of operator==
-     */
-    bool operator!=(const PLDABase& b) const;
-    /**
-     * @brief Similar to.\n Even precomputed members such as \f$\alpha\f$,
-     * \f$\beta\f$ and \f$\gamma_a\f$'s are compared!
-     */
-    bool is_similar_to(const PLDABase& b, const double r_epsilon=1e-5,
-      const double a_epsilon=1e-8) const;
-
-    /**
-     * @brief Loads data from an existing configuration object. Resets the
-     * current state.
-     * @param config HDF5 configuration file
-     */
-    void load(bob::io::base::HDF5File& config);
-    /**
-     * @brief Saves an existing machine to a configuration object.
-     * @param config HDF5 configuration file
-     */
-    void save(bob::io::base::HDF5File& config) const;
-
-    /**
-     * @brief Resizes the PLDABase.
-     * @warning \f$F\f$, \f$G\f$, \f$\Sigma\f$, \f$\mu\f$ and the variance
-     * flooring thresholds will be reinitialized!
-     * @param dim_d Dimensionality of the feature vector
-     * @param dim_f Rank of \f$F\f$ (dim_d x dim_f)
-     * @param dim_g Rank of \f$G\f$ (dim_d x dim_g)
-     */
-    void resize(const size_t dim_d, const size_t dim_f, const size_t dim_g);
-
-    /**
-     * @brief Gets the \f$F\f$ subspace/matrix of the PLDA model
-     */
-    const blitz::Array<double,2>& getF() const
-    { return m_F; }
-    /**
-     * @brief Sets the \f$F\f$ subspace/matrix of the PLDA model
-     */
-    void setF(const blitz::Array<double,2>& F);
-    /**
-     * @brief Returns the current \f$F\f$ matrix/subspace of the PLDA model
-     * in order to be updated.
-     * @warning Use with care. Only trainers should use this function for
-     * efficiency reasons.
-     */
-    blitz::Array<double,2>& updateF()
-    { return m_F; }
-
-    /**
-     * @brief Gets the \f$G\f$ subspace/matrix of the PLDA model
-     */
-    const blitz::Array<double,2>& getG() const
-    { return m_G; }
-    /**
-     * @brief Sets the \f$G\f$ subspace/matrix of the PLDA model
-     */
-    void setG(const blitz::Array<double,2>& G);
-    /**
-     * @brief Returns the current \f$G\f$ subspace/matrix of the PLDA model
-     * in order to be updated.
-     * @warning Use with care. Only trainers should use this function for
-     * efficiency reasons.
-     */
-    blitz::Array<double,2>& updateG()
-    { return m_G; }
-
-    /**
-     * @brief Gets the \f$\Sigma\f$ (diagonal) covariance matrix of the PLDA
-     * model
-     */
-    const blitz::Array<double,1>& getSigma() const
-    { return m_sigma; }
-    /**
-     * @brief Sets the \f$\Sigma\f$ (diagonal) covariance matrix of the PLDA
-     * model
-     */
-    void setSigma(const blitz::Array<double,1>& s);
-    /**
-     * @brief Returns the current \f$\Sigma\f$ (diagonal) covariance matrix of
-     * the PLDA model in order to be updated.
-     * @warning Use with care. Only trainers should use this function for
-     * efficiency reasons. Variance threshold should be applied after
-     * updating \f$\Sigma\f$!
-     */
-    blitz::Array<double,1>& updateSigma()
-    { return m_sigma; }
-
-    /**
-     * @brief Gets the \f$\mu\f$ mean vector of the PLDA model
-     */
-    const blitz::Array<double,1>& getMu() const
-    { return m_mu; }
-    /**
-     * @brief Sets the \f$\mu\f$ mean vector of the PLDA model
-     */
-    void setMu(const blitz::Array<double,1>& mu);
-    /**
-     * @brief Returns the current \f$\mu\f$ mean vector of the PLDA model
-     * in order to be updated.
-     * @warning Use with care. Only trainers should use this function for
-     * efficiency reasons.
-     */
-    blitz::Array<double,1>& updateMu()
-    { return m_mu; }
-
-    /**
-     * @brief Gets the variance flooring threshold
-     */
-    double getVarianceThreshold() const
-    { return m_variance_threshold; }
-    /**
-     * @brief Sets the variance flooring threshold
-     */
-    void setVarianceThreshold(const double value);
-    /**
-     * @brief Apply the variance flooring thresholds.
-     * This method is automatically called when using setVarianceThresholds().
-     * @warning It is only useful when using updateVarianceThreshods(),
-     * and should mostly be done by trainers
-     */
-    void applyVarianceThreshold();
-
-    /**
-     * @brief Gets the feature dimensionality
-     */
-    size_t getDimD() const
-    { return m_dim_d; }
-    /**
-     * @brief Gets the size/rank the \f$F\f$ subspace/matrix of the PLDA model
-     */
-    size_t getDimF() const
-    { return m_dim_f; }
-    /**
-     * @brief Gets the size/rank the \f$G\f$ subspace/matrix of the PLDA model
-     */
-    size_t getDimG() const
-    { return m_dim_g; }
-
-    /**
-     * @brief Precomputes useful values such as \f$\Sigma^{-1}\f$,
-     * \f$G^{T}\Sigma^{-1}\f$, \f$\alpha\f$, \f$\beta\f$, and
-     * \f$F^{T}\beta\f$.
-     * @warning Previous \f$\gamma_a\f$ values and log likelihood constant
-     * terms are cleared.
-     */
-    void precompute();
-    /**
-     * @brief Precomputes useful values for the log likelihood
-     * \f$\log(\det(\alpha))\f$ and \f$\log(\det(\Sigma))\f$.
-     */
-    void precomputeLogLike();
-    /**
-     * @brief Gets the inverse vector/diagonal matrix of \f$\Sigma^{-1}\f$
-     */
-    const blitz::Array<double,1>& getISigma() const
-    { return m_cache_isigma; }
-    /**
-     * @brief Gets the \f$\alpha\f$ matrix.
-     * \f$\alpha = (Id + G^T \Sigma^{-1} G)^{-1} = \mathcal{G}\f$
-     */
-    const blitz::Array<double,2>& getAlpha() const
-    { return m_cache_alpha; }
-    /**
-     * @brief Gets the \f$\beta\f$ matrix
-     * \f$\beta = (\Sigma + G G^T)^{-1} = \mathcal{S} =
-     *    \Sigma^{-1} - \Sigma^{-1} G \mathcal{G} G^{T} \Sigma^{-1}\f$
-     */
-    const blitz::Array<double,2>& getBeta() const
-    { return m_cache_beta; }
-    /**
-     * @brief Gets the \f$\gamma_a\f$ matrix for a given \f$a\f$ (number of
-     * samples).
-     * \f$\gamma_{a} = (Id + a F^T \beta F)^{-1} = \mathcal{F}_{a}\f$
-     * @warning an exception is thrown if \f$\gamma_a\f$ does not exists
-     */
-    const blitz::Array<double,2>& getGamma(const size_t a) const;
-    /**
-     * @brief Gets the \f$\gamma_a\f$ matrix for a given \f$a\f$ (number of
-     * samples).
-     * \f$\gamma_a = (Id + a F^T \beta F)^{-1} = \mathcal{F}_{a}\f$
-     * @warning The matrix is computed if it does not already exists
-     */
-    const blitz::Array<double,2>& getAddGamma(const size_t a);
-    /**
-     * @brief Gets the \f$F^T \beta\f$ matrix
-     */
-    const blitz::Array<double,2>& getFtBeta() const
-    { return m_cache_Ft_beta; }
-    /**
-     * @brief Gets the \f$G^T \Sigma^{-1}\f$ matrix
-     */
-    const blitz::Array<double,2>& getGtISigma() const
-    { return m_cache_Gt_isigma; }
-    /**
-     * @brief Gets \f$\log(\det(\alpha))\f$
-     */
-    double getLogDetAlpha() const
-    { return m_cache_logdet_alpha; }
-    /**
-     * @brief Gets \f$\log(\det(\Sigma))\f$
-     */
-    double getLogDetSigma() const
-    { return m_cache_logdet_sigma; }
-    /**
-     * @brief Computes the log likelihood constant term for a given \f$a\f$
-     * (number of samples), given the provided \f$\gamma_a\f$ matrix
-     * \f$l_{a} = \frac{a}{2} ( -D log(2\pi) -log|\Sigma| +log|\alpha| +log|\gamma_a|)\f$
-     */
-    double computeLogLikeConstTerm(const size_t a,
-      const blitz::Array<double,2>& gamma_a) const;
-    /**
-     * @brief Computes the log likelihood constant term for a given \f$a\f$
-     * (number of samples)
-     * \f$l_{a} = \frac{a}{2} ( -D log(2\pi) -log|\Sigma| +log|\alpha| +log|\gamma_a|)\f$
-     * @warning: gamma_a will be computed and added if it does
-     *  not already exists
-     */
-    double computeLogLikeConstTerm(const size_t a);
-    /**
-     * @brief Tells if the log likelihood constant term for a given \f$a\f$
-     * (number of samples) exists
-     * \f$l_{a} = \frac{a}{2} ( -D log(2\pi) -log|\Sigma| +log|\alpha| +log|\gamma_a|)\f$
-     */
-    bool hasLogLikeConstTerm(const size_t a) const
-    { return (m_cache_loglike_constterm.find(a) != m_cache_loglike_constterm.end()); }
-    /**
-     * @brief Gets the log likelihood constant term for a given \f$a\f$
-     * (number of samples)
-     * \f$l_{a} = \frac{a}{2} ( -D log(2\pi) -log|\Sigma| +log|\alpha| +log|\gamma_a|)\f$
-     * @warning an exception is thrown if the value does not exists
-     */
-    double getLogLikeConstTerm(const size_t a) const;
-    /**
-     * @brief Gets the log likelihood constant term for a given \f$a\f$
-     * (number of samples)
-     * \f$l_{a} = \frac{a}{2} ( -D log(2\pi) -log|\Sigma| +log|\alpha| +log|\gamma_a|)\f$
-     * @warning The value is computed if it does not already exists
-     */
-    double getAddLogLikeConstTerm(const size_t a);
-
-    /**
-     * @brief Computes the \f$\gamma_a\f$ matrix for a given \f$a\f$ (number
-     * of samples) and put the result in the provided array.
-     * \f$\gamma_a = (Id + a F^T \beta F)^{-1}\f$
-     */
-    void computeGamma(const size_t a, blitz::Array<double,2> res) const;
-    /**
-     * @brief Tells if the \f$\gamma_a\f$ matrix for a given a (number of
-     * samples) exists.
-     * \f$\gamma_a = (Id + a F^T \beta F)^{-1}\f$
-     */
-    bool hasGamma(const size_t a) const
-    { return (m_cache_gamma.find(a) != m_cache_gamma.end()); }
-
-    /**
-     * @brief Clears the maps (\f$\gamma_a\f$ and loglike_constterm_a).
-     */
-    void clearMaps();
-
-    /**
-     * @brief Gets the log-likelihood of an observation, given the current model
-     * and the latent variables (point estimate).\n
-     * This will basically compute \f$p(x_{ij} | h_{i}, w_{ij}, \Theta)\f$\n
-     * , given by \n
-     * \f$\mathcal{N}(x_{ij}|[\mu + F h_{i} + G w_{ij} + \epsilon_{ij}, \Sigma])\f$\n
-     * , which is in logarithm, \n
-     * \f$-\frac{D}{2} log(2\pi) -\frac{1}{2} log(det(\Sigma)) -\frac{1}{2} {(x_{ij}-(\mu+F h_{i}+G w_{ij}))^{T}\Sigma^{-1}(x_{ij}-(\mu+F h_{i}+G w_{ij}))}\f$.
-     */
-    double computeLogLikelihoodPointEstimate(const blitz::Array<double,1>& xij,
-      const blitz::Array<double,1>& hi, const blitz::Array<double,1>& wij) const;
-
-    // Friend method declaration
-    friend std::ostream& operator<<(std::ostream& os, const PLDABase& m);
-
-
-  private:
-    // Attributes
-    size_t m_dim_d; ///< Dimensionality of the input feature vector
-    size_t m_dim_f; ///< Size/rank of the \f$F\f$ subspace
-    size_t m_dim_g; ///< Size/rank of the \f$G\f$ subspace
-    blitz::Array<double,2> m_F; ///< \f$F\f$ subspace of the PLDA model
-    blitz::Array<double,2> m_G; ///< \f$G\f$ subspace of the PLDA model
-    /**
-     * @brief \f$\Sigma\f$ diagonal (by assumption) covariance matrix of the
-     * PLDA model
-     */
-    blitz::Array<double,1> m_sigma;
-    blitz::Array<double,1> m_mu; ///< \f$\mu\f$ mean vector of the PLDA model
-    /**
-     * @brief The variance flooring thresholds, i.e. the minimum allowed
-     * value of variance m_sigma in each dimension.
-     * The variance will be set to this value if an attempt is made
-     * to set it to a smaller value.
-     */
-    double m_variance_threshold;
-
-    // Internal values very useful used to optimize the code
-    blitz::Array<double,1> m_cache_isigma; ///< \f$\Sigma^{-1}\f$
-    blitz::Array<double,2> m_cache_alpha; ///< \f$\alpha = (Id + G^T \Sigma^{-1} G)^{-1}\f$
-    /**
-     * @brief \f$\beta = (\Sigma+G G^T)^{-1} = (\Sigma^{-1} - \Sigma^{-1} G \alpha G^T \Sigma^{-1})^{-1}\f$
-     */
-    blitz::Array<double,2> m_cache_beta;
-    std::map<size_t, blitz::Array<double,2> > m_cache_gamma; ///< \f$\gamma_{a} = (Id + a F^T \beta F)^{-1}\f$
-    blitz::Array<double,2> m_cache_Ft_beta; ///< \f$F^{T} \beta \f$
-    blitz::Array<double,2> m_cache_Gt_isigma; ///< \f$G^{T} \Sigma^{-1} \f$
-    double m_cache_logdet_alpha; ///< \f$\log(\det(\alpha))\f$
-    double m_cache_logdet_sigma; ///< \f$\log(\det(\Sigma))\f$
-    /**
-     * @brief \f$l_{a} = \frac{a}{2} ( -D log(2*\pi) -log|\Sigma| +log|\alpha| +log|\gamma_a|)\f$
-     */
-    std::map<size_t, double> m_cache_loglike_constterm;
-
-    // working arrays
-    mutable blitz::Array<double,1> m_tmp_d_1; ///< Cache vector of size dim_d
-    mutable blitz::Array<double,1> m_tmp_d_2; ///< Cache vector of size dim_d
-    mutable blitz::Array<double,2> m_tmp_d_ng_1; ///< Cache matrix of size dim_d x dim_g
-    mutable blitz::Array<double,2> m_tmp_nf_nf_1; ///< Cache matrix of size dim_f x dim_f
-    mutable blitz::Array<double,2> m_tmp_ng_ng_1; ///< Cache matrix of size dim_g x dim_g
-
-    // private methods
-    void resizeNoInit(const size_t dim_d, const size_t dim_f, const size_t dim_g);
-    void resizeTmp();
-    void initMuFGSigma();
-    void precomputeISigma();
-    void precomputeAlpha();
-    void precomputeBeta();
-    void precomputeGamma(const size_t a);
-    void precomputeFtBeta();
-    void precomputeGtISigma();
-    void precomputeLogDetAlpha();
-    void precomputeLogDetSigma();
-    void precomputeLogLikeConstTerm(const size_t a);
-};
-
-
-/**
- * @brief This class is a container for an enrolled identity/class. It
- * contains information extracted from the enrollment samples. It should
- * be used in combination with a PLDABase instance.\n
- * References:\n
- * 1. 'A Scalable Formulation of Probabilistic Linear Discriminant Analysis:
- *     Applied to Face Recognition', Laurent El Shafey, Chris McCool,
- *     Roy Wallace, Sebastien Marcel, TPAMI'2013
- * 2. 'Probabilistic Linear Discriminant Analysis for Inference About
- *     Identity', Prince and Elder, ICCV'2007\n
- * 3. 'Probabilistic Models for Inference about Identity', Li, Fu, Mohammed,
- *     Elder and Prince, TPAMI'2012
- */
-class PLDAMachine
-{
-  public:
-    /**
-     * @brief Default constructor.\n
-     * Builds an otherwise invalid (No attached PLDABase) PLDAMachine.
-     */
-    PLDAMachine();
-    /**
-     * @brief Constructor, builds a new PLDAMachine, setting a
-     * PLDABase.
-     */
-    PLDAMachine(const boost::shared_ptr<bob::learn::em::PLDABase> pldabase);
-    /**
-     * @brief Copies another PLDAMachine.\n Both PLDAMachine's will point
-     * to the same PLDABase.
-     */
-    PLDAMachine(const PLDAMachine& other);
-    /**
-     * @brief Starts a new PLDAMachine from an existing configuration object,
-     * and a PLDABase.
-     */
-    PLDAMachine(bob::io::base::HDF5File& config,
-      const boost::shared_ptr<bob::learn::em::PLDABase> pldabase);
-
-    /**
-     * @brief Just to virtualise the destructor
-     */
-    virtual ~PLDAMachine();
-
-    /**
-     * @brief Assigns from a different machine
-     */
-    PLDAMachine& operator=(const PLDAMachine &other);
-
-    /**
-     * @brief Equal to.\n The two PLDAMachine's should have the same
-     * PLDABase. Precomputed members such as \f$\gamma_a\f$'s
-     * are compared!
-     */
-    bool operator==(const PLDAMachine& b) const;
-    /**
-     * @brief Not equal to.\n Defined as the negation of operator==
-     */
-    bool operator!=(const PLDAMachine& b) const;
-    /**
-     * @brief Equal to.\n The two PLDAMachine's should have the same
-     * PLDABase. Precomputed members such as \f$\gamma_a\f$'s
-     * are compared!
-     */
-    bool is_similar_to(const PLDAMachine& b, const double r_epsilon=1e-5,
-      const double a_epsilon=1e-8) const;
-
-    /**
-     * @brief Loads data from an existing configuration object. Resets the
-     * current state.
-     */
-    void load(bob::io::base::HDF5File& config);
-    /**
-     * @brief Saves an existing machine to a configuration object.
-     */
-    void save(bob::io::base::HDF5File& config) const;
-
-    /**
-     * @brief Gets the attached PLDABase
-     */
-    const boost::shared_ptr<PLDABase> getPLDABase() const
-    { return m_plda_base; }
-    /**
-     * @brief Sets the attached PLDABase
-     */
-    void setPLDABase(const boost::shared_ptr<bob::learn::em::PLDABase> plda_base);
-
-    /**
-     * @brief Gets the feature dimensionality
-     */
-    size_t getDimD() const
-    { if (!m_plda_base) throw std::runtime_error("No PLDABase set to this machine");
-      return m_plda_base->getDimD(); }
-    /**
-     * @brief Gets the size/rank the \f$F\f$ subspace/matrix of the PLDA model
-     */
-    size_t getDimF() const
-    { if (!m_plda_base) throw std::runtime_error("No PLDABase set to this machine");
-      return m_plda_base->getDimF(); }
-    /**
-     * @brief Gets the size/rank the \f$G\f$ subspace/matrix of the PLDA model
-     */
-    size_t getDimG() const
-    { if (!m_plda_base) throw std::runtime_error("No PLDABase set to this machine");
-      return m_plda_base->getDimG(); }
-
-    /**
-     * @brief Gets the number of enrolled samples
-     */
-    uint64_t getNSamples() const
-    { return m_n_samples; }
-    /**
-     * @brief Sets the number of enrolled samples
-     */
-    void setNSamples(const uint64_t n_samples)
-    { m_n_samples = n_samples; }
-    /**
-     * @brief Gets the \f$A = -0.5 \sum_{i} x_{i}^T \beta x_{i}\f$ value
-     */
-    double getWSumXitBetaXi() const
-    { return m_nh_sum_xit_beta_xi; }
-    /**
-     * @brief Sets the \f$A = -0.5 \sum_{i} x_{i}^T \beta x_{i}\f$ value
-     */
-    void setWSumXitBetaXi(const double val)
-    { m_nh_sum_xit_beta_xi = val; }
-    /**
-     * @brief Gets the current \f$\sum_{i} F^T \beta x_{i}\f$ value
-     */
-    const blitz::Array<double,1>& getWeightedSum() const
-    { return m_weighted_sum; }
-    /**
-     * @brief Sets the \f$\sum_{i} F^T \beta x_{i}\f$ value
-     */
-    void setWeightedSum(const blitz::Array<double,1>& weighted_sum);
-    /**
-     * @brief Returns the current \f$\sum_{i} F^T \beta x_{i}\f$ value
-     * in order to be updated.
-     * @warning Use with care. Only trainers should use this function for
-     * efficiency reasons.
-     */
-    blitz::Array<double,1>& updateWeightedSum()
-    { return m_weighted_sum; }
-    /**
-     * @brief Gets the log likelihood of the enrollment samples
-     */
-    double getLogLikelihood() const
-    { return m_loglikelihood; }
-    /**
-     * @brief Sets the log likelihood of the enrollment samples
-     */
-    void setLogLikelihood(const double val)
-    { m_loglikelihood = val; }
-
-    /**
-     * @brief Tells if the \f$\gamma_a\f$ matrix for a given \f$a\f$ (number
-     * of samples) exists in this machine (does not check the base machine)
-     * \f$\gamma_a = (Id + a F^T \beta F)^{-1} = \mathcal{F}_{a}\f$
-     */
-    bool hasGamma(const size_t a) const
-    { return (m_cache_gamma.find(a) != m_cache_gamma.end()); }
-    /**
-     * @brief Gets the \f$\gamma_a\f$ matrix for a given \f$a\f$ (number of
-     * samples) \f$\gamma_a = (Id + a F^T \beta F)^{-1} = \mathcal{F}_{a}\f$
-     * Tries to find it from the base machine and then from this machine
-     * @warning an exception is thrown if gamma does not exists
-     */
-    const blitz::Array<double,2>& getGamma(const size_t a) const;
-    /**
-     * @brief Gets the \f$\gamma_a\f$ matrix for a given \f$a\f$ (number of
-     * samples) \f$\gamma_a = (Id + a F^T \beta F)^{-1} = \mathcal{F}_{a}\f$
-     * Tries to find it from the base machine and then from this machine
-     * @warning The matrix is computed if it does not already exists,
-     *   and stored in this machine
-     */
-    const blitz::Array<double,2>& getAddGamma(const size_t a);
-
-    /**
-     * @brief Tells if the log likelihood constant term for a given \f$a\f$
-     * (number of samples) exists in this machine
-     * (does not check the base machine)
-     * \f$l_{a} = \frac{a}{2} ( -D log(2\pi) -log|\Sigma| +log|\alpha| +log|\gamma_a|)\f$
-     */
-    bool hasLogLikeConstTerm(const size_t a) const
-    { return (m_cache_loglike_constterm.find(a) != m_cache_loglike_constterm.end()); }
-    /**
-     * @brief Gets the log likelihood constant term for a given \f$a\f$
-     * (number of samples)
-     * Tries to find it from the base machine and then from this machine
-     * \f$l_{a} = \frac{a}{2} ( -D log(2\pi) -log|\Sigma| +log|\alpha| +log|\gamma_a|)\f$
-     * @warning an exception is thrown if the value does not exists
-     */
-    double getLogLikeConstTerm(const size_t a) const;
-    /**
-     * @brief Gets the log likelihood constant term for a given \f$a\f$
-     * (number of samples)
-     * Tries to find it from the base machine and then from this machine
-     * \f$l_{a} = \frac{a}{2} ( -D log(2\pi) -log|\Sigma| +log|\alpha| +log|\gamma_a|)\f$
-     * @warning The value is computed if it does not already exists
-     */
-    double getAddLogLikeConstTerm(const size_t a);
-
-    /**
-     * @brief Clears the maps (\f$\gamma_a\f$ and loglike_constterm[a]).
-     */
-    void clearMaps();
-
-
-    /**
-     * @brief Compute the log-likelihood of the given sample and (optionally)
-     * the enrolled samples
-     */
-    double computeLogLikelihood(const blitz::Array<double,1>& sample,
-      bool with_enrolled_samples=true) const;
-    /**
-     * @brief Compute the log-likelihood of the given samples and (optionally)
-     * the enrolled samples
-     */
-    double computeLogLikelihood(const blitz::Array<double,2>& samples,
-      bool with_enrolled_samples=true) const;
-
-    /**
-     * @brief Computes a log likelihood ratio from a 1D or 2D blitz::Array
-     */
-    double forward(const blitz::Array<double,1>& sample);
-    double forward_(const blitz::Array<double,1>& sample);
-    double forward(const blitz::Array<double,2>& samples);
-
-
-  private:
-    /**
-     * @brief Associated PLDABase containing the model (\f$\mu\f$,
-     * \f$F\f$, \f$G\f$ and \f$\Sigma\f$)
-     */
-    boost::shared_ptr<PLDABase> m_plda_base;
-    uint64_t m_n_samples; ///< Number of enrollment samples
-    /**
-     * @brief Contains the value:\n
-     * \f$A = -0.5 (\sum_{i} x_{i}^{T} \Sigma^{-1} x_{i} - x_{i}^T \Sigma^{-1} G \alpha G^{T} \Sigma^{-1} x_{i})\f$\n
-     * \f$A = -0.5 \sum_{i} x_{i}^T \beta x_{i}\f$\n
-     * used in the likelihood computation (first \f$x_{i}\f$ dependent term)
-     */
-    double m_nh_sum_xit_beta_xi;
-    /**
-     * @brief Contains the value \f$\sum_{i} F^T \beta x_{i}\f$ used in the
-     * likelihood computation (for the second \f$x_{i}\f$ dependent term)
-     */
-    blitz::Array<double,1> m_weighted_sum;
-    double m_loglikelihood; ///< Log likelihood of the enrollment samples
-    /**
-     * @brief \f$\gamma_a\f$ balues which are not already in the
-     * PLDABase \f$\gamma_a = (Id + a F^T \beta F)^{-1}\f$
-     * (depend on the number of samples \f$a\f$)
-     */
-    std::map<size_t, blitz::Array<double,2> > m_cache_gamma;
-    /**
-     * @brief Log likelihood constant terms which depend on the number of
-     * samples \f$a\f$
-     * \f$l_{a} = \frac{a}{2} ( -D log(2\pi) -log|\Sigma| +log|\alpha| +log|\gamma_a|)\f$
-     */
-    std::map<size_t, double> m_cache_loglike_constterm;
-
-
-    // working arrays
-    mutable blitz::Array<double,1> m_tmp_d_1; ///< Cache vector of size dim_d
-    mutable blitz::Array<double,1> m_tmp_d_2; ///< Cache vector of size dim_d
-    mutable blitz::Array<double,1> m_tmp_nf_1; ///< Cache vector of size dim_f
-    mutable blitz::Array<double,1> m_tmp_nf_2; ///< Cache vector of size dim_f
-    mutable blitz::Array<double,2> m_tmp_nf_nf_1; ///< Cache vector of size dim_f dim_f
-
-    /**
-     * @brief Resizes the PLDAMachine
-     */
-    void resize(const size_t dim_d, const size_t dim_f, const size_t dim_g);
-    /**
-     * @brief Resize working arrays
-     */
-    void resizeTmp();
-};
-
-} } } // namespaces
-
-#endif // BOB_LEARN_EM_PLDAMACHINE_H
diff --git a/bob/learn/em/include/bob.learn.em/PLDATrainer.h b/bob/learn/em/include/bob.learn.em/PLDATrainer.h
deleted file mode 100644
index f5bc4024c14b3e013e52795201fe9ba72f9ba813..0000000000000000000000000000000000000000
--- a/bob/learn/em/include/bob.learn.em/PLDATrainer.h
+++ /dev/null
@@ -1,310 +0,0 @@
-/**
- * @date Fri Oct 14 18:07:56 2011 +0200
- * @author Laurent El Shafey <Laurent.El-Shafey@idiap.ch>
- *
- * @brief Probabilistic PLDA Discriminant Analysis implemented using
- * Expectation Maximization.
- *
- * Copyright (C) Idiap Research Institute, Martigny, Switzerland
- */
-
-#ifndef BOB_LEARN_EM_PLDA_TRAINER_H
-#define BOB_LEARN_EM_PLDA_TRAINER_H
-
-#include <bob.learn.em/PLDAMachine.h>
-#include <boost/shared_ptr.hpp>
-#include <vector>
-#include <map>
-#include <bob.core/array_copy.h>
-#include <boost/random.hpp>
-#include <boost/random/mersenne_twister.hpp>
-
-namespace bob { namespace learn { namespace em {
-
-/**
- * @brief This class can be used to train the \f$F\f$, \f$G\f$ and
- * \f$\Sigma\f$ matrices and the mean vector \f$\mu\f$ of a PLDA model.\n
- * References:\n
- * 1. 'A Scalable Formulation of Probabilistic Linear Discriminant Analysis:
- *     Applied to Face Recognition', Laurent El Shafey, Chris McCool,
- *     Roy Wallace, Sebastien Marcel, TPAMI'2013
- * 2. 'Probabilistic Linear Discriminant Analysis for Inference About
- *     Identity', Prince and Elder, ICCV'2007\n
- * 3. 'Probabilistic Models for Inference about Identity', Li, Fu, Mohammed,
- *     Elder and Prince, TPAMI'2012
- */
-class PLDATrainer
-{
-  public: //api
-    /**
-     * @brief Default constructor.\n Initializes a new PLDA trainer. The
-     * training stage will place the resulting components in the
-     * PLDABase.
-     */
-    PLDATrainer(const bool use_sum_second_order);
-
-    /**
-     * @brief Copy constructor
-     */
-    PLDATrainer(const PLDATrainer& other);
-
-    /**
-     * @brief (virtual) Destructor
-     */
-    virtual ~PLDATrainer();
-
-    /**
-     * @brief Assignment operator
-     */
-    PLDATrainer& operator=(const PLDATrainer& other);
-
-    /**
-     * @brief Equal to
-     */
-    bool operator==(const PLDATrainer& other) const;
-
-    /**
-     * @brief Not equal to
-     */
-    bool operator!=(const PLDATrainer& other) const;
-
-    /**
-     * @brief Similarity operator
-     */
-    bool is_similar_to(const PLDATrainer& b,
-      const double r_epsilon=1e-5, const double a_epsilon=1e-8) const;
-
-    /**
-     * @brief Performs some initialization before the E- and M-steps.
-     */
-    void initialize(bob::learn::em::PLDABase& machine,
-      const std::vector<blitz::Array<double,2> >& v_ar);
-    /**
-     * @brief Performs some actions after the end of the E- and M-steps.
-      */
-    void finalize(bob::learn::em::PLDABase& machine,
-      const std::vector<blitz::Array<double,2> >& v_ar);
-
-    /**
-     * @brief Calculates and saves statistics across the dataset, and saves
-     * these as m_z_{first,second}_order.
-     * The statistics will be used in the mStep() that follows.
-     */
-    void eStep(bob::learn::em::PLDABase& machine,
-      const std::vector<blitz::Array<double,2> >& v_ar);
-
-    /**
-     * @brief Performs a maximization step to update the parameters of the
-     * PLDABase
-     */
-    void mStep(bob::learn::em::PLDABase& machine,
-       const std::vector<blitz::Array<double,2> >& v_ar);
-
-
-    /**
-     * @brief Sets whether the second order statistics are stored during the
-     * training procedure, or only their sum.
-     */
-    void setUseSumSecondOrder(bool v) { m_use_sum_second_order = v; }
-    /**
-     * @brief Tells whether the second order statistics are stored during the
-     * training procedure, or only their sum.
-     */
-    bool getUseSumSecondOrder() const
-    { return m_use_sum_second_order; }
-
-    /**
-     * @brief This enum defines different methods for initializing the \f$F\f$
-     * subspace
-     */
-    typedef enum {
-      RANDOM_F=0,
-      BETWEEN_SCATTER=1
-    }
-    InitFMethod;
-    /**
-     * @brief This enum defines different methods for initializing the \f$G\f$
-     * subspace
-     */
-    typedef enum {
-      RANDOM_G=0,
-      WITHIN_SCATTER=1
-    }
-    InitGMethod;
-    /**
-     * @brief This enum defines different methods for initializing the
-     * \f$\Sigma\f$ covariance matrix
-     */
-    typedef enum {
-      RANDOM_SIGMA=0,
-      VARIANCE_G=1,
-      CONSTANT=2,
-      VARIANCE_DATA=3
-    }
-    InitSigmaMethod;
-    /**
-     * @brief Sets the method used to initialize \f$F\f$
-     */
-    void setInitFMethod(const InitFMethod m) { m_initF_method = m; }
-    /**
-     * @brief Gets the method used to initialize \f$F\f$
-     */
-    InitFMethod getInitFMethod() const { return m_initF_method; }
-    /**
-     * @brief Sets the ratio value used to initialize \f$F\f$
-     */
-    void setInitFRatio(double d) { m_initF_ratio = d; }
-    /**
-     * @brief Gets the ratio value used to initialize \f$F\f$
-     */
-    double getInitFRatio() const { return m_initF_ratio; }
-    /**
-     * @brief Sets the method used to initialize \f$G\f$
-     */
-    void setInitGMethod(const InitGMethod m) { m_initG_method = m; }
-    /**
-     * @brief Gets the method used to initialize \f$G\f$
-     */
-    InitGMethod getInitGMethod() const { return m_initG_method; }
-    /**
-     * @brief Sets the ratio value used to initialize \f$G\f$
-     */
-    void setInitGRatio(double d) { m_initG_ratio = d; }
-    /**
-     * @brief Gets the ratio value used to initialize \f$G\f$
-     */
-    double getInitGRatio() const { return m_initG_ratio; }
-    /**
-     * @brief Sets the method used to initialize \f$\Sigma\f$
-     */
-    void setInitSigmaMethod(const InitSigmaMethod m)
-    { m_initSigma_method = m; }
-    /**
-     * @brief Gets the method used to initialize \f$\Sigma\f$
-     */
-    InitSigmaMethod getInitSigmaMethod() const
-    { return m_initSigma_method; }
-    /**
-     * @brief Sets the ratio value used to initialize \f$\Sigma\f$
-     */
-    void setInitSigmaRatio(double d) { m_initSigma_ratio = d; }
-    /**
-     * @brief Gets the ratio value used to initialize \f$\Sigma\f$
-     */
-    double getInitSigmaRatio() const { return m_initSigma_ratio; }
-
-    /**
-     * @brief Gets the z first order statistics (mostly for test purposes)
-     */
-    const std::vector<blitz::Array<double,2> >& getZFirstOrder() const
-    { return m_cache_z_first_order;}
-    /**
-     * @brief Gets the z second order statistics (mostly for test purposes)
-     */
-    const blitz::Array<double,2>& getZSecondOrderSum() const
-    { return m_cache_sum_z_second_order;}
-    /**
-     * @brief Gets the z second order statistics (mostly for test purposes)
-     */
-    const std::vector<blitz::Array<double,3> >& getZSecondOrder() const
-    { if(m_use_sum_second_order)
-        throw std::runtime_error("You should disable the use_sum_second_order flag to use this feature");
-      return m_cache_z_second_order;
-    }
-
-    /**
-     * @brief Main procedure for enrolling a PLDAMachine
-     */
-    void enroll(bob::learn::em::PLDAMachine& plda_machine,
-      const blitz::Array<double,2>& ar) const;
-
-
-    /**
-     * @brief Sets the Random Number Generator
-     */
-    void setRng(boost::shared_ptr<boost::mt19937> rng)
-    { m_rng = rng; }
-
-    /**
-     * @brief Gets the Random Number Generator
-     */
-    boost::shared_ptr<boost::mt19937> getRng() const
-    { return m_rng; }
-
-  private:
-
-	    boost::shared_ptr<boost::mt19937> m_rng;
-
-	    //representation
-	    size_t m_dim_d; ///< Dimensionality of the input features
-	    size_t m_dim_f; ///< Size/rank of the \f$F\f$ subspace
-	    size_t m_dim_g; ///< Size/rank of the \f$G\f$ subspace
-	    bool m_use_sum_second_order; ///< If set, only the sum of the second order statistics is stored/allocated
-	    InitFMethod m_initF_method; ///< Initialization method for \f$F\f$
-	    double m_initF_ratio; ///< Ratio/factor used for the initialization of \f$F\f$
-	    InitGMethod m_initG_method; ///< Initialization method for \f$G\f$
-	    double m_initG_ratio; ///< Ratio/factor used for the initialization of \f$G\f$
-	    InitSigmaMethod m_initSigma_method; ///< Initialization method for \f$\Sigma\f$
-	    double m_initSigma_ratio; ///< Ratio/factor used for the initialization of \f$\Sigma\f$
-
-	    // Statistics and covariance computed during the training process
-	    blitz::Array<double,2> m_cache_S; ///< Covariance of the training data
-	    std::vector<blitz::Array<double,2> > m_cache_z_first_order; ///< Current mean of the z_{n} latent variable (1 for each sample)
-	    blitz::Array<double,2> m_cache_sum_z_second_order; ///< Current sum of the covariance of the z_{n} latent variable
-	    std::vector<blitz::Array<double,3> > m_cache_z_second_order; ///< Current covariance of the z_{n} latent variable
-	    // Precomputed
-	    /**
-	     * @brief Number of training samples for each individual in the training set
-	     */
-	    std::vector<size_t> m_cache_n_samples_per_id;
-	    /**
-	     * @brief Tells if there is an identity with a 'key'/particular number of
-     * training samples, and if corresponding matrices are up to date.
-     */
-    std::map<size_t,bool> m_cache_n_samples_in_training;
-    blitz::Array<double,2> m_cache_B; ///< \f$B = [F, G]\f$ (size nfeatures x (m_dim_f+m_dim_g) )
-    blitz::Array<double,2> m_cache_Ft_isigma_G; ///< \f$F^T \Sigma^-1 G\f$
-    blitz::Array<double,2> m_cache_eta; ///< \f$F^T \Sigma^-1 G \alpha\f$
-    // Blocks (with \f$\gamma_{a}\f$) of \f$(Id + A^T \Sigma'^-1 A)^-1\f$ (efficient inversion)
-    std::map<size_t,blitz::Array<double,2> > m_cache_zeta; ///< \f$\zeta_{a} = \alpha + \eta^T \gamma_{a} \eta\f$
-    std::map<size_t,blitz::Array<double,2> > m_cache_iota; ///< \f$\iota_{a} = -\gamma_{a} \eta\f$
-
-    // Working arrays
-    mutable blitz::Array<double,1> m_tmp_nf_1; ///< vector of dimension dim_f
-    mutable blitz::Array<double,1> m_tmp_nf_2; ///< vector of dimension dim_f
-    mutable blitz::Array<double,1> m_tmp_ng_1; ///< vector of dimension dim_f
-    mutable blitz::Array<double,1> m_tmp_D_1; ///< vector of dimension dim_d
-    mutable blitz::Array<double,1> m_tmp_D_2; ///< vector of dimension dim_d
-    mutable blitz::Array<double,2> m_tmp_nfng_nfng; ///< matrix of dimension (dim_f+dim_g)x(dim_f+dim_g)
-    mutable blitz::Array<double,2> m_tmp_D_nfng_1; ///< matrix of dimension (dim_d)x(dim_f+dim_g)
-    mutable blitz::Array<double,2> m_tmp_D_nfng_2; ///< matrix of dimension (dim_d)x(dim_f+dim_g)
-
-    // internal methods
-    void computeMeanVariance(bob::learn::em::PLDABase& machine,
-      const std::vector<blitz::Array<double,2> >& v_ar);
-    void initMembers(const std::vector<blitz::Array<double,2> >& v_ar);
-    void initFGSigma(bob::learn::em::PLDABase& machine,
-      const std::vector<blitz::Array<double,2> >& v_ar);
-    void initF(bob::learn::em::PLDABase& machine,
-      const std::vector<blitz::Array<double,2> >& v_ar);
-    void initG(bob::learn::em::PLDABase& machine,
-      const std::vector<blitz::Array<double,2> >& v_ar);
-    void initSigma(bob::learn::em::PLDABase& machine,
-      const std::vector<blitz::Array<double,2> >& v_ar);
-
-    void checkTrainingData(const std::vector<blitz::Array<double,2> >& v_ar);
-    void precomputeFromFGSigma(bob::learn::em::PLDABase& machine);
-    void precomputeLogLike(bob::learn::em::PLDABase& machine,
-      const std::vector<blitz::Array<double,2> >& v_ar);
-
-    void updateFG(bob::learn::em::PLDABase& machine,
-      const std::vector<blitz::Array<double,2> >& v_ar);
-    void updateSigma(bob::learn::em::PLDABase& machine,
-      const std::vector<blitz::Array<double,2> >& v_ar);
-
-    void resizeTmp();
-};
-
-} } } // namespaces
-
-#endif /* BOB_LEARN_EM_PLDA_TRAINER_H */
diff --git a/bob/learn/em/include/bob.learn.em/api.h b/bob/learn/em/include/bob.learn.em/api.h
deleted file mode 100644
index 7548a302f751d0a20b13d5966affffed670b8bf6..0000000000000000000000000000000000000000
--- a/bob/learn/em/include/bob.learn.em/api.h
+++ /dev/null
@@ -1,127 +0,0 @@
-/**
- * @author Tiago de Freitas Pereira <tiago.pereira@idiap.ch>
- * @date Fri 21 Nov 10:38:48 2013
- *
- * @brief Python API for bob::learn::em
- */
-
-#ifndef BOB_LEARN_EM_API_H
-#define BOB_LEARN_EM_API_H
-
-/* Define Module Name and Prefix for other Modules
-   Note: We cannot use BOB_EXT_* macros here, unfortunately */
-#define BOB_LEARN_EM_PREFIX    "bob.learn.em"
-#define BOB_LEARN_EM_FULL_NAME "bob.learn.em._library"
-
-#include <Python.h>
-
-#include <bob.learn.em/config.h>
-#include <boost/shared_ptr.hpp>
-
-/*******************
- * C API functions *
- *******************/
-
-/* Enum defining entries in the function table */
-enum _PyBobLearnEM_ENUM{
-  PyBobLearnEM_APIVersion_NUM = 0,
-  // bindings
-  ////PyBobIpBaseLBP_Type_NUM,
-  ////PyBobIpBaseLBP_Check_NUM,
-  ////PyBobIpBaseLBP_Converter_NUM,
-  // Total number of C API pointers
-  PyBobLearnEM_API_pointers
-};
-
-
-#ifdef BOB_LEARN_EM_MODULE
-
-  /* This section is used when compiling `bob.io.base' itself */
-
-  /**************
-   * Versioning *
-   **************/
-
-  extern int PyBobLearnEM_APIVersion;
-
-#else // BOB_LEARN_EM_MODULE
-
-  /* This section is used in modules that use `bob.io.base's' C-API */
-
-#if defined(NO_IMPORT_ARRAY)
-  extern void **PyBobLearnEM_API;
-#elif defined(PY_ARRAY_UNIQUE_SYMBOL)
-  void **PyBobLearnEM_API;
-#else
-  static void **PyBobLearnEM_API=NULL;
-#endif
-
-  /**************
-   * Versioning *
-   **************/
-
-#define PyBobLearnEM_APIVersion (*(int *)PyBobLearnEM_API[PyBobLearnEM_APIVersion_NUM])
-
-#if !defined(NO_IMPORT_ARRAY)
-
-  /**
-   * Returns -1 on error, 0 on success.
-   */
-  static int import_bob_learn_em(void) {
-
-    PyObject *c_api_object;
-    PyObject *module;
-
-    module = PyImport_ImportModule(BOB_LEARN_EM_FULL_NAME);
-
-    if (module == NULL) return -1;
-
-    c_api_object = PyObject_GetAttrString(module, "_C_API");
-
-    if (c_api_object == NULL) {
-      Py_DECREF(module);
-      return -1;
-    }
-
-#if PY_VERSION_HEX >= 0x02070000
-    if (PyCapsule_CheckExact(c_api_object)) {
-      PyBobLearnEM_API = (void **)PyCapsule_GetPointer(c_api_object, PyCapsule_GetName(c_api_object));
-    }
-#else
-    if (PyCObject_Check(c_api_object)) {
-      PyBobLearnEM_API = (void **)PyCObject_AsVoidPtr(c_api_object);
-    }
-#endif
-
-    Py_DECREF(c_api_object);
-    Py_DECREF(module);
-
-    if (!PyBobLearnEM_API) {
-      PyErr_SetString(PyExc_ImportError, "cannot find C/C++ API "
-#if PY_VERSION_HEX >= 0x02070000
-          "capsule"
-#else
-          "cobject"
-#endif
-          " at `" BOB_LEARN_EM_FULL_NAME "._C_API'");
-      return -1;
-    }
-
-    /* Checks that the imported version matches the compiled version */
-    int imported_version = *(int*)PyBobLearnEM_API[PyBobLearnEM_APIVersion_NUM];
-
-    if (BOB_LEARN_EM_API_VERSION != imported_version) {
-      PyErr_Format(PyExc_ImportError, BOB_LEARN_EM_FULL_NAME " import error: you compiled against API version 0x%04x, but are now importing an API with version 0x%04x which is not compatible - check your Python runtime environment for errors", BOB_LEARN_EM_API_VERSION, imported_version);
-      return -1;
-    }
-
-    /* If you get to this point, all is good */
-    return 0;
-
-  }
-
-#endif //!defined(NO_IMPORT_ARRAY)
-
-#endif /* BOB_LEARN_EM_MODULE */
-
-#endif /* BOB_LEARN_EM_API_H */
diff --git a/bob/learn/em/include/bob.learn.em/config.h b/bob/learn/em/include/bob.learn.em/config.h
deleted file mode 100644
index 71676a62966c87370c320b1c692ed5fa78f531e6..0000000000000000000000000000000000000000
--- a/bob/learn/em/include/bob.learn.em/config.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/**
- * @author Manuel Guenther <manuel.guenther@idiap.ch>
- * @date Thu Aug 21 20:49:42 CEST 2014
- *
- * @brief General directives for all modules in bob.learn.em
- */
-
-#ifndef BOB_LEARN_EM_CONFIG_H
-#define BOB_LEARN_EM_CONFIG_H
-
-/* Macros that define versions and important names */
-#define BOB_LEARN_EM_API_VERSION 0x0200
-
-#ifdef BOB_IMPORT_VERSION
-
-  /***************************************
-  * Here we define some functions that should be used to build version dictionaries in the version.cpp file
-  * There will be a compiler warning, when these functions are not used, so use them!
-  ***************************************/
-
-  #include <Python.h>
-  #include <boost/preprocessor/stringize.hpp>
-
-  /**
-   * bob.learn.em c/c++ api version
-   */
-  static PyObject* bob_learn_em_version() {
-    return Py_BuildValue("{ss}", "api", BOOST_PP_STRINGIZE(BOB_LEARN_EM_API_VERSION));
-  }
-
-#endif // BOB_IMPORT_VERSION
-
-#endif /* BOB_LEARN_EM_CONFIG_H */
diff --git a/bob/learn/em/isv_base.cpp b/bob/learn/em/isv_base.cpp
deleted file mode 100644
index 352e8ef66ed09519cdcbd96804fbbc97dee6e545..0000000000000000000000000000000000000000
--- a/bob/learn/em/isv_base.cpp
+++ /dev/null
@@ -1,562 +0,0 @@
-/**
- * @date Wed Jan 28 11:13:15 2015 +0200
- * @author Tiago de Freitas Pereira <tiago.pereira@idiap.ch>
- *
- * @brief Python API for bob::learn::em
- *
- * Copyright (C) 2011-2014 Idiap Research Institute, Martigny, Switzerland
- */
-
-#include "main.h"
-
-/******************************************************************/
-/************ Constructor Section *********************************/
-/******************************************************************/
-
-static auto ISVBase_doc = bob::extension::ClassDoc(
-  BOB_EXT_MODULE_PREFIX ".ISVBase",
-
-  "A ISVBase instance can be seen as a container for U and D when performing Joint Factor Analysis (JFA).\n\n"
-  "References: [Vogt2008]_ [McCool2013]_",
-  ""
-).add_constructor(
-  bob::extension::FunctionDoc(
-    "__init__",
-    "Creates a ISVBase",
-    "",
-    true
-  )
-  .add_prototype("ubm,ru","")
-  .add_prototype("other","")
-  .add_prototype("hdf5","")
-  .add_prototype("","")
-
-  .add_parameter("ubm", ":py:class:`bob.learn.em.GMMMachine`", "The Universal Background Model.")
-  .add_parameter("ru", "int", "Size of U (Within client variation matrix). In the end the U matrix will have (number_of_gaussians * feature_dimension x ru)")
-  .add_parameter("other", ":py:class:`bob.learn.em.ISVBase`", "A ISVBase object to be copied.")
-  .add_parameter("hdf5", ":py:class:`bob.io.base.HDF5File`", "An HDF5 file open for reading")
-
-);
-
-
-static int PyBobLearnEMISVBase_init_copy(PyBobLearnEMISVBaseObject* self, PyObject* args, PyObject* kwargs) {
-
-  char** kwlist = ISVBase_doc.kwlist(1);
-  PyBobLearnEMISVBaseObject* o;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", kwlist, &PyBobLearnEMISVBase_Type, &o)){
-    ISVBase_doc.print_usage();
-    return -1;
-  }
-
-  self->cxx.reset(new bob::learn::em::ISVBase(*o->cxx));
-  return 0;
-}
-
-
-static int PyBobLearnEMISVBase_init_hdf5(PyBobLearnEMISVBaseObject* self, PyObject* args, PyObject* kwargs) {
-
-  char** kwlist = ISVBase_doc.kwlist(2);
-
-  PyBobIoHDF5FileObject* config = 0;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, &PyBobIoHDF5File_Converter, &config)){
-    ISVBase_doc.print_usage();
-    return -1;
-  }
-  auto config_ = make_safe(config);
-
-  self->cxx.reset(new bob::learn::em::ISVBase(*(config->f)));
-
-  return 0;
-}
-
-
-static int PyBobLearnEMISVBase_init_ubm(PyBobLearnEMISVBaseObject* self, PyObject* args, PyObject* kwargs) {
-
-  char** kwlist = ISVBase_doc.kwlist(0);
-  
-  PyBobLearnEMGMMMachineObject* ubm;
-  int ru = 1;
-
-  //Here we have to select which keyword argument to read  
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!i", kwlist, &PyBobLearnEMGMMMachine_Type, &ubm, &ru)){
-    ISVBase_doc.print_usage();
-    return -1;
-  }
-  
-  if(ru < 0){
-    PyErr_Format(PyExc_TypeError, "ru argument must be greater than or equal to one");
-    return -1;
-  }
-  
-  self->cxx.reset(new bob::learn::em::ISVBase(ubm->cxx, ru));
-  return 0;
-}
-
-
-static int PyBobLearnEMISVBase_init(PyBobLearnEMISVBaseObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  // get the number of command line arguments
-  int nargs = (args?PyTuple_Size(args):0) + (kwargs?PyDict_Size(kwargs):0);
-    
-  switch (nargs) {
-
-    case 1:{
-      //Reading the input argument
-      PyObject* arg = 0;
-      if (PyTuple_Size(args))
-        arg = PyTuple_GET_ITEM(args, 0);
-      else {
-        PyObject* tmp = PyDict_Values(kwargs);
-        auto tmp_ = make_safe(tmp);
-        arg = PyList_GET_ITEM(tmp, 0);
-      }
-
-      // If the constructor input is Gaussian object
-     if (PyBobLearnEMISVBase_Check(arg))
-       return PyBobLearnEMISVBase_init_copy(self, args, kwargs);
-      // If the constructor input is a HDF5
-     else if (PyBobIoHDF5File_Check(arg))
-       return PyBobLearnEMISVBase_init_hdf5(self, args, kwargs);
-    }
-    case 2:
-      return PyBobLearnEMISVBase_init_ubm(self, args, kwargs);
-    default:
-      PyErr_Format(PyExc_RuntimeError, "number of arguments mismatch - %s requires 1 or 2 arguments, but you provided %d (see help)", Py_TYPE(self)->tp_name, nargs);
-      ISVBase_doc.print_usage();
-      return -1;
-  }
-  BOB_CATCH_MEMBER("cannot create ISVBase", -1)
-  return 0;
-}
-
-
-static void PyBobLearnEMISVBase_delete(PyBobLearnEMISVBaseObject* self) {
-  self->cxx.reset();
-  Py_TYPE(self)->tp_free((PyObject*)self);
-}
-
-static PyObject* PyBobLearnEMISVBase_RichCompare(PyBobLearnEMISVBaseObject* self, PyObject* other, int op) {
-  BOB_TRY
-
-  if (!PyBobLearnEMISVBase_Check(other)) {
-    PyErr_Format(PyExc_TypeError, "cannot compare `%s' with `%s'", Py_TYPE(self)->tp_name, Py_TYPE(other)->tp_name);
-    return 0;
-  }
-  auto other_ = reinterpret_cast<PyBobLearnEMISVBaseObject*>(other);
-  switch (op) {
-    case Py_EQ:
-      if (*self->cxx==*other_->cxx) Py_RETURN_TRUE; else Py_RETURN_FALSE;
-    case Py_NE:
-      if (*self->cxx==*other_->cxx) Py_RETURN_FALSE; else Py_RETURN_TRUE;
-    default:
-      Py_INCREF(Py_NotImplemented);
-      return Py_NotImplemented;
-  }
-  BOB_CATCH_MEMBER("cannot compare ISVBase objects", 0)
-}
-
-int PyBobLearnEMISVBase_Check(PyObject* o) {
-  return PyObject_IsInstance(o, reinterpret_cast<PyObject*>(&PyBobLearnEMISVBase_Type));
-}
-
-
-/******************************************************************/
-/************ Variables Section ***********************************/
-/******************************************************************/
-
-/***** shape *****/
-static auto shape = bob::extension::VariableDoc(
-  "shape",
-  "(int,int, int)",
-  "A tuple that represents the number of gaussians, dimensionality of each Gaussian, dimensionality of the rU (within client variability matrix) `(#Gaussians, #Inputs, #rU)`.",
-  ""
-);
-PyObject* PyBobLearnEMISVBase_getShape(PyBobLearnEMISVBaseObject* self, void*) {
-  BOB_TRY
-  return Py_BuildValue("(i,i,i)", self->cxx->getNGaussians(), self->cxx->getNInputs(), self->cxx->getDimRu());
-  BOB_CATCH_MEMBER("shape could not be read", 0)
-}
-
-/***** supervector_length *****/
-static auto supervector_length = bob::extension::VariableDoc(
-  "supervector_length",
-  "int",
-  "Returns the supervector length.",
-  "NGaussians x NInputs: Number of Gaussian components by the feature dimensionality"
-  "An exception is thrown if no Universal Background Model has been set yet."
-  ""
-);
-PyObject* PyBobLearnEMISVBase_getSupervectorLength(PyBobLearnEMISVBaseObject* self, void*) {
-  BOB_TRY
-  return Py_BuildValue("i", self->cxx->getSupervectorLength());
-  BOB_CATCH_MEMBER("supervector_length could not be read", 0)
-}
-
-
-/***** u *****/
-static auto U = bob::extension::VariableDoc(
-  "u",
-  "array_like <float, 2D>",
-  "Returns the U matrix (within client variability matrix)",
-  ""
-);
-PyObject* PyBobLearnEMISVBase_getU(PyBobLearnEMISVBaseObject* self, void*){
-  BOB_TRY
-  return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getU());
-  BOB_CATCH_MEMBER("``u`` could not be read", 0)
-}
-int PyBobLearnEMISVBase_setU(PyBobLearnEMISVBaseObject* self, PyObject* value, void*){
-  BOB_TRY
-  PyBlitzArrayObject* input;
-  if (!PyBlitzArray_Converter(value, &input)){
-    PyErr_Format(PyExc_RuntimeError, "%s %s expects a 2D array of floats", Py_TYPE(self)->tp_name, U.name());
-    return -1;
-  }
-  auto o_ = make_safe(input);
-  
-  // perform check on the input  
-  if (input->type_num != NPY_FLOAT64){
-    PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, U.name());
-    return -1;
-  }  
-
-  if (input->ndim != 2){
-    PyErr_Format(PyExc_TypeError, "`%s' only processes 2D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, U.name());
-    return -1;
-  }  
-
-  if (input->shape[0] != (Py_ssize_t)self->cxx->getU().extent(0) && input->shape[1] != self->cxx->getU().extent(1)) {
-    PyErr_Format(PyExc_TypeError, "`%s' 2D `input` array should have the shape [%" PY_FORMAT_SIZE_T "d, %" PY_FORMAT_SIZE_T "d] not [%" PY_FORMAT_SIZE_T "d, %" PY_FORMAT_SIZE_T "d] for `%s`", Py_TYPE(self)->tp_name, (Py_ssize_t)self->cxx->getU().extent(0), (Py_ssize_t)self->cxx->getU().extent(1), (Py_ssize_t)input->shape[0], (Py_ssize_t)input->shape[1], U.name());
-    return -1;
-  }  
-  
-  auto b = PyBlitzArrayCxx_AsBlitz<double,2>(input, "u");
-  if (!b) return -1;
-  self->cxx->setU(*b);
-  return 0;
-  BOB_CATCH_MEMBER("``u`` matrix could not be set", -1)
-}
-
-
-/***** d *****/
-static auto D = bob::extension::VariableDoc(
-  "d",
-  "array_like <float, 1D>",
-  "Returns the diagonal matrix diag(d) (as a 1D vector)",
-  ""
-);
-PyObject* PyBobLearnEMISVBase_getD(PyBobLearnEMISVBaseObject* self, void*){
-  BOB_TRY
-  return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getD());
-  BOB_CATCH_MEMBER("``d`` could not be read", 0)
-}
-int PyBobLearnEMISVBase_setD(PyBobLearnEMISVBaseObject* self, PyObject* value, void*){
-  BOB_TRY
-  PyBlitzArrayObject* input;
-  if (!PyBlitzArray_Converter(value, &input)){
-    PyErr_Format(PyExc_RuntimeError, "%s %s expects a 1D array of floats", Py_TYPE(self)->tp_name, D.name());
-    return -1;
-  }
-  auto o_ = make_safe(input);
-  
-  // perform check on the input  
-  if (input->type_num != NPY_FLOAT64){
-    PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, D.name());
-    return -1;
-  }  
-
-  if (input->ndim != 1){
-    PyErr_Format(PyExc_TypeError, "`%s' only processes 1D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, D.name());
-    return -1;
-  }  
-
-  if (input->shape[0] != (Py_ssize_t)self->cxx->getD().extent(0)) {
-    PyErr_Format(PyExc_TypeError, "`%s' 1D `input` array should have %" PY_FORMAT_SIZE_T "d, elements, not %" PY_FORMAT_SIZE_T "d for `%s`", Py_TYPE(self)->tp_name, (Py_ssize_t)self->cxx->getU().extent(0), (Py_ssize_t)input->shape[0], D.name());
-    return -1;
-  }  
-  
-  auto b = PyBlitzArrayCxx_AsBlitz<double,1>(input, "d");
-  if (!b) return -1;
-  self->cxx->setD(*b);
-  return 0;
-  BOB_CATCH_MEMBER("``d`` matrix could not be set", -1)
-}
-
-
-/***** ubm *****/
-static auto ubm = bob::extension::VariableDoc(
-  "ubm",
-  ":py:class:`bob.learn.em.GMMMachine`",
-  "Returns the UBM (Universal Background Model",
-  ""
-);
-PyObject* PyBobLearnEMISVBase_getUBM(PyBobLearnEMISVBaseObject* self, void*){
-  BOB_TRY
-
-  boost::shared_ptr<bob::learn::em::GMMMachine> ubm_gmmMachine = self->cxx->getUbm();
-
-  //Allocating the correspondent python object
-  PyBobLearnEMGMMMachineObject* retval =
-    (PyBobLearnEMGMMMachineObject*)PyBobLearnEMGMMMachine_Type.tp_alloc(&PyBobLearnEMGMMMachine_Type, 0);
-  retval->cxx = ubm_gmmMachine;
-
-  return Py_BuildValue("N",retval);
-  BOB_CATCH_MEMBER("ubm could not be read", 0)
-}
-int PyBobLearnEMISVBase_setUBM(PyBobLearnEMISVBaseObject* self, PyObject* value, void*){
-  BOB_TRY
-
-  if (!PyBobLearnEMGMMMachine_Check(value)){
-    PyErr_Format(PyExc_RuntimeError, "%s %s expects a :py:class:`bob.learn.em.GMMMachine`", Py_TYPE(self)->tp_name, ubm.name());
-    return -1;
-  }
-
-  PyBobLearnEMGMMMachineObject* ubm_gmmMachine = 0;
-  PyArg_Parse(value, "O!", &PyBobLearnEMGMMMachine_Type,&ubm_gmmMachine);
-
-  self->cxx->setUbm(ubm_gmmMachine->cxx);
-
-  return 0;
-  BOB_CATCH_MEMBER("ubm could not be set", -1)  
-}
-
-
-
-static PyGetSetDef PyBobLearnEMISVBase_getseters[] = { 
-  {
-   shape.name(),
-   (getter)PyBobLearnEMISVBase_getShape,
-   0,
-   shape.doc(),
-   0
-  },
-  
-  {
-   supervector_length.name(),
-   (getter)PyBobLearnEMISVBase_getSupervectorLength,
-   0,
-   supervector_length.doc(),
-   0
-  },
-  
-  {
-   U.name(),
-   (getter)PyBobLearnEMISVBase_getU,
-   (setter)PyBobLearnEMISVBase_setU,
-   U.doc(),
-   0
-  },
-  
-  {
-   D.name(),
-   (getter)PyBobLearnEMISVBase_getD,
-   (setter)PyBobLearnEMISVBase_setD,
-   D.doc(),
-   0
-  },
-
-  {
-   ubm.name(),
-   (getter)PyBobLearnEMISVBase_getUBM,
-   (setter)PyBobLearnEMISVBase_setUBM,
-   ubm.doc(),
-   0
-  },
-
-
-  {0}  // Sentinel
-};
-
-
-/******************************************************************/
-/************ Functions Section ***********************************/
-/******************************************************************/
-
-
-/*** save ***/
-static auto save = bob::extension::FunctionDoc(
-  "save",
-  "Save the configuration of the ISVBase to a given HDF5 file"
-)
-.add_prototype("hdf5")
-.add_parameter("hdf5", ":py:class:`bob.io.base.HDF5File`", "An HDF5 file open for writing");
-static PyObject* PyBobLearnEMISVBase_Save(PyBobLearnEMISVBaseObject* self,  PyObject* args, PyObject* kwargs) {
-
-  BOB_TRY
-  
-  // get list of arguments
-  char** kwlist = save.kwlist(0);  
-  PyBobIoHDF5FileObject* hdf5;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, PyBobIoHDF5File_Converter, &hdf5)) return 0;
-
-  auto hdf5_ = make_safe(hdf5);
-  self->cxx->save(*hdf5->f);
-
-  BOB_CATCH_MEMBER("cannot save the data", 0)
-  Py_RETURN_NONE;
-}
-
-/*** load ***/
-static auto load = bob::extension::FunctionDoc(
-  "load",
-  "Load the configuration of the ISVBase to a given HDF5 file"
-)
-.add_prototype("hdf5")
-.add_parameter("hdf5", ":py:class:`bob.io.base.HDF5File`", "An HDF5 file open for reading");
-static PyObject* PyBobLearnEMISVBase_Load(PyBobLearnEMISVBaseObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-  
-  char** kwlist = load.kwlist(0);  
-  PyBobIoHDF5FileObject* hdf5;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, PyBobIoHDF5File_Converter, &hdf5)) return 0;
-  
-  auto hdf5_ = make_safe(hdf5);  
-  self->cxx->load(*hdf5->f);
-
-  BOB_CATCH_MEMBER("cannot load the data", 0)
-  Py_RETURN_NONE;
-}
-
-
-/*** is_similar_to ***/
-static auto is_similar_to = bob::extension::FunctionDoc(
-  "is_similar_to",
-  
-  "Compares this ISVBase with the ``other`` one to be approximately the same.",
-  "The optional values ``r_epsilon`` and ``a_epsilon`` refer to the "
-  "relative and absolute precision for the ``weights``, ``biases`` "
-  "and any other values internal to this machine."
-)
-.add_prototype("other, [r_epsilon], [a_epsilon]","output")
-.add_parameter("other", ":py:class:`bob.learn.em.ISVBase`", "A ISVBase object to be compared.")
-.add_parameter("r_epsilon", "float", "Relative precision.")
-.add_parameter("a_epsilon", "float", "Absolute precision.")
-.add_return("output","bool","True if it is similar, otherwise false.");
-static PyObject* PyBobLearnEMISVBase_IsSimilarTo(PyBobLearnEMISVBaseObject* self, PyObject* args, PyObject* kwds) {
-
-  /* Parses input arguments in a single shot */
-  char** kwlist = is_similar_to.kwlist(0);
-
-  //PyObject* other = 0;
-  PyBobLearnEMISVBaseObject* other = 0;
-  double r_epsilon = 1.e-5;
-  double a_epsilon = 1.e-8;
-
-  if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|dd", kwlist,
-        &PyBobLearnEMISVBase_Type, &other,
-        &r_epsilon, &a_epsilon)){
-
-        is_similar_to.print_usage(); 
-        return 0;        
-  }
-
-  if (self->cxx->is_similar_to(*other->cxx, r_epsilon, a_epsilon))
-    Py_RETURN_TRUE;
-  else
-    Py_RETURN_FALSE;
-}
-
-
-/*** resize ***/
-static auto resize = bob::extension::FunctionDoc(
-  "resize",
-  "Resets the dimensionality of the subspace U. "
-  "U is hence uninitialized.",
-  0,
-  true
-)
-.add_prototype("rU")
-.add_parameter("rU", "int", "Size of U (Within client variation matrix)");
-static PyObject* PyBobLearnEMISVBase_resize(PyBobLearnEMISVBaseObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  /* Parses input arguments in a single shot */
-  char** kwlist = resize.kwlist(0);
-
-  int rU = 0;
-
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, &rU)) return 0;
-
-  if (rU <= 0){
-    PyErr_Format(PyExc_TypeError, "rU must be greater than zero");
-    resize.print_usage();
-    return 0;
-  }
-
-  self->cxx->resize(rU);
-
-  BOB_CATCH_MEMBER("cannot perform the resize method", 0)
-
-  Py_RETURN_NONE;
-}
-
-
-
-
-static PyMethodDef PyBobLearnEMISVBase_methods[] = {
-  {
-    save.name(),
-    (PyCFunction)PyBobLearnEMISVBase_Save,
-    METH_VARARGS|METH_KEYWORDS,
-    save.doc()
-  },
-  {
-    load.name(),
-    (PyCFunction)PyBobLearnEMISVBase_Load,
-    METH_VARARGS|METH_KEYWORDS,
-    load.doc()
-  },
-  {
-    is_similar_to.name(),
-    (PyCFunction)PyBobLearnEMISVBase_IsSimilarTo,
-    METH_VARARGS|METH_KEYWORDS,
-    is_similar_to.doc()
-  },
-  {
-    resize.name(),
-    (PyCFunction)PyBobLearnEMISVBase_resize,
-    METH_VARARGS|METH_KEYWORDS,
-    resize.doc()
-  },
-  
-  {0} /* Sentinel */
-};
-
-
-/******************************************************************/
-/************ Module Section **************************************/
-/******************************************************************/
-
-// Define the ISV type struct; will be initialized later
-PyTypeObject PyBobLearnEMISVBase_Type = {
-  PyVarObject_HEAD_INIT(0,0)
-  0
-};
-
-bool init_BobLearnEMISVBase(PyObject* module)
-{
-  // initialize the type struct
-  PyBobLearnEMISVBase_Type.tp_name      = ISVBase_doc.name();
-  PyBobLearnEMISVBase_Type.tp_basicsize = sizeof(PyBobLearnEMISVBaseObject);
-  PyBobLearnEMISVBase_Type.tp_flags     = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE;
-  PyBobLearnEMISVBase_Type.tp_doc       = ISVBase_doc.doc();
-
-  // set the functions
-  PyBobLearnEMISVBase_Type.tp_new         = PyType_GenericNew;
-  PyBobLearnEMISVBase_Type.tp_init        = reinterpret_cast<initproc>(PyBobLearnEMISVBase_init);
-  PyBobLearnEMISVBase_Type.tp_dealloc     = reinterpret_cast<destructor>(PyBobLearnEMISVBase_delete);
-  PyBobLearnEMISVBase_Type.tp_richcompare = reinterpret_cast<richcmpfunc>(PyBobLearnEMISVBase_RichCompare);
-  PyBobLearnEMISVBase_Type.tp_methods     = PyBobLearnEMISVBase_methods;
-  PyBobLearnEMISVBase_Type.tp_getset      = PyBobLearnEMISVBase_getseters;
-  //PyBobLearnEMISVBase_Type.tp_call = reinterpret_cast<ternaryfunc>(PyBobLearnEMISVBase_forward);
-
-
-  // check that everything is fine
-  if (PyType_Ready(&PyBobLearnEMISVBase_Type) < 0) return false;
-
-  // add the type to the module
-  Py_INCREF(&PyBobLearnEMISVBase_Type);
-  return PyModule_AddObject(module, "ISVBase", (PyObject*)&PyBobLearnEMISVBase_Type) >= 0;
-}
-
diff --git a/bob/learn/em/isv_machine.cpp b/bob/learn/em/isv_machine.cpp
deleted file mode 100644
index 097467a553d36b617fe1be644e3fddc04ba52bbf..0000000000000000000000000000000000000000
--- a/bob/learn/em/isv_machine.cpp
+++ /dev/null
@@ -1,702 +0,0 @@
-/**
- * @date Wed Jan 28 13:03:15 2015 +0200
- * @author Tiago de Freitas Pereira <tiago.pereira@idiap.ch>
- *
- * @brief Python API for bob::learn::em
- *
- * Copyright (C) 2011-2014 Idiap Research Institute, Martigny, Switzerland
- */
-
-#include "main.h"
-
-/******************************************************************/
-/************ Constructor Section *********************************/
-/******************************************************************/
-
-static auto ISVMachine_doc = bob::extension::ClassDoc(
-  BOB_EXT_MODULE_PREFIX ".ISVMachine",
-  "A ISVMachine. An attached :py:class:`bob.learn.em.ISVBase` should be provided for Joint Factor Analysis. The :py:class:`bob.learn.em.ISVMachine` carries information about the speaker factors :math:`y` and :math:`z`, whereas a :py:class:`bob.learn.em.JFABase` carries information about the matrices :math:`U` and :math:`D`.\n\n"
-  "References: [Vogt2008]_ [McCool2013]_",
-  ""
-).add_constructor(
-  bob::extension::FunctionDoc(
-    "__init__",
-    "Constructor. Builds a new ISVMachine",
-    "",
-    true
-  )
-  .add_prototype("isv_base","")
-  .add_prototype("other","")
-  .add_prototype("hdf5","")
-
-  .add_parameter("isv_base", ":py:class:`bob.learn.em.ISVBase`", "The ISVBase associated with this machine")
-  .add_parameter("other", ":py:class:`bob.learn.em.ISVMachine`", "A ISVMachine object to be copied.")
-  .add_parameter("hdf5", ":py:class:`bob.io.base.HDF5File`", "An HDF5 file open for reading")
-
-);
-
-
-static int PyBobLearnEMISVMachine_init_copy(PyBobLearnEMISVMachineObject* self, PyObject* args, PyObject* kwargs) {
-
-  char** kwlist = ISVMachine_doc.kwlist(1);
-  PyBobLearnEMISVMachineObject* o;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", kwlist, &PyBobLearnEMISVMachine_Type, &o)){
-    ISVMachine_doc.print_usage();
-    return -1;
-  }
-
-  self->cxx.reset(new bob::learn::em::ISVMachine(*o->cxx));
-  return 0;
-}
-
-
-static int PyBobLearnEMISVMachine_init_hdf5(PyBobLearnEMISVMachineObject* self, PyObject* args, PyObject* kwargs) {
-
-  char** kwlist = ISVMachine_doc.kwlist(2);
-
-  PyBobIoHDF5FileObject* config = 0;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, &PyBobIoHDF5File_Converter, &config)){
-    ISVMachine_doc.print_usage();
-    return -1;
-  }
-  auto config_ = make_safe(config);
-  self->cxx.reset(new bob::learn::em::ISVMachine(*(config->f)));
-
-  return 0;
-}
-
-
-static int PyBobLearnEMISVMachine_init_isvbase(PyBobLearnEMISVMachineObject* self, PyObject* args, PyObject* kwargs) {
-
-  char** kwlist = ISVMachine_doc.kwlist(0);
-
-  PyBobLearnEMISVBaseObject* isv_base;
-
-  //Here we have to select which keyword argument to read
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", kwlist, &PyBobLearnEMISVBase_Type, &isv_base)){
-    ISVMachine_doc.print_usage();
-    return -1;
-  }
-
-  self->cxx.reset(new bob::learn::em::ISVMachine(isv_base->cxx));
-  return 0;
-}
-
-
-static int PyBobLearnEMISVMachine_init(PyBobLearnEMISVMachineObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  // get the number of command line arguments
-  int nargs = (args?PyTuple_Size(args):0) + (kwargs?PyDict_Size(kwargs):0);
-
-  if(nargs == 1){
-    //Reading the input argument
-    PyObject* arg = 0;
-    if (PyTuple_Size(args))
-      arg = PyTuple_GET_ITEM(args, 0);
-    else {
-      PyObject* tmp = PyDict_Values(kwargs);
-      auto tmp_ = make_safe(tmp);
-      arg = PyList_GET_ITEM(tmp, 0);
-    }
-
-    // If the constructor input is Gaussian object
-    if (PyBobLearnEMISVMachine_Check(arg))
-      return PyBobLearnEMISVMachine_init_copy(self, args, kwargs);
-    // If the constructor input is a HDF5
-    else if (PyBobIoHDF5File_Check(arg))
-      return PyBobLearnEMISVMachine_init_hdf5(self, args, kwargs);
-    // If the constructor input is a JFABase Object
-    else
-      return PyBobLearnEMISVMachine_init_isvbase(self, args, kwargs);
-  }
-  else{
-    PyErr_Format(PyExc_RuntimeError, "number of arguments mismatch - %s requires only 1 argument, but you provided %d (see help)", Py_TYPE(self)->tp_name, nargs);
-    ISVMachine_doc.print_usage();
-    return -1;
-  }
-
-  BOB_CATCH_MEMBER("cannot create ISVMachine", -1)
-  return 0;
-}
-
-static void PyBobLearnEMISVMachine_delete(PyBobLearnEMISVMachineObject* self) {
-  self->cxx.reset();
-  Py_TYPE(self)->tp_free((PyObject*)self);
-}
-
-static PyObject* PyBobLearnEMISVMachine_RichCompare(PyBobLearnEMISVMachineObject* self, PyObject* other, int op) {
-  BOB_TRY
-
-  if (!PyBobLearnEMISVMachine_Check(other)) {
-    PyErr_Format(PyExc_TypeError, "cannot compare `%s' with `%s'", Py_TYPE(self)->tp_name, Py_TYPE(other)->tp_name);
-    return 0;
-  }
-  auto other_ = reinterpret_cast<PyBobLearnEMISVMachineObject*>(other);
-  switch (op) {
-    case Py_EQ:
-      if (*self->cxx==*other_->cxx) Py_RETURN_TRUE; else Py_RETURN_FALSE;
-    case Py_NE:
-      if (*self->cxx==*other_->cxx) Py_RETURN_FALSE; else Py_RETURN_TRUE;
-    default:
-      Py_INCREF(Py_NotImplemented);
-      return Py_NotImplemented;
-  }
-  BOB_CATCH_MEMBER("cannot compare ISVMachine objects", 0)
-}
-
-int PyBobLearnEMISVMachine_Check(PyObject* o) {
-  return PyObject_IsInstance(o, reinterpret_cast<PyObject*>(&PyBobLearnEMISVMachine_Type));
-}
-
-
-/******************************************************************/
-/************ Variables Section ***********************************/
-/******************************************************************/
-
-/***** shape *****/
-static auto shape = bob::extension::VariableDoc(
-  "shape",
-  "(int,int, int, int)",
-  "A tuple that represents the number of gaussians, dimensionality of each Gaussian and dimensionality of the rU (within client variability matrix)) ``(#Gaussians, #Inputs, #rU)``.",
-  ""
-);
-PyObject* PyBobLearnEMISVMachine_getShape(PyBobLearnEMISVMachineObject* self, void*) {
-  BOB_TRY
-  return Py_BuildValue("(i,i,i)", self->cxx->getNGaussians(), self->cxx->getNInputs(), self->cxx->getDimRu());
-  BOB_CATCH_MEMBER("shape could not be read", 0)
-}
-
-/***** supervector_length *****/
-static auto supervector_length = bob::extension::VariableDoc(
-  "supervector_length",
-  "int",
-
-  "Returns the supervector length.",
-  "NGaussians x NInputs: Number of Gaussian components by the feature dimensionality. "
-  "An exception is thrown if no Universal Background Model has been set yet."
-  ""
-);
-PyObject* PyBobLearnEMISVMachine_getSupervectorLength(PyBobLearnEMISVMachineObject* self, void*) {
-  BOB_TRY
-  return Py_BuildValue("i", self->cxx->getSupervectorLength());
-  BOB_CATCH_MEMBER("supervector_length could not be read", 0)
-}
-
-/***** z *****/
-static auto Z = bob::extension::VariableDoc(
-  "z",
-  "array_like <float, 1D>",
-  "Returns the :math:`z` speaker factor. Eq (31) from [McCool2013]_",
-  ""
-);
-PyObject* PyBobLearnEMISVMachine_getZ(PyBobLearnEMISVMachineObject* self, void*){
-  BOB_TRY
-  return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getZ());
-  BOB_CATCH_MEMBER("`z` could not be read", 0)
-}
-int PyBobLearnEMISVMachine_setZ(PyBobLearnEMISVMachineObject* self, PyObject* value, void*){
-  BOB_TRY
-  PyBlitzArrayObject* input;
-  if (!PyBlitzArray_Converter(value, &input)){
-    PyErr_Format(PyExc_RuntimeError, "%s %s expects a 1D array of floats", Py_TYPE(self)->tp_name, Z.name());
-    return -1;
-  }
-  auto o_ = make_safe(input);
-
-  // perform check on the input
-  if (input->type_num != NPY_FLOAT64){
-    PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, Z.name());
-    return -1;
-  }
-
-  if (input->ndim != 1){
-    PyErr_Format(PyExc_TypeError, "`%s' only processes 1D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, Z.name());
-    return -1;
-  }
-
-  if (input->shape[0] != (Py_ssize_t)self->cxx->getZ().extent(0)) {
-    PyErr_Format(PyExc_TypeError, "`%s' 1D `input` array should have %" PY_FORMAT_SIZE_T "d, elements, not %" PY_FORMAT_SIZE_T "d for `%s`", Py_TYPE(self)->tp_name, (Py_ssize_t)self->cxx->getZ().extent(0), (Py_ssize_t)input->shape[0], Z.name());
-    return -1;
-  }
-
-  auto b = PyBlitzArrayCxx_AsBlitz<double,1>(input, "z");
-  if (!b) return -1;
-  self->cxx->setZ(*b);
-  return 0;
-  BOB_CATCH_MEMBER("`z` vector could not be set", -1)
-}
-
-
-/***** x *****/
-static auto X = bob::extension::VariableDoc(
-  "x",
-  "array_like <float, 1D>",
-  "Returns the :math:`X` session factor. Eq (29) from [McCool2013]_",
-  "The latent variable x (last one computed). This is a feature provided for convenience, but this attribute is not 'part' of the machine. The session latent variable :math:`x` is indeed not class-specific, but depends on the sample considered. Furthermore, it is not saved into the machine or used when comparing machines."
-);
-PyObject* PyBobLearnEMISVMachine_getX(PyBobLearnEMISVMachineObject* self, void*){
-  BOB_TRY
-  return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getX());
-  BOB_CATCH_MEMBER("`x` could not be read", 0)
-}
-int PyBobLearnEMISVMachine_setX(PyBobLearnEMISVMachineObject* self, PyObject* value, void*){
-  BOB_TRY
-  PyBlitzArrayObject* input;
-  if (!PyBlitzArray_Converter(value, &input)){
-    PyErr_Format(PyExc_RuntimeError, "%s %s expects a 1D array of floats", Py_TYPE(self)->tp_name, X.name());
-    return -1;
-  }
-  auto o_ = make_safe(input);
-
-  // perform check on the input
-  if (input->type_num != NPY_FLOAT64){
-    PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, X.name());
-    return -1;
-  }
-
-  if (input->ndim != 1){
-    PyErr_Format(PyExc_TypeError, "`%s' only processes 1D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, X.name());
-    return -1;
-  }
-
-  if (input->shape[0] != (Py_ssize_t)self->cxx->getX().extent(0)) {
-    PyErr_Format(PyExc_TypeError, "`%s' 1D `input` array should have %" PY_FORMAT_SIZE_T "d, elements, not %" PY_FORMAT_SIZE_T "d for `%s`", Py_TYPE(self)->tp_name, (Py_ssize_t)self->cxx->getX().extent(0), (Py_ssize_t)input->shape[0], X.name());
-    return -1;
-  }
-
-  auto b = PyBlitzArrayCxx_AsBlitz<double,1>(input, "x");
-  if (!b) return -1;
-  self->cxx->setX(*b);
-  return 0;
-  BOB_CATCH_MEMBER("`x` vector could not be set", -1)
-}
-
-
-/***** isv_base *****/
-static auto isv_base = bob::extension::VariableDoc(
-  "isv_base",
-  ":py:class:`bob.learn.em.ISVBase`",
-  "The ISVBase attached to this machine",
-  ""
-);
-PyObject* PyBobLearnEMISVMachine_getISVBase(PyBobLearnEMISVMachineObject* self, void*){
-  BOB_TRY
-
-  boost::shared_ptr<bob::learn::em::ISVBase> isv_base_o = self->cxx->getISVBase();
-
-  //Allocating the correspondent python object
-  PyBobLearnEMISVBaseObject* retval =
-    (PyBobLearnEMISVBaseObject*)PyBobLearnEMISVBase_Type.tp_alloc(&PyBobLearnEMISVBase_Type, 0);
-  retval->cxx = isv_base_o;
-
-  return Py_BuildValue("N",retval);
-  BOB_CATCH_MEMBER("isv_base could not be read", 0)
-}
-int PyBobLearnEMISVMachine_setISVBase(PyBobLearnEMISVMachineObject* self, PyObject* value, void*){
-  BOB_TRY
-
-  if (!PyBobLearnEMISVBase_Check(value)){
-    PyErr_Format(PyExc_RuntimeError, "%s %s expects a :py:class:`bob.learn.em.ISVBase`", Py_TYPE(self)->tp_name, isv_base.name());
-    return -1;
-  }
-
-  PyBobLearnEMISVBaseObject* isv_base_o = 0;
-  PyArg_Parse(value, "O!", &PyBobLearnEMISVBase_Type,&isv_base_o);
-
-  self->cxx->setISVBase(isv_base_o->cxx);
-
-  return 0;
-  BOB_CATCH_MEMBER("isv_base could not be set", -1)
-}
-
-
-
-
-static PyGetSetDef PyBobLearnEMISVMachine_getseters[] = {
-  {
-   shape.name(),
-   (getter)PyBobLearnEMISVMachine_getShape,
-   0,
-   shape.doc(),
-   0
-  },
-
-  {
-   supervector_length.name(),
-   (getter)PyBobLearnEMISVMachine_getSupervectorLength,
-   0,
-   supervector_length.doc(),
-   0
-  },
-
-  {
-   isv_base.name(),
-   (getter)PyBobLearnEMISVMachine_getISVBase,
-   (setter)PyBobLearnEMISVMachine_setISVBase,
-   isv_base.doc(),
-   0
-  },
-
-  {
-   Z.name(),
-   (getter)PyBobLearnEMISVMachine_getZ,
-   (setter)PyBobLearnEMISVMachine_setZ,
-   Z.doc(),
-   0
-  },
-
-  {
-   X.name(),
-   (getter)PyBobLearnEMISVMachine_getX,
-   (setter)PyBobLearnEMISVMachine_setX,
-   X.doc(),
-   0
-  },
-
-
-  {0}  // Sentinel
-};
-
-
-/******************************************************************/
-/************ Functions Section ***********************************/
-/******************************************************************/
-
-
-/*** save ***/
-static auto save = bob::extension::FunctionDoc(
-  "save",
-  "Save the configuration of the ISVMachine to a given HDF5 file"
-)
-.add_prototype("hdf5")
-.add_parameter("hdf5", ":py:class:`bob.io.base.HDF5File`", "An HDF5 file open for writing");
-static PyObject* PyBobLearnEMISVMachine_Save(PyBobLearnEMISVMachineObject* self,  PyObject* args, PyObject* kwargs) {
-
-  BOB_TRY
-
-  // get list of arguments
-  char** kwlist = save.kwlist(0);
-  PyBobIoHDF5FileObject* hdf5;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, PyBobIoHDF5File_Converter, &hdf5)) return 0;
-
-  auto hdf5_ = make_safe(hdf5);
-  self->cxx->save(*hdf5->f);
-
-  BOB_CATCH_MEMBER("cannot save the data", 0)
-  Py_RETURN_NONE;
-}
-
-/*** load ***/
-static auto load = bob::extension::FunctionDoc(
-  "load",
-  "Load the configuration of the ISVMachine to a given HDF5 file"
-)
-.add_prototype("hdf5")
-.add_parameter("hdf5", ":py:class:`bob.io.base.HDF5File`", "An HDF5 file open for reading");
-static PyObject* PyBobLearnEMISVMachine_Load(PyBobLearnEMISVMachineObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  char** kwlist = load.kwlist(0);
-  PyBobIoHDF5FileObject* hdf5;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, PyBobIoHDF5File_Converter, &hdf5)) return 0;
-
-  auto hdf5_ = make_safe(hdf5);
-  self->cxx->load(*hdf5->f);
-
-  BOB_CATCH_MEMBER("cannot load the data", 0)
-  Py_RETURN_NONE;
-}
-
-
-/*** is_similar_to ***/
-static auto is_similar_to = bob::extension::FunctionDoc(
-  "is_similar_to",
-
-  "Compares this ISVMachine with the ``other`` one to be approximately the same.",
-  "The optional values ``r_epsilon`` and ``a_epsilon`` refer to the "
-  "relative and absolute precision for the ``weights``, ``biases`` "
-  "and any other values internal to this machine."
-)
-.add_prototype("other, [r_epsilon], [a_epsilon]","output")
-.add_parameter("other", ":py:class:`bob.learn.em.ISVMachine`", "A ISVMachine object to be compared.")
-.add_parameter("r_epsilon", "float", "Relative precision.")
-.add_parameter("a_epsilon", "float", "Absolute precision.")
-.add_return("output","bool","True if it is similar, otherwise false.");
-static PyObject* PyBobLearnEMISVMachine_IsSimilarTo(PyBobLearnEMISVMachineObject* self, PyObject* args, PyObject* kwds) {
-
-  /* Parses input arguments in a single shot */
-  char** kwlist = is_similar_to.kwlist(0);
-
-  //PyObject* other = 0;
-  PyBobLearnEMISVMachineObject* other = 0;
-  double r_epsilon = 1.e-5;
-  double a_epsilon = 1.e-8;
-
-  if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|dd", kwlist,
-        &PyBobLearnEMISVMachine_Type, &other,
-        &r_epsilon, &a_epsilon)){
-
-        is_similar_to.print_usage();
-        return 0;
-  }
-
-  if (self->cxx->is_similar_to(*other->cxx, r_epsilon, a_epsilon))
-    Py_RETURN_TRUE;
-  else
-    Py_RETURN_FALSE;
-}
-
-
-/*** estimate_x ***/
-static auto estimate_x = bob::extension::FunctionDoc(
-  "estimate_x",
-  "Estimates the session offset x (LPT assumption) given GMM statistics.",
-  "Estimates :math:`x` from the GMM statistics considering the LPT assumption, that is the latent session variable :math:`x` is approximated using the UBM",
-  true
-)
-.add_prototype("stats,input")
-.add_parameter("stats", ":py:class:`bob.learn.em.GMMStats`", "Statistics of the GMM")
-.add_parameter("input", "array_like <float, 1D>", "Input vector");
-static PyObject* PyBobLearnEMISVMachine_estimateX(PyBobLearnEMISVMachineObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  char** kwlist = estimate_x.kwlist(0);
-
-  PyBobLearnEMGMMStatsObject* stats = 0;
-  PyBlitzArrayObject* input           = 0;
-
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O&", kwlist, &PyBobLearnEMGMMStats_Type, &stats,
-                                                                 &PyBlitzArray_Converter,&input))
-    return 0;
-
-  //protects acquired resources through this scope
-  auto input_ = make_safe(input);
-
-  // perform check on the input
-  if (input->type_num != NPY_FLOAT64){
-    PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, estimate_x.name());
-    return 0;
-  }
-
-  if (input->ndim != 1){
-    PyErr_Format(PyExc_TypeError, "`%s' only processes 1D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, estimate_x.name());
-    return 0;
-  }
-
-  if (input->shape[0] != (Py_ssize_t)self->cxx->getNGaussians()) {
-    PyErr_Format(PyExc_TypeError, "`%s' 1D `input` array should have %" PY_FORMAT_SIZE_T "d, elements, not %" PY_FORMAT_SIZE_T "d for `%s`", Py_TYPE(self)->tp_name, self->cxx->getNInputs(), (Py_ssize_t)input->shape[0], estimate_x.name());
-    return 0;
-  }
-
-  self->cxx->estimateX(*stats->cxx, *PyBlitzArrayCxx_AsBlitz<double,1>(input));
-
-  BOB_CATCH_MEMBER("cannot estimate X", 0)
-  Py_RETURN_NONE;
-}
-
-
-/*** estimate_ux ***/
-static auto estimate_ux = bob::extension::FunctionDoc(
-  "estimate_ux",
-  "Estimates Ux (LPT assumption) given GMM statistics.",
-  "Estimates Ux from the GMM statistics considering the LPT assumption, that is the latent session variable x is approximated using the UBM.",
-  true
-)
-.add_prototype("stats,input")
-.add_parameter("stats", ":py:class:`bob.learn.em.GMMStats`", "Statistics of the GMM")
-.add_parameter("input", "array_like <float, 1D>", "Input vector");
-static PyObject* PyBobLearnEMISVMachine_estimateUx(PyBobLearnEMISVMachineObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  char** kwlist = estimate_ux.kwlist(0);
-
-  PyBobLearnEMGMMStatsObject* stats = 0;
-  PyBlitzArrayObject* input           = 0;
-
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O&", kwlist, &PyBobLearnEMGMMStats_Type, &stats,
-                                                                 &PyBlitzArray_Converter,&input))
-    return 0;
-
-  //protects acquired resources through this scope
-  auto input_ = make_safe(input);
-
-  // perform check on the input
-  if (input->type_num != NPY_FLOAT64){
-    PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, estimate_ux.name());
-    return 0;
-  }
-
-  if (input->ndim != 1){
-    PyErr_Format(PyExc_TypeError, "`%s' only processes 1D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, estimate_ux.name());
-    return 0;
-  }
-
-  if (input->shape[0] != (Py_ssize_t)self->cxx->getNGaussians()*(Py_ssize_t)self->cxx->getNInputs()) {
-    PyErr_Format(PyExc_TypeError, "`%s' 1D `input` array should have %" PY_FORMAT_SIZE_T "d, elements, not %" PY_FORMAT_SIZE_T "d for `%s`", Py_TYPE(self)->tp_name, self->cxx->getNInputs()*(Py_ssize_t)self->cxx->getNGaussians(), (Py_ssize_t)input->shape[0], estimate_ux.name());
-    return 0;
-  }
-
-  self->cxx->estimateUx(*stats->cxx, *PyBlitzArrayCxx_AsBlitz<double,1>(input));
-
-  BOB_CATCH_MEMBER("cannot estimate Ux", 0)
-  Py_RETURN_NONE;
-}
-
-
-/*** forward_ux ***/
-static auto forward_ux = bob::extension::FunctionDoc(
-  "forward_ux",
-  "Computes a score for the given UBM statistics and given the Ux vector",
-  "",
-  true
-)
-.add_prototype("stats,ux")
-.add_parameter("stats", ":py:class:`bob.learn.em.GMMStats`", "Statistics as input")
-.add_parameter("ux", "array_like <float, 1D>", "Input vector");
-static PyObject* PyBobLearnEMISVMachine_ForwardUx(PyBobLearnEMISVMachineObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  char** kwlist = forward_ux.kwlist(0);
-
-  PyBobLearnEMGMMStatsObject* stats = 0;
-  PyBlitzArrayObject* ux_input        = 0;
-
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O&", kwlist, &PyBobLearnEMGMMStats_Type, &stats,
-                                                                 &PyBlitzArray_Converter,&ux_input))
-    return 0;
-
-  //protects acquired resources through this scope
-  auto ux_input_ = make_safe(ux_input);
-
-  // perform check on the input
-  if (ux_input->type_num != NPY_FLOAT64){
-    PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, forward_ux.name());
-    return 0;
-  }
-
-  if (ux_input->ndim != 1){
-    PyErr_Format(PyExc_TypeError, "`%s' only processes 1D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, forward_ux.name());
-    return 0;
-  }
-
-  if (ux_input->shape[0] != (Py_ssize_t)self->cxx->getNGaussians()*(Py_ssize_t)self->cxx->getNInputs()) {
-    PyErr_Format(PyExc_TypeError, "`%s' 1D `input` array should have %" PY_FORMAT_SIZE_T "d, elements, not %" PY_FORMAT_SIZE_T "d for `%s`", Py_TYPE(self)->tp_name, (Py_ssize_t)self->cxx->getNGaussians()*(Py_ssize_t)self->cxx->getNInputs(), (Py_ssize_t)ux_input->shape[0], forward_ux.name());
-    return 0;
-  }
-  double score = self->cxx->forward(*stats->cxx, *PyBlitzArrayCxx_AsBlitz<double,1>(ux_input));
-
-  return Py_BuildValue("d", score);
-  BOB_CATCH_MEMBER("cannot forward_ux", 0)
-}
-
-
-/*** forward ***/
-static auto forward = bob::extension::FunctionDoc(
-  "forward",
-  "Execute the machine",
-  "",
-  true
-)
-.add_prototype("stats")
-.add_parameter("stats", ":py:class:`bob.learn.em.GMMStats`", "Statistics as input");
-static PyObject* PyBobLearnEMISVMachine_Forward(PyBobLearnEMISVMachineObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  char** kwlist = forward.kwlist(0);
-
-  PyBobLearnEMGMMStatsObject* stats = 0;
-
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", kwlist, &PyBobLearnEMGMMStats_Type, &stats))
-    return 0;
-
-  //protects acquired resources through this scope
-  double score = self->cxx->forward(*stats->cxx);
-
-  return Py_BuildValue("d", score);
-  BOB_CATCH_MEMBER("cannot forward", 0)
-
-}
-
-
-static PyMethodDef PyBobLearnEMISVMachine_methods[] = {
-  {
-    save.name(),
-    (PyCFunction)PyBobLearnEMISVMachine_Save,
-    METH_VARARGS|METH_KEYWORDS,
-    save.doc()
-  },
-  {
-    load.name(),
-    (PyCFunction)PyBobLearnEMISVMachine_Load,
-    METH_VARARGS|METH_KEYWORDS,
-    load.doc()
-  },
-  {
-    is_similar_to.name(),
-    (PyCFunction)PyBobLearnEMISVMachine_IsSimilarTo,
-    METH_VARARGS|METH_KEYWORDS,
-    is_similar_to.doc()
-  },
-
-  {
-    estimate_x.name(),
-    (PyCFunction)PyBobLearnEMISVMachine_estimateX,
-    METH_VARARGS|METH_KEYWORDS,
-    estimate_x.doc()
-  },
-
-  {
-    estimate_ux.name(),
-    (PyCFunction)PyBobLearnEMISVMachine_estimateUx,
-    METH_VARARGS|METH_KEYWORDS,
-    estimate_ux.doc()
-  },
-
-  {
-    forward_ux.name(),
-    (PyCFunction)PyBobLearnEMISVMachine_ForwardUx,
-    METH_VARARGS|METH_KEYWORDS,
-    forward_ux.doc()
-  },
-
-  {0} /* Sentinel */
-};
-
-
-/******************************************************************/
-/************ Module Section **************************************/
-/******************************************************************/
-
-// Define the JFA type struct; will be initialized later
-PyTypeObject PyBobLearnEMISVMachine_Type = {
-  PyVarObject_HEAD_INIT(0,0)
-  0
-};
-
-bool init_BobLearnEMISVMachine(PyObject* module)
-{
-  // initialize the type struct
-  PyBobLearnEMISVMachine_Type.tp_name      = ISVMachine_doc.name();
-  PyBobLearnEMISVMachine_Type.tp_basicsize = sizeof(PyBobLearnEMISVMachineObject);
-  PyBobLearnEMISVMachine_Type.tp_flags     = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE;
-  PyBobLearnEMISVMachine_Type.tp_doc       = ISVMachine_doc.doc();
-
-  // set the functions
-  PyBobLearnEMISVMachine_Type.tp_new         = PyType_GenericNew;
-  PyBobLearnEMISVMachine_Type.tp_init        = reinterpret_cast<initproc>(PyBobLearnEMISVMachine_init);
-  PyBobLearnEMISVMachine_Type.tp_dealloc     = reinterpret_cast<destructor>(PyBobLearnEMISVMachine_delete);
-  PyBobLearnEMISVMachine_Type.tp_richcompare = reinterpret_cast<richcmpfunc>(PyBobLearnEMISVMachine_RichCompare);
-  PyBobLearnEMISVMachine_Type.tp_methods     = PyBobLearnEMISVMachine_methods;
-  PyBobLearnEMISVMachine_Type.tp_getset      = PyBobLearnEMISVMachine_getseters;
-  PyBobLearnEMISVMachine_Type.tp_call = reinterpret_cast<ternaryfunc>(PyBobLearnEMISVMachine_Forward);
-
-
-  // check that everything is fine
-  if (PyType_Ready(&PyBobLearnEMISVMachine_Type) < 0) return false;
-
-  // add the type to the module
-  Py_INCREF(&PyBobLearnEMISVMachine_Type);
-  return PyModule_AddObject(module, "ISVMachine", (PyObject*)&PyBobLearnEMISVMachine_Type) >= 0;
-}
-
diff --git a/bob/learn/em/isv_trainer.cpp b/bob/learn/em/isv_trainer.cpp
deleted file mode 100644
index dbf310d1ee27aa999ad019aa27fef29d720b4036..0000000000000000000000000000000000000000
--- a/bob/learn/em/isv_trainer.cpp
+++ /dev/null
@@ -1,612 +0,0 @@
-/**
- * @author Tiago de Freitas Pereira <tiago.pereira@idiap.ch>
- * @date Mon 02 Fev 20:20:00 2015
- *
- * @brief Python API for bob::learn::em
- *
- * Copyright (C) 2011-2014 Idiap Research Institute, Martigny, Switzerland
- */
-
-#include "main.h"
-#include <boost/make_shared.hpp>
-
-/******************************************************************/
-/************ Constructor Section *********************************/
-/******************************************************************/
-
-static int extract_GMMStats_1d(PyObject *list,
-                             std::vector<boost::shared_ptr<bob::learn::em::GMMStats> >& training_data)
-{
-  for (int i=0; i<PyList_GET_SIZE(list); i++){
-
-    PyBobLearnEMGMMStatsObject* stats;
-    if (!PyArg_Parse(PyList_GetItem(list, i), "O!", &PyBobLearnEMGMMStats_Type, &stats)){
-      PyErr_Format(PyExc_RuntimeError, "Expected GMMStats objects");
-      return -1;
-    }
-    training_data.push_back(stats->cxx);
-  }
-  return 0;
-}
-
-static int extract_GMMStats_2d(PyObject *list,
-                             std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& training_data)
-{
-  for (int i=0; i<PyList_GET_SIZE(list); i++)
-  {
-    PyObject* another_list;
-    if(!PyArg_Parse(PyList_GetItem(list, i), "O!", &PyList_Type, &another_list)){
-      PyErr_Format(PyExc_RuntimeError, "Expected a list of lists of GMMStats objects [[GMMStats,GMMStats],[GMMStats,GMMStats].....[GMMStats,GMMStats]]");
-      return -1;
-    }
-
-    std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > another_training_data;
-    for (int j=0; j<PyList_GET_SIZE(another_list); j++){
-
-      PyBobLearnEMGMMStatsObject* stats;
-      if (!PyArg_Parse(PyList_GetItem(another_list, j), "O!", &PyBobLearnEMGMMStats_Type, &stats)){
-        PyErr_Format(PyExc_RuntimeError, "Expected GMMStats objects");
-        return -1;
-      }
-      another_training_data.push_back(stats->cxx);
-    }
-    training_data.push_back(another_training_data);
-  }
-  return 0;
-}
-
-template <int N>
-static PyObject* vector_as_list(const std::vector<blitz::Array<double,N> >& vec)
-{
-  PyObject* list = PyList_New(vec.size());
-  for(size_t i=0; i<vec.size(); i++){
-    blitz::Array<double,N> numpy_array = vec[i];
-    PyObject* numpy_py_object = PyBlitzArrayCxx_AsNumpy(numpy_array);
-    PyList_SET_ITEM(list, i, numpy_py_object);
-  }
-  return list;
-}
-
-template <int N>
-int list_as_vector(PyObject* list, std::vector<blitz::Array<double,N> >& vec)
-{
-  for (int i=0; i<PyList_GET_SIZE(list); i++)
-  {
-    PyBlitzArrayObject* blitz_object;
-    if (!PyArg_Parse(PyList_GetItem(list, i), "O&", &PyBlitzArray_Converter, &blitz_object)){
-      PyErr_Format(PyExc_RuntimeError, "Expected numpy array object");
-      return -1;
-    }
-    auto blitz_object_ = make_safe(blitz_object);
-    vec.push_back(*PyBlitzArrayCxx_AsBlitz<double,N>(blitz_object));
-  }
-  return 0;
-}
-
-
-
-static auto ISVTrainer_doc = bob::extension::ClassDoc(
-  BOB_EXT_MODULE_PREFIX ".ISVTrainer",
-  "ISVTrainer"
-  "Train Intersession varibility modeling :ref:`ISV <isv>`.",
-  ""
-).add_constructor(
-  bob::extension::FunctionDoc(
-    "__init__",
-    "Constructor. Builds a new ISVTrainer",
-    "",
-    true
-  )
-  .add_prototype("relevance_factor","")
-  .add_prototype("other","")
-  .add_prototype("","")
-  .add_parameter("other", ":py:class:`bob.learn.em.ISVTrainer`", "A ISVTrainer object to be copied.")
-  .add_parameter("relevance_factor", "float", "")
-);
-
-
-static int PyBobLearnEMISVTrainer_init_copy(PyBobLearnEMISVTrainerObject* self, PyObject* args, PyObject* kwargs) {
-
-  char** kwlist = ISVTrainer_doc.kwlist(1);
-  PyBobLearnEMISVTrainerObject* o;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", kwlist, &PyBobLearnEMISVTrainer_Type, &o)){
-    ISVTrainer_doc.print_usage();
-    return -1;
-  }
-
-  self->cxx.reset(new bob::learn::em::ISVTrainer(*o->cxx));
-  return 0;
-}
-
-
-static int PyBobLearnEMISVTrainer_init_number(PyBobLearnEMISVTrainerObject* self, PyObject* args, PyObject* kwargs) {
-
-  char** kwlist = ISVTrainer_doc.kwlist(0);
-  double relevance_factor      = 4.;
-
-  //Parsing the input argments
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "d", kwlist, &relevance_factor))
-    return -1;
-
-  if(relevance_factor < 0){
-    PyErr_Format(PyExc_TypeError, "gaussians argument must be greater than zero");
-    return -1;
-  }
-
-  self->cxx.reset(new bob::learn::em::ISVTrainer(relevance_factor));
-  return 0;
-}
-
-
-static int PyBobLearnEMISVTrainer_init(PyBobLearnEMISVTrainerObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  // get the number of command line arguments
-  int nargs = (args?PyTuple_Size(args):0) + (kwargs?PyDict_Size(kwargs):0);
-
-  switch(nargs){
-    case 0:{
-      self->cxx.reset(new bob::learn::em::ISVTrainer());
-      return 0;
-    }
-    case 1:{
-      //Reading the input argument
-      PyObject* arg = 0;
-      if (PyTuple_Size(args))
-        arg = PyTuple_GET_ITEM(args, 0);
-      else {
-        PyObject* tmp = PyDict_Values(kwargs);
-        auto tmp_ = make_safe(tmp);
-        arg = PyList_GET_ITEM(tmp, 0);
-      }
-
-      if(PyBobLearnEMISVTrainer_Check(arg))
-        // If the constructor input is ISVTrainer object
-        return PyBobLearnEMISVTrainer_init_copy(self, args, kwargs);
-      else
-        return PyBobLearnEMISVTrainer_init_number(self, args, kwargs);
-
-    }
-    default:{
-      PyErr_Format(PyExc_RuntimeError, "number of arguments mismatch - %s requires only 0 or 1 arguments, but you provided %d (see help)", Py_TYPE(self)->tp_name, nargs);
-      ISVTrainer_doc.print_usage();
-      return -1;
-    }
-  }
-  BOB_CATCH_MEMBER("cannot create ISVTrainer", -1)
-  return 0;
-}
-
-
-static void PyBobLearnEMISVTrainer_delete(PyBobLearnEMISVTrainerObject* self) {
-  self->cxx.reset();
-  Py_TYPE(self)->tp_free((PyObject*)self);
-}
-
-
-int PyBobLearnEMISVTrainer_Check(PyObject* o) {
-  return PyObject_IsInstance(o, reinterpret_cast<PyObject*>(&PyBobLearnEMISVTrainer_Type));
-}
-
-
-static PyObject* PyBobLearnEMISVTrainer_RichCompare(PyBobLearnEMISVTrainerObject* self, PyObject* other, int op) {
-  BOB_TRY
-
-  if (!PyBobLearnEMISVTrainer_Check(other)) {
-    PyErr_Format(PyExc_TypeError, "cannot compare `%s' with `%s'", Py_TYPE(self)->tp_name, Py_TYPE(other)->tp_name);
-    return 0;
-  }
-  auto other_ = reinterpret_cast<PyBobLearnEMISVTrainerObject*>(other);
-  switch (op) {
-    case Py_EQ:
-      if (*self->cxx==*other_->cxx) Py_RETURN_TRUE; else Py_RETURN_FALSE;
-    case Py_NE:
-      if (*self->cxx==*other_->cxx) Py_RETURN_FALSE; else Py_RETURN_TRUE;
-    default:
-      Py_INCREF(Py_NotImplemented);
-      return Py_NotImplemented;
-  }
-  BOB_CATCH_MEMBER("cannot compare ISVTrainer objects", 0)
-}
-
-
-/******************************************************************/
-/************ Variables Section ***********************************/
-/******************************************************************/
-
-static auto acc_u_a1 = bob::extension::VariableDoc(
-  "acc_u_a1",
-  "array_like <float, 3D>",
-  "Accumulator updated during the E-step",
-  ""
-);
-PyObject* PyBobLearnEMISVTrainer_get_acc_u_a1(PyBobLearnEMISVTrainerObject* self, void*){
-  BOB_TRY
-  return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getAccUA1());
-  BOB_CATCH_MEMBER("acc_u_a1 could not be read", 0)
-}
-int PyBobLearnEMISVTrainer_set_acc_u_a1(PyBobLearnEMISVTrainerObject* self, PyObject* value, void*){
-  BOB_TRY
-  PyBlitzArrayObject* input;
-  if (!PyBlitzArray_Converter(value, &input)){
-    PyErr_Format(PyExc_RuntimeError, "%s %s expects a 3D array of floats", Py_TYPE(self)->tp_name, acc_u_a1.name());
-    return -1;
-  }
-  auto o_ = make_safe(input);
-
-  // perform check on the input
-  if (input->type_num != NPY_FLOAT64){
-    PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, acc_u_a1.name());
-    return -1;
-  }
-
-  if (input->ndim != 3){
-    PyErr_Format(PyExc_TypeError, "`%s' only processes 3D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, acc_u_a1.name());
-    return -1;
-  }
-
-  if (input->shape[0] != (Py_ssize_t)self->cxx->getAccUA1().extent(0) && input->shape[1] != (Py_ssize_t)self->cxx->getAccUA1().extent(1) && input->shape[2] != (Py_ssize_t)self->cxx->getAccUA1().extent(2)) {
-    PyErr_Format(PyExc_TypeError, "`%s' 3D `input` array should have the shape [%" PY_FORMAT_SIZE_T "d, %" PY_FORMAT_SIZE_T "d, %" PY_FORMAT_SIZE_T "d] not [%" PY_FORMAT_SIZE_T "d, %" PY_FORMAT_SIZE_T "d, %" PY_FORMAT_SIZE_T "d] for `%s`", Py_TYPE(self)->tp_name, (Py_ssize_t)self->cxx->getAccUA1().extent(0), (Py_ssize_t)self->cxx->getAccUA1().extent(1), (Py_ssize_t)self->cxx->getAccUA1().extent(2), (Py_ssize_t)input->shape[0], (Py_ssize_t)input->shape[1], (Py_ssize_t)input->shape[2], acc_u_a1.name());
-    return -1;
-  }
-
-
-  auto b = PyBlitzArrayCxx_AsBlitz<double,3>(input, "acc_u_a1");
-  if (!b) return -1;
-  self->cxx->setAccUA1(*b);
-  return 0;
-  BOB_CATCH_MEMBER("acc_u_a1 could not be set", -1)
-}
-
-
-static auto acc_u_a2 = bob::extension::VariableDoc(
-  "acc_u_a2",
-  "array_like <float, 2D>",
-  "Accumulator updated during the E-step",
-  ""
-);
-PyObject* PyBobLearnEMISVTrainer_get_acc_u_a2(PyBobLearnEMISVTrainerObject* self, void*){
-  BOB_TRY
-  return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getAccUA2());
-  BOB_CATCH_MEMBER("acc_u_a2 could not be read", 0)
-}
-int PyBobLearnEMISVTrainer_set_acc_u_a2(PyBobLearnEMISVTrainerObject* self, PyObject* value, void*){
-  BOB_TRY
-  PyBlitzArrayObject* input;
-  if (!PyBlitzArray_Converter(value, &input)){
-    PyErr_Format(PyExc_RuntimeError, "%s %s expects a 2D array of floats", Py_TYPE(self)->tp_name, acc_u_a2.name());
-    return -1;
-  }
-  auto o_ = make_safe(input);
-
-  // perform check on the input
-  if (input->type_num != NPY_FLOAT64){
-    PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, acc_u_a2.name());
-    return -1;
-  }
-
-  if (input->ndim != 2){
-    PyErr_Format(PyExc_TypeError, "`%s' only processes 2D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, acc_u_a2.name());
-    return -1;
-  }
-
-  if (input->shape[0] != (Py_ssize_t)self->cxx->getAccUA2().extent(0) && input->shape[1] != (Py_ssize_t)self->cxx->getAccUA2().extent(1)) {
-    PyErr_Format(PyExc_TypeError, "`%s' 3D `input` array should have the shape [%" PY_FORMAT_SIZE_T "d, %" PY_FORMAT_SIZE_T "d] not [%" PY_FORMAT_SIZE_T "d, %" PY_FORMAT_SIZE_T "d] for `%s`", Py_TYPE(self)->tp_name, (Py_ssize_t)self->cxx->getAccUA2().extent(0), (Py_ssize_t)self->cxx->getAccUA2().extent(1), input->shape[0], input->shape[1], acc_u_a2.name());
-    return -1;
-  }
-
-  auto b = PyBlitzArrayCxx_AsBlitz<double,2>(input, "acc_u_a2");
-  if (!b) return -1;
-  self->cxx->setAccUA2(*b);
-  return 0;
-  BOB_CATCH_MEMBER("acc_u_a2 could not be set", -1)
-}
-
-
-static auto __X__ = bob::extension::VariableDoc(
-  "__X__",
-  "list",
-  "",
-  ""
-);
-PyObject* PyBobLearnEMISVTrainer_get_X(PyBobLearnEMISVTrainerObject* self, void*){
-  BOB_TRY
-  return vector_as_list(self->cxx->getX());
-  BOB_CATCH_MEMBER("__X__ could not be read", 0)
-}
-int PyBobLearnEMISVTrainer_set_X(PyBobLearnEMISVTrainerObject* self, PyObject* value, void*){
-  BOB_TRY
-
-  // Parses input arguments in a single shot
-  if (!PyList_Check(value)){
-    PyErr_Format(PyExc_TypeError, "Expected a list in `%s'", __X__.name());
-    return -1;
-  }
-
-  std::vector<blitz::Array<double,2> > data;
-  if(list_as_vector(value ,data)==0){
-    self->cxx->setX(data);
-  }
-
-  return 0;
-  BOB_CATCH_MEMBER("__X__ could not be written", -1)
-}
-
-
-static auto __Z__ = bob::extension::VariableDoc(
-  "__Z__",
-  "list",
-  "",
-  ""
-);
-PyObject* PyBobLearnEMISVTrainer_get_Z(PyBobLearnEMISVTrainerObject* self, void*){
-  BOB_TRY
-  return vector_as_list(self->cxx->getZ());
-  BOB_CATCH_MEMBER("__Z__ could not be read", 0)
-}
-int PyBobLearnEMISVTrainer_set_Z(PyBobLearnEMISVTrainerObject* self, PyObject* value, void*){
-  BOB_TRY
-
-  // Parses input arguments in a single shot
-  if (!PyList_Check(value)){
-    PyErr_Format(PyExc_TypeError, "Expected a list in `%s'", __Z__.name());
-    return -1;
-  }
-
-  std::vector<blitz::Array<double,1> > data;
-  if(list_as_vector(value ,data)==0){
-    self->cxx->setZ(data);
-  }
-
-  return 0;
-  BOB_CATCH_MEMBER("__Z__ could not be written", -1)
-}
-
-
-static PyGetSetDef PyBobLearnEMISVTrainer_getseters[] = {
-  {
-   acc_u_a1.name(),
-   (getter)PyBobLearnEMISVTrainer_get_acc_u_a1,
-   (setter)PyBobLearnEMISVTrainer_set_acc_u_a1,
-   acc_u_a1.doc(),
-   0
-  },
-  {
-   acc_u_a2.name(),
-   (getter)PyBobLearnEMISVTrainer_get_acc_u_a2,
-   (setter)PyBobLearnEMISVTrainer_set_acc_u_a2,
-   acc_u_a2.doc(),
-   0
-  },
-  {
-   __X__.name(),
-   (getter)PyBobLearnEMISVTrainer_get_X,
-   (setter)PyBobLearnEMISVTrainer_set_X,
-   __X__.doc(),
-   0
-  },
-  {
-   __Z__.name(),
-   (getter)PyBobLearnEMISVTrainer_get_Z,
-   (setter)PyBobLearnEMISVTrainer_set_Z,
-   __Z__.doc(),
-   0
-  },
-
-
-  {0}  // Sentinel
-};
-
-
-/******************************************************************/
-/************ Functions Section ***********************************/
-/******************************************************************/
-
-/*** initialize ***/
-static auto initialize = bob::extension::FunctionDoc(
-  "initialize",
-  "Initialization before the EM steps",
-  "",
-  true
-)
-.add_prototype("isv_base, stats, [rng]")
-.add_parameter("isv_base", ":py:class:`bob.learn.em.ISVBase`", "ISVBase Object")
-.add_parameter("stats", ":py:class:`bob.learn.em.GMMStats`", "GMMStats Object")
-.add_parameter("rng", ":py:class:`bob.core.random.mt19937`", "The Mersenne Twister mt19937 random generator used for the initialization of subspaces/arrays before the EM loop.");
-static PyObject* PyBobLearnEMISVTrainer_initialize(PyBobLearnEMISVTrainerObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  /* Parses input arguments in a single shot */
-  char** kwlist = initialize.kwlist(0);
-
-  PyBobLearnEMISVBaseObject* isv_base = 0;
-  PyObject* stats = 0;
-  PyBoostMt19937Object* rng = 0;
-
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O!|O!", kwlist, &PyBobLearnEMISVBase_Type, &isv_base,
-                                                                 &PyList_Type, &stats,
-                                                                 &PyBoostMt19937_Type, &rng)) return 0;
-
-  if(rng){
-    self->cxx->setRng(rng->rng);
-  }
-
-  std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > > training_data;
-  if(extract_GMMStats_2d(stats ,training_data)==0)
-    self->cxx->initialize(*isv_base->cxx, training_data);
-  else
-    return 0;
-
-  BOB_CATCH_MEMBER("cannot perform the initialize method", 0)
-
-  Py_RETURN_NONE;
-}
-
-
-/*** e_step ***/
-static auto e_step = bob::extension::FunctionDoc(
-  "e_step",
-  "Call the e-step procedure (for the U subspace).",
-  "",
-  true
-)
-.add_prototype("isv_base, stats")
-.add_parameter("isv_base", ":py:class:`bob.learn.em.ISVBase`", "ISVBase Object")
-.add_parameter("stats", ":py:class:`bob.learn.em.GMMStats`", "GMMStats Object");
-static PyObject* PyBobLearnEMISVTrainer_e_step(PyBobLearnEMISVTrainerObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  // Parses input arguments in a single shot
-  char** kwlist = e_step.kwlist(0);
-
-  PyBobLearnEMISVBaseObject* isv_base = 0;
-  PyObject* stats = 0;
-
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O!", kwlist, &PyBobLearnEMISVBase_Type, &isv_base,
-                                                                 &PyList_Type, &stats)) return 0;
-
-  std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > > training_data;
-  if(extract_GMMStats_2d(stats ,training_data)==0)
-    self->cxx->eStep(*isv_base->cxx, training_data);
-  else
-    return 0;
-
-  BOB_CATCH_MEMBER("cannot perform the e_step method", 0)
-
-  Py_RETURN_NONE;
-}
-
-
-/*** m_step ***/
-static auto m_step = bob::extension::FunctionDoc(
-  "m_step",
-  "Call the m-step procedure (for the U subspace).",
-  "",
-  true
-)
-.add_prototype("isv_base, [stats]")
-.add_parameter("isv_base", ":py:class:`bob.learn.em.ISVBase`", "ISVBase Object")
-.add_parameter("stats", "object", "Ignored.");
-static PyObject* PyBobLearnEMISVTrainer_m_step(PyBobLearnEMISVTrainerObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  // Parses input arguments in a single shot
-  char** kwlist = m_step.kwlist(0);
-
-  PyBobLearnEMISVBaseObject* isv_base = 0;
-  PyObject* stats;
-
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!|O", kwlist, &PyBobLearnEMISVBase_Type, &isv_base,
-                                                                 &stats)) return 0;
-
-  self->cxx->mStep(*isv_base->cxx);
-
-  BOB_CATCH_MEMBER("cannot perform the m_step method", 0)
-
-  Py_RETURN_NONE;
-}
-
-
-
-/*** enroll ***/
-static auto enroll = bob::extension::FunctionDoc(
-  "enroll",
-  "",
-  "",
-  true
-)
-.add_prototype("isv_machine, features, n_iter")
-.add_parameter("isv_machine", ":py:class:`bob.learn.em.ISVMachine`", "ISVMachine Object")
-.add_parameter("features", "list(:py:class:`bob.learn.em.GMMStats`)`", "")
-.add_parameter("n_iter", "int", "Number of iterations");
-static PyObject* PyBobLearnEMISVTrainer_enroll(PyBobLearnEMISVTrainerObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  // Parses input arguments in a single shot
-  char** kwlist = enroll.kwlist(0);
-
-  PyBobLearnEMISVMachineObject* isv_machine = 0;
-  PyObject* stats = 0;
-  int n_iter = 1;
-
-
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O!i", kwlist, &PyBobLearnEMISVMachine_Type, &isv_machine,
-                                                                  &PyList_Type, &stats, &n_iter)) return 0;
-
-  std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > training_data;
-  if(extract_GMMStats_1d(stats ,training_data)==0)
-    self->cxx->enroll(*isv_machine->cxx, training_data, n_iter);
-  else
-    return 0;
-
-  BOB_CATCH_MEMBER("cannot perform the enroll method", 0)
-
-  Py_RETURN_NONE;
-}
-
-
-
-static PyMethodDef PyBobLearnEMISVTrainer_methods[] = {
-  {
-    initialize.name(),
-    (PyCFunction)PyBobLearnEMISVTrainer_initialize,
-    METH_VARARGS|METH_KEYWORDS,
-    initialize.doc()
-  },
-  {
-    e_step.name(),
-    (PyCFunction)PyBobLearnEMISVTrainer_e_step,
-    METH_VARARGS|METH_KEYWORDS,
-    e_step.doc()
-  },
-  {
-    m_step.name(),
-    (PyCFunction)PyBobLearnEMISVTrainer_m_step,
-    METH_VARARGS|METH_KEYWORDS,
-    m_step.doc()
-  },
-  {
-    enroll.name(),
-    (PyCFunction)PyBobLearnEMISVTrainer_enroll,
-    METH_VARARGS|METH_KEYWORDS,
-    enroll.doc()
-  },
-  {0} /* Sentinel */
-};
-
-
-/******************************************************************/
-/************ Module Section **************************************/
-/******************************************************************/
-
-// Define the Gaussian type struct; will be initialized later
-PyTypeObject PyBobLearnEMISVTrainer_Type = {
-  PyVarObject_HEAD_INIT(0,0)
-  0
-};
-
-bool init_BobLearnEMISVTrainer(PyObject* module)
-{
-  // initialize the type struct
-  PyBobLearnEMISVTrainer_Type.tp_name      = ISVTrainer_doc.name();
-  PyBobLearnEMISVTrainer_Type.tp_basicsize = sizeof(PyBobLearnEMISVTrainerObject);
-  PyBobLearnEMISVTrainer_Type.tp_flags     = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE;//Enable the class inheritance;
-  PyBobLearnEMISVTrainer_Type.tp_doc       = ISVTrainer_doc.doc();
-
-  // set the functions
-  PyBobLearnEMISVTrainer_Type.tp_new          = PyType_GenericNew;
-  PyBobLearnEMISVTrainer_Type.tp_init         = reinterpret_cast<initproc>(PyBobLearnEMISVTrainer_init);
-  PyBobLearnEMISVTrainer_Type.tp_dealloc      = reinterpret_cast<destructor>(PyBobLearnEMISVTrainer_delete);
-  PyBobLearnEMISVTrainer_Type.tp_richcompare = reinterpret_cast<richcmpfunc>(PyBobLearnEMISVTrainer_RichCompare);
-  PyBobLearnEMISVTrainer_Type.tp_methods      = PyBobLearnEMISVTrainer_methods;
-  PyBobLearnEMISVTrainer_Type.tp_getset       = PyBobLearnEMISVTrainer_getseters;
-  //PyBobLearnEMISVTrainer_Type.tp_call         = reinterpret_cast<ternaryfunc>(PyBobLearnEMISVTrainer_compute_likelihood);
-
-
-  // check that everything is fine
-  if (PyType_Ready(&PyBobLearnEMISVTrainer_Type) < 0) return false;
-
-  // add the type to the module
-  Py_INCREF(&PyBobLearnEMISVTrainer_Type);
-  return PyModule_AddObject(module, "ISVTrainer", (PyObject*)&PyBobLearnEMISVTrainer_Type) >= 0;
-}
diff --git a/bob/learn/em/ivector_machine.cpp b/bob/learn/em/ivector_machine.cpp
deleted file mode 100644
index 159bec7415c9e44abe24675a75c8dc7bf28ed192..0000000000000000000000000000000000000000
--- a/bob/learn/em/ivector_machine.cpp
+++ /dev/null
@@ -1,673 +0,0 @@
-/**
- * @date Wed Jan 28 17:46:15 2015 +0200
- * @author Tiago de Freitas Pereira <tiago.pereira@idiap.ch>
- *
- * @brief Python API for bob::learn::em
- *
- * Copyright (C) 2011-2014 Idiap Research Institute, Martigny, Switzerland
- */
-
-#include "main.h"
-
-/******************************************************************/
-/************ Constructor Section *********************************/
-/******************************************************************/
-
-static auto IVectorMachine_doc = bob::extension::ClassDoc(
-  BOB_EXT_MODULE_PREFIX ".IVectorMachine",
-  "Statistical model for the Total Variability training for more information and explanation see the user guide in documentation (:ref:`iVectors <ivector>`)" // this documentation text is intentionally written to be long!
-  "",
-  ""
-).add_constructor(
-  bob::extension::FunctionDoc(
-    "__init__",
-    "Constructor. Builds a new IVectorMachine",
-    "",
-    true
-  )
-  .add_prototype("ubm,rt,variance_threshold","")
-  .add_prototype("other","")
-  .add_prototype("hdf5","")
-
-  .add_parameter("ubm", ":py:class:`bob.learn.em.GMMMachine`", "The Universal Background Model.")
-  .add_parameter("rt", "int", "Size of the Total Variability matrix (CD x rt).")
-  .add_parameter("variance_threshold", "float", "Variance flooring threshold for the :math:`\\Sigma` (diagonal) matrix")
-
-  .add_parameter("other", ":py:class:`bob.learn.em.IVectorMachine`", "A IVectorMachine object to be copied.")
-  .add_parameter("hdf5", ":py:class:`bob.io.base.HDF5File`", "An HDF5 file open for reading")
-
-);
-
-
-static int PyBobLearnEMIVectorMachine_init_copy(PyBobLearnEMIVectorMachineObject* self, PyObject* args, PyObject* kwargs) {
-
-  char** kwlist = IVectorMachine_doc.kwlist(1);
-  PyBobLearnEMIVectorMachineObject* o;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", kwlist, &PyBobLearnEMIVectorMachine_Type, &o)){
-    IVectorMachine_doc.print_usage();
-    return -1;
-  }
-
-  self->cxx.reset(new bob::learn::em::IVectorMachine(*o->cxx));
-  return 0;
-}
-
-
-static int PyBobLearnEMIVectorMachine_init_hdf5(PyBobLearnEMIVectorMachineObject* self, PyObject* args, PyObject* kwargs) {
-
-  char** kwlist = IVectorMachine_doc.kwlist(2);
-
-  PyBobIoHDF5FileObject* config = 0;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, &PyBobIoHDF5File_Converter, &config)){
-    IVectorMachine_doc.print_usage();
-    return -1;
-  }
-  auto config_ = make_safe(config);
-  self->cxx.reset(new bob::learn::em::IVectorMachine(*(config->f)));
-
-  return 0;
-}
-
-
-static int PyBobLearnEMIVectorMachine_init_ubm(PyBobLearnEMIVectorMachineObject* self, PyObject* args, PyObject* kwargs) {
-
-  char** kwlist = IVectorMachine_doc.kwlist(0);
-
-  PyBobLearnEMGMMMachineObject* gmm_machine;
-  int rt = 1;
-  double variance_threshold = 1e-10;
-
-  //Here we have to select which keyword argument to read
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!i|d", kwlist, &PyBobLearnEMGMMMachine_Type, &gmm_machine,
-                                                                  &rt, &variance_threshold)){
-    IVectorMachine_doc.print_usage();
-    return -1;
-  }
-
-  if(rt < 1){
-    PyErr_Format(PyExc_TypeError, "rt argument must be greater than or equal to one");
-    return -1;
-  }
-
-  if(variance_threshold <= 0){
-    PyErr_Format(PyExc_TypeError, "variance_threshold argument must be greater than zero");
-    return -1;
-  }
-
-  self->cxx.reset(new bob::learn::em::IVectorMachine(gmm_machine->cxx, rt, variance_threshold));
-  return 0;
-}
-
-
-static int PyBobLearnEMIVectorMachine_init(PyBobLearnEMIVectorMachineObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  // get the number of command line arguments
-  int nargs = (args?PyTuple_Size(args):0) + (kwargs?PyDict_Size(kwargs):0);
-
-  if(nargs == 1){
-    //Reading the input argument
-    PyObject* arg = 0;
-    if (PyTuple_Size(args))
-      arg = PyTuple_GET_ITEM(args, 0);
-    else {
-      PyObject* tmp = PyDict_Values(kwargs);
-      auto tmp_ = make_safe(tmp);
-      arg = PyList_GET_ITEM(tmp, 0);
-    }
-
-    // If the constructor input is Gaussian object
-    if (PyBobLearnEMIVectorMachine_Check(arg))
-      return PyBobLearnEMIVectorMachine_init_copy(self, args, kwargs);
-    // If the constructor input is a HDF5
-    else
-      return PyBobLearnEMIVectorMachine_init_hdf5(self, args, kwargs);
-  }
-  else if ((nargs == 2) || (nargs == 3))
-    PyBobLearnEMIVectorMachine_init_ubm(self, args, kwargs);
-  else{
-    PyErr_Format(PyExc_RuntimeError, "number of arguments mismatch - %s requires 1,2 or 3 argument, but you provided %d (see help)", Py_TYPE(self)->tp_name, nargs);
-    IVectorMachine_doc.print_usage();
-    return -1;
-  }
-
-  BOB_CATCH_MEMBER("cannot create IVectorMachine", -1)
-  return 0;
-}
-
-static void PyBobLearnEMIVectorMachine_delete(PyBobLearnEMIVectorMachineObject* self) {
-  self->cxx.reset();
-  Py_TYPE(self)->tp_free((PyObject*)self);
-}
-
-static PyObject* PyBobLearnEMIVectorMachine_RichCompare(PyBobLearnEMIVectorMachineObject* self, PyObject* other, int op) {
-  BOB_TRY
-
-  if (!PyBobLearnEMIVectorMachine_Check(other)) {
-    PyErr_Format(PyExc_TypeError, "cannot compare `%s' with `%s'", Py_TYPE(self)->tp_name, Py_TYPE(other)->tp_name);
-    return 0;
-  }
-  auto other_ = reinterpret_cast<PyBobLearnEMIVectorMachineObject*>(other);
-  switch (op) {
-    case Py_EQ:
-      if (*self->cxx==*other_->cxx) Py_RETURN_TRUE; else Py_RETURN_FALSE;
-    case Py_NE:
-      if (*self->cxx==*other_->cxx) Py_RETURN_FALSE; else Py_RETURN_TRUE;
-    default:
-      Py_INCREF(Py_NotImplemented);
-      return Py_NotImplemented;
-  }
-  BOB_CATCH_MEMBER("cannot compare IVectorMachine objects", 0)
-}
-
-int PyBobLearnEMIVectorMachine_Check(PyObject* o) {
-  return PyObject_IsInstance(o, reinterpret_cast<PyObject*>(&PyBobLearnEMIVectorMachine_Type));
-}
-
-
-/******************************************************************/
-/************ Variables Section ***********************************/
-/******************************************************************/
-
-/***** shape *****/
-static auto shape = bob::extension::VariableDoc(
-  "shape",
-  "(int,int, int)",
-  "A tuple that represents the number of gaussians, dimensionality of each Gaussian, dimensionality of the rT (total variability matrix) ``(#Gaussians, #Inputs, #rT)``.",
-  ""
-);
-PyObject* PyBobLearnEMIVectorMachine_getShape(PyBobLearnEMIVectorMachineObject* self, void*) {
-  BOB_TRY
-  return Py_BuildValue("(i,i,i)", self->cxx->getNGaussians(), self->cxx->getNInputs(), self->cxx->getDimRt());
-  BOB_CATCH_MEMBER("shape could not be read", 0)
-}
-
-/***** supervector_length *****/
-static auto supervector_length = bob::extension::VariableDoc(
-  "supervector_length",
-  "int",
-
-  "Returns the supervector length.",
-  "NGaussians x NInputs: Number of Gaussian components by the feature dimensionality"
-  "An exception is thrown if no Universal Background Model has been set yet."
-  ""
-);
-PyObject* PyBobLearnEMIVectorMachine_getSupervectorLength(PyBobLearnEMIVectorMachineObject* self, void*) {
-  BOB_TRY
-  return Py_BuildValue("i", self->cxx->getSupervectorLength());
-  BOB_CATCH_MEMBER("supervector_length could not be read", 0)
-}
-
-
-/***** T *****/
-static auto T = bob::extension::VariableDoc(
-  "t",
-  "array_like <float, 2D>",
-  "Returns the Total Variability matrix, :math:`T`",
-  ""
-);
-PyObject* PyBobLearnEMIVectorMachine_getT(PyBobLearnEMIVectorMachineObject* self, void*){
-  BOB_TRY
-  return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getT());
-  BOB_CATCH_MEMBER("`t` could not be read", 0)
-}
-int PyBobLearnEMIVectorMachine_setT(PyBobLearnEMIVectorMachineObject* self, PyObject* value, void*){
-  BOB_TRY
-  PyBlitzArrayObject* o;
-  if (!PyBlitzArray_Converter(value, &o)){
-    PyErr_Format(PyExc_RuntimeError, "%s %s expects a 2D array of floats", Py_TYPE(self)->tp_name, T.name());
-    return -1;
-  }
-  auto o_ = make_safe(o);
-  auto b = PyBlitzArrayCxx_AsBlitz<double,2>(o, "t");
-  if (!b) return -1;
-  self->cxx->setT(*b);
-  return 0;
-  BOB_CATCH_MEMBER("`t` vector could not be set", -1)
-}
-
-
-/***** sigma *****/
-static auto sigma = bob::extension::VariableDoc(
-  "sigma",
-  "array_like <float, 1D>",
-  "The residual matrix of the model sigma",
-  ""
-);
-PyObject* PyBobLearnEMIVectorMachine_getSigma(PyBobLearnEMIVectorMachineObject* self, void*){
-  BOB_TRY
-  return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getSigma());
-  BOB_CATCH_MEMBER("`sigma` could not be read", 0)
-}
-int PyBobLearnEMIVectorMachine_setSigma(PyBobLearnEMIVectorMachineObject* self, PyObject* value, void*){
-  BOB_TRY
-  PyBlitzArrayObject* o;
-  if (!PyBlitzArray_Converter(value, &o)){
-    PyErr_Format(PyExc_RuntimeError, "%s %s expects a 1D array of floats", Py_TYPE(self)->tp_name, sigma.name());
-    return -1;
-  }
-  auto o_ = make_safe(o);
-  auto b = PyBlitzArrayCxx_AsBlitz<double,1>(o, "sigma");
-  if (!b) return -1;
-  self->cxx->setSigma(*b);
-  return 0;
-  BOB_CATCH_MEMBER("`sigma` vector could not be set", -1)
-}
-
-
-/***** variance_threshold *****/
-static auto variance_threshold = bob::extension::VariableDoc(
-  "variance_threshold",
-  "float",
-  "Threshold for the variance contained in sigma",
-  ""
-);
-PyObject* PyBobLearnEMIVectorMachine_getVarianceThreshold(PyBobLearnEMIVectorMachineObject* self, void*) {
-  BOB_TRY
-  return Py_BuildValue("d", self->cxx->getVarianceThreshold());
-  BOB_CATCH_MEMBER("variance_threshold could not be read", 0)
-}
-int PyBobLearnEMIVectorMachine_setVarianceThreshold(PyBobLearnEMIVectorMachineObject* self, PyObject* value, void*){
-  BOB_TRY
-
-  if (!PyBob_NumberCheck(value)){
-    PyErr_Format(PyExc_RuntimeError, "%s %s expects a float", Py_TYPE(self)->tp_name, variance_threshold.name());
-    return -1;
-  }
-
-  if (PyFloat_AS_DOUBLE(value) < 0){
-    PyErr_Format(PyExc_TypeError, "variance_threshold must be greater than or equal to zero");
-    return -1;
-  }
-
-  self->cxx->setVarianceThreshold(PyFloat_AS_DOUBLE(value));
-  BOB_CATCH_MEMBER("variance_threshold could not be set", -1)
-  return 0;
-}
-
-
-/***** ubm *****/
-static auto ubm = bob::extension::VariableDoc(
-  "ubm",
-  ":py:class:`bob.learn.em.GMMMachine`",
-  "Returns the UBM (Universal Background Model)",
-  ""
-);
-PyObject* PyBobLearnEMIVectorMachine_getUBM(PyBobLearnEMIVectorMachineObject* self, void*){
-  BOB_TRY
-
-  boost::shared_ptr<bob::learn::em::GMMMachine> ubm_gmmMachine = self->cxx->getUbm();
-
-  //Allocating the correspondent python object
-  PyBobLearnEMGMMMachineObject* retval =
-    (PyBobLearnEMGMMMachineObject*)PyBobLearnEMGMMMachine_Type.tp_alloc(&PyBobLearnEMGMMMachine_Type, 0);
-  retval->cxx = ubm_gmmMachine;
-
-  return Py_BuildValue("N",retval);
-  BOB_CATCH_MEMBER("ubm could not be read", 0)
-}
-int PyBobLearnEMIVectorMachine_setUBM(PyBobLearnEMIVectorMachineObject* self, PyObject* value, void*){
-  BOB_TRY
-
-  if (!PyBobLearnEMGMMMachine_Check(value)){
-    PyErr_Format(PyExc_RuntimeError, "%s %s expects a :py:class:`bob.learn.em.GMMMachine`", Py_TYPE(self)->tp_name, ubm.name());
-    return -1;
-  }
-
-  PyBobLearnEMGMMMachineObject* ubm_gmmMachine = 0;
-  PyArg_Parse(value, "O!", &PyBobLearnEMGMMMachine_Type,&ubm_gmmMachine);
-
-  self->cxx->setUbm(ubm_gmmMachine->cxx);
-
-  return 0;
-  BOB_CATCH_MEMBER("ubm could not be set", -1)
-}
-
-
-static PyGetSetDef PyBobLearnEMIVectorMachine_getseters[] = {
-  {
-   shape.name(),
-   (getter)PyBobLearnEMIVectorMachine_getShape,
-   0,
-   shape.doc(),
-   0
-  },
-
-  {
-   supervector_length.name(),
-   (getter)PyBobLearnEMIVectorMachine_getSupervectorLength,
-   0,
-   supervector_length.doc(),
-   0
-  },
-
-  {
-   T.name(),
-   (getter)PyBobLearnEMIVectorMachine_getT,
-   (setter)PyBobLearnEMIVectorMachine_setT,
-   T.doc(),
-   0
-  },
-
-  {
-   variance_threshold.name(),
-   (getter)PyBobLearnEMIVectorMachine_getVarianceThreshold,
-   (setter)PyBobLearnEMIVectorMachine_setVarianceThreshold,
-   variance_threshold.doc(),
-   0
-  },
-
-  {
-   sigma.name(),
-   (getter)PyBobLearnEMIVectorMachine_getSigma,
-   (setter)PyBobLearnEMIVectorMachine_setSigma,
-   sigma.doc(),
-   0
-  },
-
-  {
-   ubm.name(),
-   (getter)PyBobLearnEMIVectorMachine_getUBM,
-   (setter)PyBobLearnEMIVectorMachine_setUBM,
-   ubm.doc(),
-   0
-  },
-
-
-  {0}  // Sentinel
-};
-
-
-/******************************************************************/
-/************ Functions Section ***********************************/
-/******************************************************************/
-
-
-/*** save ***/
-static auto save = bob::extension::FunctionDoc(
-  "save",
-  "Save the configuration of the IVectorMachine to a given HDF5 file"
-)
-.add_prototype("hdf5")
-.add_parameter("hdf5", ":py:class:`bob.io.base.HDF5File`", "An HDF5 file open for writing");
-static PyObject* PyBobLearnEMIVectorMachine_Save(PyBobLearnEMIVectorMachineObject* self,  PyObject* args, PyObject* kwargs) {
-
-  BOB_TRY
-
-  // get list of arguments
-  char** kwlist = save.kwlist(0);
-  PyBobIoHDF5FileObject* hdf5;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, PyBobIoHDF5File_Converter, &hdf5)) return 0;
-
-  auto hdf5_ = make_safe(hdf5);
-  self->cxx->save(*hdf5->f);
-
-  BOB_CATCH_MEMBER("cannot save the data", 0)
-  Py_RETURN_NONE;
-}
-
-/*** load ***/
-static auto load = bob::extension::FunctionDoc(
-  "load",
-  "Load the configuration of the IVectorMachine to a given HDF5 file"
-)
-.add_prototype("hdf5")
-.add_parameter("hdf5", ":py:class:`bob.io.base.HDF5File`", "An HDF5 file open for reading");
-static PyObject* PyBobLearnEMIVectorMachine_Load(PyBobLearnEMIVectorMachineObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  char** kwlist = load.kwlist(0);
-  PyBobIoHDF5FileObject* hdf5;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, PyBobIoHDF5File_Converter, &hdf5)) return 0;
-
-  auto hdf5_ = make_safe(hdf5);
-  self->cxx->load(*hdf5->f);
-
-  BOB_CATCH_MEMBER("cannot load the data", 0)
-  Py_RETURN_NONE;
-}
-
-
-/*** is_similar_to ***/
-static auto is_similar_to = bob::extension::FunctionDoc(
-  "is_similar_to",
-
-  "Compares this IVectorMachine with the ``other`` one to be approximately the same.",
-  "The optional values ``r_epsilon`` and ``a_epsilon`` refer to the "
-  "relative and absolute precision for the ``weights``, ``biases`` "
-  "and any other values internal to this machine."
-)
-.add_prototype("other, [r_epsilon], [a_epsilon]","output")
-.add_parameter("other", ":py:class:`bob.learn.em.IVectorMachine`", "A IVectorMachine object to be compared.")
-.add_parameter("r_epsilon", "float", "Relative precision.")
-.add_parameter("a_epsilon", "float", "Absolute precision.")
-.add_return("output","bool","True if it is similar, otherwise false.");
-static PyObject* PyBobLearnEMIVectorMachine_IsSimilarTo(PyBobLearnEMIVectorMachineObject* self, PyObject* args, PyObject* kwds) {
-
-  /* Parses input arguments in a single shot */
-  char** kwlist = is_similar_to.kwlist(0);
-
-  //PyObject* other = 0;
-  PyBobLearnEMIVectorMachineObject* other = 0;
-  double r_epsilon = 1.e-5;
-  double a_epsilon = 1.e-8;
-
-  if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|dd", kwlist,
-        &PyBobLearnEMIVectorMachine_Type, &other,
-        &r_epsilon, &a_epsilon)){
-
-        is_similar_to.print_usage();
-        return 0;
-  }
-
-  if (self->cxx->is_similar_to(*other->cxx, r_epsilon, a_epsilon))
-    Py_RETURN_TRUE;
-  else
-    Py_RETURN_FALSE;
-}
-
-
-
-/*** project ***/
-static auto project = bob::extension::FunctionDoc(
-  "project",
-  "Projects the given GMM statistics into the i-vector subspace",
-  ".. note:: The ``__call__`` function is an alias for this function",
-  true
-)
-.add_prototype("stats")
-.add_parameter("stats", ":py:class:`bob.learn.em.GMMStats`", "Statistics as input");
-static PyObject* PyBobLearnEMIVectorMachine_project(PyBobLearnEMIVectorMachineObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  char** kwlist = project.kwlist(0);
-
-  PyBobLearnEMGMMStatsObject* stats = 0;
-
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", kwlist, &PyBobLearnEMGMMStats_Type, &stats))
-    return 0;
-
-   blitz::Array<double,1> ivector(self->cxx->getDimRt());
-   self->cxx->forward(*stats->cxx, ivector);
-
-  return PyBlitzArrayCxx_AsConstNumpy(ivector);
-
-  BOB_CATCH_MEMBER("cannot project", 0)
-
-}
-
-/*** resize ***/
-static auto resize = bob::extension::FunctionDoc(
-  "resize",
-  "Resets the dimensionality of the subspace T. ",
-  0,
-  true
-)
-.add_prototype("rT")
-.add_parameter("rT", "int", "Size of T (Total variability matrix)");
-static PyObject* PyBobLearnEMIVectorMachine_resize(PyBobLearnEMIVectorMachineObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  /* Parses input arguments in a single shot */
-  char** kwlist = resize.kwlist(0);
-
-  int rT = 0;
-
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, &rT)) return 0;
-
-  if (rT < 1){
-    PyErr_Format(PyExc_TypeError, "rU must be greater than one");
-    resize.print_usage();
-    return 0;
-  }
-
-  self->cxx->resize(rT);
-
-  BOB_CATCH_MEMBER("cannot perform the resize method", 0)
-
-  Py_RETURN_NONE;
-}
-
-
-/*** __compute_Id_TtSigmaInvT__ ***/
-static auto __compute_Id_TtSigmaInvT__ = bob::extension::FunctionDoc(
-  "__compute_Id_TtSigmaInvT__",
-  "",
-  "",
-  true
-)
-.add_prototype("stats")
-.add_parameter("stats", ":py:class:`bob.learn.em.GMMStats`", "Statistics as input");
-static PyObject* PyBobLearnEMIVectorMachine_compute_Id_TtSigmaInvT__(PyBobLearnEMIVectorMachineObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  char** kwlist = __compute_Id_TtSigmaInvT__.kwlist(0);
-
-  PyBobLearnEMGMMStatsObject* stats = 0;
-
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", kwlist, &PyBobLearnEMGMMStats_Type, &stats))
-    return 0;
-
-
-  blitz::Array<double,2> output(self->cxx->getDimRt(), self->cxx->getDimRt());
-  self->cxx->computeIdTtSigmaInvT(*stats->cxx, output);
-  return PyBlitzArrayCxx_AsConstNumpy(output);
-
-  BOB_CATCH_MEMBER("cannot __compute_Id_TtSigmaInvT__", 0)
-}
-
-
-
-/*** __compute_TtSigmaInvFnorm__ ***/
-static auto __compute_TtSigmaInvFnorm__ = bob::extension::FunctionDoc(
-  "__compute_TtSigmaInvFnorm__",
-  "",
-  "",
-  true
-)
-.add_prototype("stats")
-.add_parameter("stats", ":py:class:`bob.learn.em.GMMStats`", "Statistics as input");
-static PyObject* PyBobLearnEMIVectorMachine_compute_TtSigmaInvFnorm__(PyBobLearnEMIVectorMachineObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  char** kwlist = __compute_TtSigmaInvFnorm__.kwlist(0);
-
-  PyBobLearnEMGMMStatsObject* stats = 0;
-
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", kwlist, &PyBobLearnEMGMMStats_Type, &stats))
-    return 0;
-
-
-  blitz::Array<double,1> output(self->cxx->getDimRt());
-  self->cxx->computeTtSigmaInvFnorm(*stats->cxx, output);
-  return PyBlitzArrayCxx_AsConstNumpy(output);
-
-  BOB_CATCH_MEMBER("cannot __compute_TtSigmaInvFnorm__", 0)
-}
-
-
-
-
-static PyMethodDef PyBobLearnEMIVectorMachine_methods[] = {
-  {
-    save.name(),
-    (PyCFunction)PyBobLearnEMIVectorMachine_Save,
-    METH_VARARGS|METH_KEYWORDS,
-    save.doc()
-  },
-  {
-    load.name(),
-    (PyCFunction)PyBobLearnEMIVectorMachine_Load,
-    METH_VARARGS|METH_KEYWORDS,
-    load.doc()
-  },
-  {
-    is_similar_to.name(),
-    (PyCFunction)PyBobLearnEMIVectorMachine_IsSimilarTo,
-    METH_VARARGS|METH_KEYWORDS,
-    is_similar_to.doc()
-  },
-  {
-    resize.name(),
-    (PyCFunction)PyBobLearnEMIVectorMachine_resize,
-    METH_VARARGS|METH_KEYWORDS,
-    resize.doc()
-  },
-  {
-    project.name(),
-    (PyCFunction)PyBobLearnEMIVectorMachine_project,
-    METH_VARARGS|METH_KEYWORDS,
-    project.doc()
-  },
-  {
-    __compute_Id_TtSigmaInvT__.name(),
-    (PyCFunction)PyBobLearnEMIVectorMachine_compute_Id_TtSigmaInvT__,
-    METH_VARARGS|METH_KEYWORDS,
-    __compute_Id_TtSigmaInvT__.doc()
-  },
-  {
-    __compute_TtSigmaInvFnorm__.name(),
-    (PyCFunction)PyBobLearnEMIVectorMachine_compute_TtSigmaInvFnorm__,
-    METH_VARARGS|METH_KEYWORDS,
-    __compute_TtSigmaInvFnorm__.doc()
-  },
-
-  {0} /* Sentinel */
-};
-
-
-/******************************************************************/
-/************ Module Section **************************************/
-/******************************************************************/
-
-// Define the JFA type struct; will be initialized later
-PyTypeObject PyBobLearnEMIVectorMachine_Type = {
-  PyVarObject_HEAD_INIT(0,0)
-  0
-};
-
-bool init_BobLearnEMIVectorMachine(PyObject* module)
-{
-  // initialize the type struct
-  PyBobLearnEMIVectorMachine_Type.tp_name      = IVectorMachine_doc.name();
-  PyBobLearnEMIVectorMachine_Type.tp_basicsize = sizeof(PyBobLearnEMIVectorMachineObject);
-  PyBobLearnEMIVectorMachine_Type.tp_flags     = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE;
-  PyBobLearnEMIVectorMachine_Type.tp_doc       = IVectorMachine_doc.doc();
-
-  // set the functions
-  PyBobLearnEMIVectorMachine_Type.tp_new         = PyType_GenericNew;
-  PyBobLearnEMIVectorMachine_Type.tp_init        = reinterpret_cast<initproc>(PyBobLearnEMIVectorMachine_init);
-  PyBobLearnEMIVectorMachine_Type.tp_dealloc     = reinterpret_cast<destructor>(PyBobLearnEMIVectorMachine_delete);
-  PyBobLearnEMIVectorMachine_Type.tp_richcompare = reinterpret_cast<richcmpfunc>(PyBobLearnEMIVectorMachine_RichCompare);
-  PyBobLearnEMIVectorMachine_Type.tp_methods     = PyBobLearnEMIVectorMachine_methods;
-  PyBobLearnEMIVectorMachine_Type.tp_getset      = PyBobLearnEMIVectorMachine_getseters;
-  PyBobLearnEMIVectorMachine_Type.tp_call        = reinterpret_cast<ternaryfunc>(PyBobLearnEMIVectorMachine_project);
-
-
-  // check that everything is fine
-  if (PyType_Ready(&PyBobLearnEMIVectorMachine_Type) < 0) return false;
-
-  // add the type to the module
-  Py_INCREF(&PyBobLearnEMIVectorMachine_Type);
-  return PyModule_AddObject(module, "IVectorMachine", (PyObject*)&PyBobLearnEMIVectorMachine_Type) >= 0;
-}
diff --git a/bob/learn/em/ivector_trainer.cpp b/bob/learn/em/ivector_trainer.cpp
deleted file mode 100644
index 96f2c3af971959710c26fea302f9556321433e14..0000000000000000000000000000000000000000
--- a/bob/learn/em/ivector_trainer.cpp
+++ /dev/null
@@ -1,496 +0,0 @@
-/**
- * @author Tiago de Freitas Pereira <tiago.pereira@idiap.ch>
- * @date Tue 03 Fev 10:29:00 2015
- *
- * @brief Python API for bob::learn::em
- *
- * Copyright (C) 2011-2014 Idiap Research Institute, Martigny, Switzerland
- */
-
-#include "main.h"
-#include <boost/make_shared.hpp>
-
-/******************************************************************/
-/************ Constructor Section *********************************/
-/******************************************************************/
-
-static inline bool f(PyObject* o){return o != 0 && PyObject_IsTrue(o) > 0;}  /* converts PyObject to bool and returns false if object is NULL */
-
-static int extract_GMMStats_1d(PyObject *list,
-                             std::vector<bob::learn::em::GMMStats>& training_data)
-{
-  for (int i=0; i<PyList_GET_SIZE(list); i++){
-
-    PyBobLearnEMGMMStatsObject* stats;
-    if (!PyArg_Parse(PyList_GetItem(list, i), "O!", &PyBobLearnEMGMMStats_Type, &stats)){
-      PyErr_Format(PyExc_RuntimeError, "Expected GMMStats objects");
-      return -1;
-    }
-    training_data.push_back(*stats->cxx);
-
-  }
-  return 0;
-}
-
-
-static auto IVectorTrainer_doc = bob::extension::ClassDoc(
-  BOB_EXT_MODULE_PREFIX ".IVectorTrainer",
-  "IVectorTrainer"
-  "Trains the Total Variability subspace :math:`$T$` to generate :ref:`iVectors <ivector>`."
-  "",
-  ""
-).add_constructor(
-  bob::extension::FunctionDoc(
-    "__init__",
-    "Constructor. Builds a new IVectorTrainer",
-    "",
-    true
-  )
-  .add_prototype("update_sigma","")
-  .add_prototype("other","")
-  .add_prototype("","")
-  .add_parameter("other", ":py:class:`bob.learn.em.IVectorTrainer`", "A IVectorTrainer object to be copied.")
-  .add_parameter("update_sigma", "bool", "")
-);
-
-
-static int PyBobLearnEMIVectorTrainer_init_copy(PyBobLearnEMIVectorTrainerObject* self, PyObject* args, PyObject* kwargs) {
-
-  char** kwlist = IVectorTrainer_doc.kwlist(1);
-  PyBobLearnEMIVectorTrainerObject* o;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", kwlist, &PyBobLearnEMIVectorTrainer_Type, &o)){
-    IVectorTrainer_doc.print_usage();
-    return -1;
-  }
-
-  self->cxx.reset(new bob::learn::em::IVectorTrainer(*o->cxx));
-  return 0;
-}
-
-
-static int PyBobLearnEMIVectorTrainer_init_bool(PyBobLearnEMIVectorTrainerObject* self, PyObject* args, PyObject* kwargs) {
-
-  char** kwlist = IVectorTrainer_doc.kwlist(0);
-  PyObject* update_sigma   = 0;
-
-  //Parsing the input argments
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", kwlist, &PyBool_Type, &update_sigma))
-    return -1;
-
-  self->cxx.reset(new bob::learn::em::IVectorTrainer(f(update_sigma)));
-  return 0;
-}
-
-
-static int PyBobLearnEMIVectorTrainer_init(PyBobLearnEMIVectorTrainerObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  // get the number of command line arguments
-  int nargs = (args?PyTuple_Size(args):0) + (kwargs?PyDict_Size(kwargs):0);
-
-  switch(nargs){
-    case 0:{
-      self->cxx.reset(new bob::learn::em::IVectorTrainer());
-      return 0;
-    }
-    case 1:{
-      //Reading the input argument
-      PyObject* arg = 0;
-      if (PyTuple_Size(args))
-        arg = PyTuple_GET_ITEM(args, 0);
-      else {
-        PyObject* tmp = PyDict_Values(kwargs);
-        auto tmp_ = make_safe(tmp);
-        arg = PyList_GET_ITEM(tmp, 0);
-      }
-
-      // If the constructor input is IVectorTrainer object
-      if(PyBobLearnEMIVectorTrainer_Check(arg))
-        return  PyBobLearnEMIVectorTrainer_init_copy(self, args, kwargs);
-      else
-        return PyBobLearnEMIVectorTrainer_init_bool(self, args, kwargs);
-
-    }
-    default:{
-      PyErr_Format(PyExc_RuntimeError, "number of arguments mismatch - %s requires only 0 or 1 arguments, but you provided %d (see help)", Py_TYPE(self)->tp_name, nargs);
-      IVectorTrainer_doc.print_usage();
-      return -1;
-    }
-  }
-  BOB_CATCH_MEMBER("cannot create IVectorTrainer", -1)
-  return 0;
-}
-
-
-static void PyBobLearnEMIVectorTrainer_delete(PyBobLearnEMIVectorTrainerObject* self) {
-  self->cxx.reset();
-  Py_TYPE(self)->tp_free((PyObject*)self);
-}
-
-
-int PyBobLearnEMIVectorTrainer_Check(PyObject* o) {
-  return PyObject_IsInstance(o, reinterpret_cast<PyObject*>(&PyBobLearnEMIVectorTrainer_Type));
-}
-
-
-static PyObject* PyBobLearnEMIVectorTrainer_RichCompare(PyBobLearnEMIVectorTrainerObject* self, PyObject* other, int op) {
-  BOB_TRY
-
-  if (!PyBobLearnEMIVectorTrainer_Check(other)) {
-    PyErr_Format(PyExc_TypeError, "cannot compare `%s' with `%s'", Py_TYPE(self)->tp_name, Py_TYPE(other)->tp_name);
-    return 0;
-  }
-  auto other_ = reinterpret_cast<PyBobLearnEMIVectorTrainerObject*>(other);
-  switch (op) {
-    case Py_EQ:
-      if (*self->cxx==*other_->cxx) Py_RETURN_TRUE; else Py_RETURN_FALSE;
-    case Py_NE:
-      if (*self->cxx==*other_->cxx) Py_RETURN_FALSE; else Py_RETURN_TRUE;
-    default:
-      Py_INCREF(Py_NotImplemented);
-      return Py_NotImplemented;
-  }
-  BOB_CATCH_MEMBER("cannot compare IVectorTrainer objects", 0)
-}
-
-
-/******************************************************************/
-/************ Variables Section ***********************************/
-/******************************************************************/
-
-static auto acc_nij_wij2 = bob::extension::VariableDoc(
-  "acc_nij_wij2",
-  "array_like <float, 3D>",
-  "Accumulator updated during the E-step",
-  ""
-);
-PyObject* PyBobLearnEMIVectorTrainer_get_acc_nij_wij2(PyBobLearnEMIVectorTrainerObject* self, void*){
-  BOB_TRY
-  return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getAccNijWij2());
-  BOB_CATCH_MEMBER("acc_nij_wij2 could not be read", 0)
-}
-int PyBobLearnEMIVectorTrainer_set_acc_nij_wij2(PyBobLearnEMIVectorTrainerObject* self, PyObject* value, void*){
-  BOB_TRY
-  PyBlitzArrayObject* o;
-  if (!PyBlitzArray_Converter(value, &o)){
-    PyErr_Format(PyExc_RuntimeError, "%s %s expects a 3D array of floats", Py_TYPE(self)->tp_name, acc_nij_wij2.name());
-    return -1;
-  }
-  auto o_ = make_safe(o);
-  auto b = PyBlitzArrayCxx_AsBlitz<double,3>(o, "acc_nij_wij2");
-  if (!b) return -1;
-  self->cxx->setAccNijWij2(*b);
-  return 0;
-  BOB_CATCH_MEMBER("acc_nij_wij2 could not be set", -1)
-}
-
-
-static auto acc_fnormij_wij = bob::extension::VariableDoc(
-  "acc_fnormij_wij",
-  "array_like <float, 3D>",
-  "Accumulator updated during the E-step",
-  ""
-);
-PyObject* PyBobLearnEMIVectorTrainer_get_acc_fnormij_wij(PyBobLearnEMIVectorTrainerObject* self, void*){
-  BOB_TRY
-  return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getAccFnormijWij());
-  BOB_CATCH_MEMBER("acc_fnormij_wij could not be read", 0)
-}
-int PyBobLearnEMIVectorTrainer_set_acc_fnormij_wij(PyBobLearnEMIVectorTrainerObject* self, PyObject* value, void*){
-  BOB_TRY
-  PyBlitzArrayObject* o;
-  if (!PyBlitzArray_Converter(value, &o)){
-    PyErr_Format(PyExc_RuntimeError, "%s %s expects a 3D array of floats", Py_TYPE(self)->tp_name, acc_fnormij_wij.name());
-    return -1;
-  }
-  auto o_ = make_safe(o);
-  auto b = PyBlitzArrayCxx_AsBlitz<double,3>(o, "acc_fnormij_wij");
-  if (!b) return -1;
-  self->cxx->setAccFnormijWij(*b);
-  return 0;
-  BOB_CATCH_MEMBER("acc_fnormij_wij could not be set", -1)
-}
-
-
-static auto acc_nij = bob::extension::VariableDoc(
-  "acc_nij",
-  "array_like <float, 1D>",
-  "Accumulator updated during the E-step",
-  ""
-);
-PyObject* PyBobLearnEMIVectorTrainer_get_acc_nij(PyBobLearnEMIVectorTrainerObject* self, void*){
-  BOB_TRY
-  return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getAccNij());
-  BOB_CATCH_MEMBER("acc_nij could not be read", 0)
-}
-int PyBobLearnEMIVectorTrainer_set_acc_nij(PyBobLearnEMIVectorTrainerObject* self, PyObject* value, void*){
-  BOB_TRY
-  PyBlitzArrayObject* o;
-  if (!PyBlitzArray_Converter(value, &o)){
-    PyErr_Format(PyExc_RuntimeError, "%s %s expects a 1D array of floats", Py_TYPE(self)->tp_name, acc_nij.name());
-    return -1;
-  }
-  auto o_ = make_safe(o);
-  auto b = PyBlitzArrayCxx_AsBlitz<double,1>(o, "acc_nij");
-  if (!b) return -1;
-  self->cxx->setAccNij(*b);
-  return 0;
-  BOB_CATCH_MEMBER("acc_nij could not be set", -1)
-}
-
-
-static auto acc_snormij = bob::extension::VariableDoc(
-  "acc_snormij",
-  "array_like <float, 2D>",
-  "Accumulator updated during the E-step",
-  ""
-);
-PyObject* PyBobLearnEMIVectorTrainer_get_acc_snormij(PyBobLearnEMIVectorTrainerObject* self, void*){
-  BOB_TRY
-  return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getAccSnormij());
-  BOB_CATCH_MEMBER("acc_snormij could not be read", 0)
-}
-int PyBobLearnEMIVectorTrainer_set_acc_snormij(PyBobLearnEMIVectorTrainerObject* self, PyObject* value, void*){
-  BOB_TRY
-  PyBlitzArrayObject* o;
-  if (!PyBlitzArray_Converter(value, &o)){
-    PyErr_Format(PyExc_RuntimeError, "%s %s expects a 2D array of floats", Py_TYPE(self)->tp_name, acc_snormij.name());
-    return -1;
-  }
-  auto o_ = make_safe(o);
-  auto b = PyBlitzArrayCxx_AsBlitz<double,2>(o, "acc_snormij");
-  if (!b) return -1;
-  self->cxx->setAccSnormij(*b);
-  return 0;
-  BOB_CATCH_MEMBER("acc_snormij could not be set", -1)
-}
-
-
-
-
-static PyGetSetDef PyBobLearnEMIVectorTrainer_getseters[] = {
-  {
-   acc_nij_wij2.name(),
-   (getter)PyBobLearnEMIVectorTrainer_get_acc_nij_wij2,
-   (setter)PyBobLearnEMIVectorTrainer_set_acc_nij_wij2,
-   acc_nij_wij2.doc(),
-   0
-  },
-  {
-   acc_fnormij_wij.name(),
-   (getter)PyBobLearnEMIVectorTrainer_get_acc_fnormij_wij,
-   (setter)PyBobLearnEMIVectorTrainer_set_acc_fnormij_wij,
-   acc_fnormij_wij.doc(),
-   0
-  },
-  {
-   acc_nij.name(),
-   (getter)PyBobLearnEMIVectorTrainer_get_acc_nij,
-   (setter)PyBobLearnEMIVectorTrainer_set_acc_nij,
-   acc_nij.doc(),
-   0
-  },
-  {
-   acc_snormij.name(),
-   (getter)PyBobLearnEMIVectorTrainer_get_acc_snormij,
-   (setter)PyBobLearnEMIVectorTrainer_set_acc_snormij,
-   acc_snormij.doc(),
-   0
-  },
-
-  {0}  // Sentinel
-};
-
-
-/******************************************************************/
-/************ Functions Section ***********************************/
-/******************************************************************/
-
-/*** initialize ***/
-static auto initialize = bob::extension::FunctionDoc(
-  "initialize",
-  "Initialization before the EM steps",
-  "",
-  true
-)
-.add_prototype("ivector_machine, [stats], [rng]")
-.add_parameter("ivector_machine", ":py:class:`bob.learn.em.IVectorMachine`", "IVectorMachine Object")
-.add_parameter("stats", "object", "Ignored")
-.add_parameter("rng", ":py:class:`bob.core.random.mt19937`", "The Mersenne Twister mt19937 random generator used for the initialization of subspaces/arrays before the EM loop.");
-static PyObject* PyBobLearnEMIVectorTrainer_initialize(PyBobLearnEMIVectorTrainerObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  /* Parses input arguments in a single shot */
-  char** kwlist = initialize.kwlist(0);
-
-  PyBobLearnEMIVectorMachineObject* ivector_machine = 0;
-  PyObject* stats;
-  PyBoostMt19937Object* rng = 0;
-
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!|OO!", kwlist, &PyBobLearnEMIVectorMachine_Type, &ivector_machine,
-                                                                   &stats,
-                                                                   &PyBoostMt19937_Type, &rng)) return 0;
-
-  if(rng){
-    self->cxx->setRng(rng->rng);
-  }
-
-  self->cxx->initialize(*ivector_machine->cxx);
-
-  BOB_CATCH_MEMBER("cannot perform the initialize method", 0)
-
-  Py_RETURN_NONE;
-}
-
-
-/*** e_step ***/
-static auto e_step = bob::extension::FunctionDoc(
-  "e_step",
-  "Call the e-step procedure (for the U subspace).",
-  "",
-  true
-)
-.add_prototype("ivector_machine,stats")
-.add_parameter("ivector_machine", ":py:class:`bob.learn.em.ISVBase`", "IVectorMachine Object")
-.add_parameter("stats", ":py:class:`bob.learn.em.GMMStats`", "GMMStats Object");
-static PyObject* PyBobLearnEMIVectorTrainer_e_step(PyBobLearnEMIVectorTrainerObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  // Parses input arguments in a single shot
-  char** kwlist = e_step.kwlist(0);
-
-  PyBobLearnEMIVectorMachineObject* ivector_machine = 0;
-  PyObject* stats = 0;
-
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O!", kwlist, &PyBobLearnEMIVectorMachine_Type, &ivector_machine,
-                                                                 &PyList_Type, &stats)) return 0;
-
-  std::vector<bob::learn::em::GMMStats> training_data;
-  if(extract_GMMStats_1d(stats ,training_data)==0)
-    self->cxx->eStep(*ivector_machine->cxx, training_data);
-  else
-    return 0;
-
-  Py_RETURN_NONE;
-  BOB_CATCH_MEMBER("cannot perform the e_step method", 0)
-}
-
-
-/*** m_step ***/
-static auto m_step = bob::extension::FunctionDoc(
-  "m_step",
-  "Call the m-step procedure (for the U subspace).",
-  "",
-  true
-)
-.add_prototype("ivector_machine, [stats]")
-.add_parameter("ivector_machine", ":py:class:`bob.learn.em.ISVBase`", "IVectorMachine Object")
-.add_parameter("stats", "object", "Ignored");
-static PyObject* PyBobLearnEMIVectorTrainer_m_step(PyBobLearnEMIVectorTrainerObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  // Parses input arguments in a single shot
-  char** kwlist = m_step.kwlist(0);
-
-  PyBobLearnEMIVectorMachineObject* ivector_machine = 0;
-  PyObject* stats;
-
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!|O", kwlist, &PyBobLearnEMIVectorMachine_Type, &ivector_machine,
-                                                                 &stats)) return 0;
-
-  self->cxx->mStep(*ivector_machine->cxx);
-
-  BOB_CATCH_MEMBER("cannot perform the m_step method", 0)
-
-  Py_RETURN_NONE;
-}
-
-/*** reset_accumulators ***/
-static auto reset_accumulators = bob::extension::FunctionDoc(
-  "reset_accumulators",
-  "Reset the statistics accumulators to the correct size and a value of zero.",
-  0,
-  true
-)
-.add_prototype("ivector_machine")
-.add_parameter("ivector_machine", ":py:class:`bob.learn.em.IVectorMachine`", "The IVector machine containing the right dimensions");
-static PyObject* PyBobLearnEMIVectorTrainer_reset_accumulators(PyBobLearnEMIVectorTrainerObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  /* Parses input arguments in a single shot */
-  char** kwlist = reset_accumulators.kwlist(0);
-
-  PyBobLearnEMIVectorMachineObject* machine;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", kwlist, &PyBobLearnEMIVectorMachine_Type, &machine)) return 0;
-
-  self->cxx->resetAccumulators(*machine->cxx);
-  Py_RETURN_NONE;
-
-  BOB_CATCH_MEMBER("cannot perform the reset_accumulators method", 0)
-}
-
-
-static PyMethodDef PyBobLearnEMIVectorTrainer_methods[] = {
-  {
-    initialize.name(),
-    (PyCFunction)PyBobLearnEMIVectorTrainer_initialize,
-    METH_VARARGS|METH_KEYWORDS,
-    initialize.doc()
-  },
-  {
-    e_step.name(),
-    (PyCFunction)PyBobLearnEMIVectorTrainer_e_step,
-    METH_VARARGS|METH_KEYWORDS,
-    e_step.doc()
-  },
-  {
-    m_step.name(),
-    (PyCFunction)PyBobLearnEMIVectorTrainer_m_step,
-    METH_VARARGS|METH_KEYWORDS,
-    m_step.doc()
-  },
-  {
-    reset_accumulators.name(),
-    (PyCFunction)PyBobLearnEMIVectorTrainer_reset_accumulators,
-    METH_VARARGS|METH_KEYWORDS,
-    reset_accumulators.doc()
-  },
-  {0} /* Sentinel */
-};
-
-
-/******************************************************************/
-/************ Module Section **************************************/
-/******************************************************************/
-
-// Define the Gaussian type struct; will be initialized later
-PyTypeObject PyBobLearnEMIVectorTrainer_Type = {
-  PyVarObject_HEAD_INIT(0,0)
-  0
-};
-
-bool init_BobLearnEMIVectorTrainer(PyObject* module)
-{
-  // initialize the type struct
-  PyBobLearnEMIVectorTrainer_Type.tp_name      = IVectorTrainer_doc.name();
-  PyBobLearnEMIVectorTrainer_Type.tp_basicsize = sizeof(PyBobLearnEMIVectorTrainerObject);
-  PyBobLearnEMIVectorTrainer_Type.tp_flags     = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE;//Enable the class inheritance;
-  PyBobLearnEMIVectorTrainer_Type.tp_doc       = IVectorTrainer_doc.doc();
-
-  // set the functions
-  PyBobLearnEMIVectorTrainer_Type.tp_new          = PyType_GenericNew;
-  PyBobLearnEMIVectorTrainer_Type.tp_init         = reinterpret_cast<initproc>(PyBobLearnEMIVectorTrainer_init);
-  PyBobLearnEMIVectorTrainer_Type.tp_dealloc      = reinterpret_cast<destructor>(PyBobLearnEMIVectorTrainer_delete);
-  PyBobLearnEMIVectorTrainer_Type.tp_richcompare = reinterpret_cast<richcmpfunc>(PyBobLearnEMIVectorTrainer_RichCompare);
-  PyBobLearnEMIVectorTrainer_Type.tp_methods      = PyBobLearnEMIVectorTrainer_methods;
-  PyBobLearnEMIVectorTrainer_Type.tp_getset       = PyBobLearnEMIVectorTrainer_getseters;
-  //PyBobLearnEMIVectorTrainer_Type.tp_call         = reinterpret_cast<ternaryfunc>(PyBobLearnEMIVectorTrainer_compute_likelihood);
-
-
-  // check that everything is fine
-  if (PyType_Ready(&PyBobLearnEMIVectorTrainer_Type) < 0) return false;
-
-  // add the type to the module
-  Py_INCREF(&PyBobLearnEMIVectorTrainer_Type);
-  return PyModule_AddObject(module, "IVectorTrainer", (PyObject*)&PyBobLearnEMIVectorTrainer_Type) >= 0;
-}
diff --git a/bob/learn/em/jfa_base.cpp b/bob/learn/em/jfa_base.cpp
deleted file mode 100644
index aeec4a0367f758e18ba109bd13473afc25cea9cf..0000000000000000000000000000000000000000
--- a/bob/learn/em/jfa_base.cpp
+++ /dev/null
@@ -1,630 +0,0 @@
-/**
- * @date Wed Jan 27 17:03:15 2015 +0200
- * @author Tiago de Freitas Pereira <tiago.pereira@idiap.ch>
- *
- * @brief Python API for bob::learn::em
- *
- * Copyright (C) 2011-2014 Idiap Research Institute, Martigny, Switzerland
- */
-
-#include "main.h"
-
-/******************************************************************/
-/************ Constructor Section *********************************/
-/******************************************************************/
-
-static auto JFABase_doc = bob::extension::ClassDoc(
-  BOB_EXT_MODULE_PREFIX ".JFABase",
-  "Container for :math:`U`, :math:`V` and :math:`D` when performing Joint Factor Analysis (:ref:`JFA <jfa>`).\n\n"
-  "",
-  ""
-).add_constructor(
-  bob::extension::FunctionDoc(
-    "__init__",
-    "Constructor. Builds a new JFABase",
-    "",
-    true
-  )
-  .add_prototype("ubm,ru,rv","")
-  .add_prototype("other","")
-  .add_prototype("hdf5","")
-  .add_prototype("","")
-
-  .add_parameter("ubm", ":py:class:`bob.learn.em.GMMMachine`", "The Universal Background Model.")
-  .add_parameter("ru", "int", "Size of :math:`U` (Within client variation matrix). In the end the U matrix will have (#gaussians * #feature_dimension x ru)")
-  .add_parameter("rv", "int", "Size of :math:`V` (Between client variation matrix). In the end the U matrix will have (#gaussians * #feature_dimension x rv)")
-  .add_parameter("other", ":py:class:`bob.learn.em.JFABase`", "A JFABase object to be copied.")
-  .add_parameter("hdf5", ":py:class:`bob.io.base.HDF5File`", "An HDF5 file open for reading")
-
-);
-
-
-static int PyBobLearnEMJFABase_init_copy(PyBobLearnEMJFABaseObject* self, PyObject* args, PyObject* kwargs) {
-
-  char** kwlist = JFABase_doc.kwlist(1);
-  PyBobLearnEMJFABaseObject* o;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", kwlist, &PyBobLearnEMJFABase_Type, &o)){
-    JFABase_doc.print_usage();
-    return -1;
-  }
-
-  self->cxx.reset(new bob::learn::em::JFABase(*o->cxx));
-  return 0;
-}
-
-
-static int PyBobLearnEMJFABase_init_hdf5(PyBobLearnEMJFABaseObject* self, PyObject* args, PyObject* kwargs) {
-
-  char** kwlist = JFABase_doc.kwlist(2);
-
-  PyBobIoHDF5FileObject* config = 0;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, &PyBobIoHDF5File_Converter, &config)){
-    JFABase_doc.print_usage();
-    return -1;
-  }
-  auto config_ = make_safe(config);
-  self->cxx.reset(new bob::learn::em::JFABase(*(config->f)));
-
-  return 0;
-}
-
-
-static int PyBobLearnEMJFABase_init_ubm(PyBobLearnEMJFABaseObject* self, PyObject* args, PyObject* kwargs) {
-
-  char** kwlist = JFABase_doc.kwlist(0);
-  
-  PyBobLearnEMGMMMachineObject* ubm;
-  int ru = 1;
-  int rv = 1;
-
-  //Here we have to select which keyword argument to read  
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!ii", kwlist, &PyBobLearnEMGMMMachine_Type, &ubm,
-                                                                &ru, &rv)){
-    JFABase_doc.print_usage();
-    return -1;
-  }
-  
-  if(ru < 0){
-    PyErr_Format(PyExc_TypeError, "ru argument must be greater than or equal to one");
-    return -1;
-  }
-  
-  if(rv < 0){
-    PyErr_Format(PyExc_TypeError, "rv argument must be greater than or equal to one");
-    return -1;
-  }
-  
-  self->cxx.reset(new bob::learn::em::JFABase(ubm->cxx, ru, rv));
-  return 0;
-}
-
-
-static int PyBobLearnEMJFABase_init(PyBobLearnEMJFABaseObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  // get the number of command line arguments
-  int nargs = (args?PyTuple_Size(args):0) + (kwargs?PyDict_Size(kwargs):0);
-    
-  switch (nargs) {
-
-    case 1:{
-      //Reading the input argument
-      PyObject* arg = 0;
-      if (PyTuple_Size(args))
-        arg = PyTuple_GET_ITEM(args, 0);
-      else {
-        PyObject* tmp = PyDict_Values(kwargs);
-        auto tmp_ = make_safe(tmp);
-        arg = PyList_GET_ITEM(tmp, 0);
-      }
-
-      // If the constructor input is Gaussian object
-     if (PyBobLearnEMJFABase_Check(arg))
-       return PyBobLearnEMJFABase_init_copy(self, args, kwargs);
-      // If the constructor input is a HDF5
-     else if (PyBobIoHDF5File_Check(arg))
-       return PyBobLearnEMJFABase_init_hdf5(self, args, kwargs);
-    }
-    case 3:
-      return PyBobLearnEMJFABase_init_ubm(self, args, kwargs);
-    default:
-      PyErr_Format(PyExc_RuntimeError, "number of arguments mismatch - %s requires 1 or 3 arguments, but you provided %d (see help)", Py_TYPE(self)->tp_name, nargs);
-      JFABase_doc.print_usage();
-      return -1;
-  }
-  BOB_CATCH_MEMBER("cannot create JFABase", -1)
-  return 0;
-}
-
-
-
-static void PyBobLearnEMJFABase_delete(PyBobLearnEMJFABaseObject* self) {
-  self->cxx.reset();
-  Py_TYPE(self)->tp_free((PyObject*)self);
-}
-
-static PyObject* PyBobLearnEMJFABase_RichCompare(PyBobLearnEMJFABaseObject* self, PyObject* other, int op) {
-  BOB_TRY
-
-  if (!PyBobLearnEMJFABase_Check(other)) {
-    PyErr_Format(PyExc_TypeError, "cannot compare `%s' with `%s'", Py_TYPE(self)->tp_name, Py_TYPE(other)->tp_name);
-    return 0;
-  }
-  auto other_ = reinterpret_cast<PyBobLearnEMJFABaseObject*>(other);
-  switch (op) {
-    case Py_EQ:
-      if (*self->cxx==*other_->cxx) Py_RETURN_TRUE; else Py_RETURN_FALSE;
-    case Py_NE:
-      if (*self->cxx==*other_->cxx) Py_RETURN_FALSE; else Py_RETURN_TRUE;
-    default:
-      Py_INCREF(Py_NotImplemented);
-      return Py_NotImplemented;
-  }
-  BOB_CATCH_MEMBER("cannot compare JFABase objects", 0)
-}
-
-int PyBobLearnEMJFABase_Check(PyObject* o) {
-  return PyObject_IsInstance(o, reinterpret_cast<PyObject*>(&PyBobLearnEMJFABase_Type));
-}
-
-
-/******************************************************************/
-/************ Variables Section ***********************************/
-/******************************************************************/
-
-/***** shape *****/
-static auto shape = bob::extension::VariableDoc(
-  "shape",
-  "(int,int, int, int)",
-  "A tuple that represents the number of gaussians, dimensionality of each Gaussian, dimensionality of the :math:`rU` (within client variability matrix) and dimensionality of the :math:`rV` (between client variability matrix) ``(#Gaussians, #Inputs, #rU, #rV)``.",
-  ""
-);
-PyObject* PyBobLearnEMJFABase_getShape(PyBobLearnEMJFABaseObject* self, void*) {
-  BOB_TRY
-  return Py_BuildValue("(i,i,i,i)", self->cxx->getNGaussians(), self->cxx->getNInputs(), self->cxx->getDimRu(), self->cxx->getDimRv());
-  BOB_CATCH_MEMBER("shape could not be read", 0)
-}
-
-/***** supervector_length *****/
-static auto supervector_length = bob::extension::VariableDoc(
-  "supervector_length",
-  "int",
-
-  "Returns the supervector length.",
-  "NGaussians x NInputs: Number of Gaussian components by the feature dimensionality"
-  "An exception is thrown if no Universal Background Model has been set yet."
-  ""
-);
-PyObject* PyBobLearnEMJFABase_getSupervectorLength(PyBobLearnEMJFABaseObject* self, void*) {
-  BOB_TRY
-  return Py_BuildValue("i", self->cxx->getSupervectorLength());
-  BOB_CATCH_MEMBER("supervector_length could not be read", 0)
-}
-
-
-/***** u *****/
-static auto U = bob::extension::VariableDoc(
-  "u",
-  "array_like <float, 2D>",
-  "Returns the :math:`U` matrix (within client variability matrix)",
-  ""
-);
-PyObject* PyBobLearnEMJFABase_getU(PyBobLearnEMJFABaseObject* self, void*){
-  BOB_TRY
-  return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getU());
-  BOB_CATCH_MEMBER("``u`` could not be read", 0)
-}
-int PyBobLearnEMJFABase_setU(PyBobLearnEMJFABaseObject* self, PyObject* value, void*){
-  BOB_TRY
-  PyBlitzArrayObject* input;
-  if (!PyBlitzArray_Converter(value, &input)){
-    PyErr_Format(PyExc_RuntimeError, "%s %s expects a 2D array of floats", Py_TYPE(self)->tp_name, U.name());
-    return -1;
-  }
-  auto o_ = make_safe(input);
-  
-  // perform check on the input  
-  if (input->type_num != NPY_FLOAT64){
-    PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, U.name());
-    return -1;
-  }  
-
-  if (input->ndim != 2){
-    PyErr_Format(PyExc_TypeError, "`%s' only processes 2D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, U.name());
-    return -1;
-  }  
-
-  if (input->shape[0] != (Py_ssize_t)self->cxx->getU().extent(0) && input->shape[1] != self->cxx->getU().extent(1)) {
-    PyErr_Format(PyExc_TypeError, "`%s' 2D `input` array should have the shape [%" PY_FORMAT_SIZE_T "d, %" PY_FORMAT_SIZE_T "d] not [%" PY_FORMAT_SIZE_T "d, %" PY_FORMAT_SIZE_T "d] for `%s`", Py_TYPE(self)->tp_name, (Py_ssize_t)self->cxx->getU().extent(0), (Py_ssize_t)self->cxx->getU().extent(1), (Py_ssize_t)input->shape[0], (Py_ssize_t)input->shape[1], U.name());
-    return -1;
-  }  
-  
-  
-  auto b = PyBlitzArrayCxx_AsBlitz<double,2>(input, "u");
-  if (!b) return -1;
-  self->cxx->setU(*b);
-  return 0;
-  BOB_CATCH_MEMBER("``u`` matrix could not be set", -1)
-}
-
-/***** v *****/
-static auto V = bob::extension::VariableDoc(
-  "v",
-  "array_like <float, 2D>",
-  "Returns the :math:`V` matrix (between client variability matrix)",
-  ""
-);
-PyObject* PyBobLearnEMJFABase_getV(PyBobLearnEMJFABaseObject* self, void*){
-  BOB_TRY
-  return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getV());
-  BOB_CATCH_MEMBER("``v`` could not be read", 0)
-}
-int PyBobLearnEMJFABase_setV(PyBobLearnEMJFABaseObject* self, PyObject* value, void*){
-  BOB_TRY
-  PyBlitzArrayObject* input;
-  if (!PyBlitzArray_Converter(value, &input)){
-    PyErr_Format(PyExc_RuntimeError, "%s %s expects a 2D array of floats", Py_TYPE(self)->tp_name, V.name());
-    return -1;
-  }
-  auto o_ = make_safe(input);
-  
-  // perform check on the input  
-  if (input->type_num != NPY_FLOAT64){
-    PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, V.name());
-    return -1;
-  }  
-
-  if (input->ndim != 2){
-    PyErr_Format(PyExc_TypeError, "`%s' only processes 2D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, V.name());
-    return -1;
-  }  
-
-  if (input->shape[0] != (Py_ssize_t)self->cxx->getV().extent(0) && input->shape[1] != self->cxx->getV().extent(1)) {
-    PyErr_Format(PyExc_TypeError, "`%s' 2D `input` array should have the shape [%" PY_FORMAT_SIZE_T "d, %" PY_FORMAT_SIZE_T "d] not [%" PY_FORMAT_SIZE_T "d, %" PY_FORMAT_SIZE_T "d] for `%s`", Py_TYPE(self)->tp_name, (Py_ssize_t)self->cxx->getV().extent(0), (Py_ssize_t)self->cxx->getV().extent(1), (Py_ssize_t)input->shape[0], (Py_ssize_t)input->shape[1], V.name());
-    return -1;
-  }  
-  
-  auto b = PyBlitzArrayCxx_AsBlitz<double,2>(input, "v");
-  if (!b) return -1;
-  self->cxx->setV(*b);
-  return 0;
-  BOB_CATCH_MEMBER("``v`` matrix could not be set", -1)
-}
-
-
-/***** d *****/
-static auto D = bob::extension::VariableDoc(
-  "d",
-  "array_like <float, 1D>",
-  "Returns the diagonal matrix :math:`diag(d)` (as a 1D vector)",
-  ""
-);
-PyObject* PyBobLearnEMJFABase_getD(PyBobLearnEMJFABaseObject* self, void*){
-  BOB_TRY
-  return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getD());
-  BOB_CATCH_MEMBER("``d`` could not be read", 0)
-}
-int PyBobLearnEMJFABase_setD(PyBobLearnEMJFABaseObject* self, PyObject* value, void*){
-  BOB_TRY
-  PyBlitzArrayObject* input;
-  if (!PyBlitzArray_Converter(value, &input)){
-    PyErr_Format(PyExc_RuntimeError, "%s %s expects a 1D array of floats", Py_TYPE(self)->tp_name, D.name());
-    return -1;
-  }
-  auto o_ = make_safe(input);
-  
-  // perform check on the input  
-  if (input->type_num != NPY_FLOAT64){
-    PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, D.name());
-    return -1;
-  }  
-
-  if (input->ndim != 1){
-    PyErr_Format(PyExc_TypeError, "`%s' only processes 1D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, D.name());
-    return -1;
-  }  
-
-  if (input->shape[0] != (Py_ssize_t)self->cxx->getD().extent(0)) {
-    PyErr_Format(PyExc_TypeError, "`%s' 1D `input` array should have %" PY_FORMAT_SIZE_T "d elements, not [%" PY_FORMAT_SIZE_T "d for `%s`", Py_TYPE(self)->tp_name, (Py_ssize_t)self->cxx->getD().extent(0), (Py_ssize_t)input->shape[0], D.name());
-    return -1;
-  }  
-
-  auto b = PyBlitzArrayCxx_AsBlitz<double,1>(input, "d");
-  if (!b) return -1;
-  self->cxx->setD(*b);
-  return 0;
-  BOB_CATCH_MEMBER("``d`` matrix could not be set", -1)
-}
-
-
-/***** ubm *****/
-static auto ubm = bob::extension::VariableDoc(
-  "ubm",
-  ":py:class:`bob.learn.em.GMMMachine`",
-  "Returns the UBM (Universal Background Model",
-  ""
-);
-PyObject* PyBobLearnEMJFABase_getUBM(PyBobLearnEMJFABaseObject* self, void*){
-  BOB_TRY
-
-  boost::shared_ptr<bob::learn::em::GMMMachine> ubm_gmmMachine = self->cxx->getUbm();
-
-  //Allocating the correspondent python object
-  PyBobLearnEMGMMMachineObject* retval =
-    (PyBobLearnEMGMMMachineObject*)PyBobLearnEMGMMMachine_Type.tp_alloc(&PyBobLearnEMGMMMachine_Type, 0);
-  retval->cxx = ubm_gmmMachine;
-
-  return Py_BuildValue("N",retval);
-  BOB_CATCH_MEMBER("ubm could not be read", 0)
-}
-int PyBobLearnEMJFABase_setUBM(PyBobLearnEMJFABaseObject* self, PyObject* value, void*){
-  BOB_TRY
-
-  if (!PyBobLearnEMGMMMachine_Check(value)){
-    PyErr_Format(PyExc_RuntimeError, "%s %s expects a :py:class:`bob.learn.em.GMMMachine`", Py_TYPE(self)->tp_name, ubm.name());
-    return -1;
-  }
-
-  PyBobLearnEMGMMMachineObject* ubm_gmmMachine = 0;
-  PyArg_Parse(value, "O!", &PyBobLearnEMGMMMachine_Type,&ubm_gmmMachine);
-
-  self->cxx->setUbm(ubm_gmmMachine->cxx);
-
-  return 0;
-  BOB_CATCH_MEMBER("ubm could not be set", -1)  
-}
-
-
-
-
-static PyGetSetDef PyBobLearnEMJFABase_getseters[] = { 
-  {
-   shape.name(),
-   (getter)PyBobLearnEMJFABase_getShape,
-   0,
-   shape.doc(),
-   0
-  },
-  
-  {
-   supervector_length.name(),
-   (getter)PyBobLearnEMJFABase_getSupervectorLength,
-   0,
-   supervector_length.doc(),
-   0
-  },
-  
-  {
-   U.name(),
-   (getter)PyBobLearnEMJFABase_getU,
-   (setter)PyBobLearnEMJFABase_setU,
-   U.doc(),
-   0
-  },
-  
-  {
-   V.name(),
-   (getter)PyBobLearnEMJFABase_getV,
-   (setter)PyBobLearnEMJFABase_setV,
-   V.doc(),
-   0
-  },
-
-  {
-   D.name(),
-   (getter)PyBobLearnEMJFABase_getD,
-   (setter)PyBobLearnEMJFABase_setD,
-   D.doc(),
-   0
-  },
-
-  {
-   ubm.name(),
-   (getter)PyBobLearnEMJFABase_getUBM,
-   (setter)PyBobLearnEMJFABase_setUBM,
-   ubm.doc(),
-   0
-  },
-
-
-  {0}  // Sentinel
-};
-
-
-/******************************************************************/
-/************ Functions Section ***********************************/
-/******************************************************************/
-
-
-/*** save ***/
-static auto save = bob::extension::FunctionDoc(
-  "save",
-  "Save the configuration of the JFABase to a given HDF5 file"
-)
-.add_prototype("hdf5")
-.add_parameter("hdf5", ":py:class:`bob.io.base.HDF5File`", "An HDF5 file open for writing");
-static PyObject* PyBobLearnEMJFABase_Save(PyBobLearnEMJFABaseObject* self,  PyObject* args, PyObject* kwargs) {
-
-  BOB_TRY
-  
-  // get list of arguments
-  char** kwlist = save.kwlist(0);  
-  PyBobIoHDF5FileObject* hdf5;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, PyBobIoHDF5File_Converter, &hdf5)) return 0;
-
-  auto hdf5_ = make_safe(hdf5);
-  self->cxx->save(*hdf5->f);
-
-  BOB_CATCH_MEMBER("cannot save the data", 0)
-  Py_RETURN_NONE;
-}
-
-/*** load ***/
-static auto load = bob::extension::FunctionDoc(
-  "load",
-  "Load the configuration of the JFABase to a given HDF5 file"
-)
-.add_prototype("hdf5")
-.add_parameter("hdf5", ":py:class:`bob.io.base.HDF5File`", "An HDF5 file open for reading");
-static PyObject* PyBobLearnEMJFABase_Load(PyBobLearnEMJFABaseObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-  
-  char** kwlist = load.kwlist(0);  
-  PyBobIoHDF5FileObject* hdf5;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, PyBobIoHDF5File_Converter, &hdf5)) return 0;
-  
-  auto hdf5_ = make_safe(hdf5);  
-  self->cxx->load(*hdf5->f);
-
-  BOB_CATCH_MEMBER("cannot load the data", 0)
-  Py_RETURN_NONE;
-}
-
-
-/*** is_similar_to ***/
-static auto is_similar_to = bob::extension::FunctionDoc(
-  "is_similar_to",
-  
-  "Compares this JFABase with the ``other`` one to be approximately the same.",
-  "The optional values ``r_epsilon`` and ``a_epsilon`` refer to the "
-  "relative and absolute precision for the ``weights``, ``biases`` "
-  "and any other values internal to this machine."
-)
-.add_prototype("other, [r_epsilon], [a_epsilon]","output")
-.add_parameter("other", ":py:class:`bob.learn.em.JFABase`", "A JFABase object to be compared.")
-.add_parameter("r_epsilon", "float", "Relative precision.")
-.add_parameter("a_epsilon", "float", "Absolute precision.")
-.add_return("output","bool","True if it is similar, otherwise false.");
-static PyObject* PyBobLearnEMJFABase_IsSimilarTo(PyBobLearnEMJFABaseObject* self, PyObject* args, PyObject* kwds) {
-
-  /* Parses input arguments in a single shot */
-  char** kwlist = is_similar_to.kwlist(0);
-
-  //PyObject* other = 0;
-  PyBobLearnEMJFABaseObject* other = 0;
-  double r_epsilon = 1.e-5;
-  double a_epsilon = 1.e-8;
-
-  if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|dd", kwlist,
-        &PyBobLearnEMJFABase_Type, &other,
-        &r_epsilon, &a_epsilon)){
-
-        is_similar_to.print_usage(); 
-        return 0;        
-  }
-
-  if (self->cxx->is_similar_to(*other->cxx, r_epsilon, a_epsilon))
-    Py_RETURN_TRUE;
-  else
-    Py_RETURN_FALSE;
-}
-
-
-/*** resize ***/
-static auto resize = bob::extension::FunctionDoc(
-  "resize",
-  "Resets the dimensionality of the subspace U and V. "
-  "U and V are hence uninitialized",
-  0,
-  true
-)
-.add_prototype("rU,rV")
-.add_parameter("rU", "int", "Size of :math:`U` (Within client variation matrix)")
-.add_parameter("rV", "int", "Size of :math:`V` (Between client variation matrix)");
-static PyObject* PyBobLearnEMJFABase_resize(PyBobLearnEMJFABaseObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  /* Parses input arguments in a single shot */
-  char** kwlist = resize.kwlist(0);
-
-  int rU = 0;
-  int rV = 0;
-
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ii", kwlist, &rU, &rV)) return 0;
-
-  if (rU <= 0){
-    PyErr_Format(PyExc_TypeError, "rU must be greater than zero");
-    resize.print_usage();
-    return 0;
-  }
-  if (rV <= 0){
-    PyErr_Format(PyExc_TypeError, "rV must be greater than zero");
-    resize.print_usage();
-    return 0;
-  }
-  self->cxx->resize(rU, rV);
-
-  BOB_CATCH_MEMBER("cannot perform the resize method", 0)
-
-  Py_RETURN_NONE;
-}
-
-
-
-
-static PyMethodDef PyBobLearnEMJFABase_methods[] = {
-  {
-    save.name(),
-    (PyCFunction)PyBobLearnEMJFABase_Save,
-    METH_VARARGS|METH_KEYWORDS,
-    save.doc()
-  },
-  {
-    load.name(),
-    (PyCFunction)PyBobLearnEMJFABase_Load,
-    METH_VARARGS|METH_KEYWORDS,
-    load.doc()
-  },
-  {
-    is_similar_to.name(),
-    (PyCFunction)PyBobLearnEMJFABase_IsSimilarTo,
-    METH_VARARGS|METH_KEYWORDS,
-    is_similar_to.doc()
-  },
-  {
-    resize.name(),
-    (PyCFunction)PyBobLearnEMJFABase_resize,
-    METH_VARARGS|METH_KEYWORDS,
-    resize.doc()
-  },
-  
-  {0} /* Sentinel */
-};
-
-
-/******************************************************************/
-/************ Module Section **************************************/
-/******************************************************************/
-
-// Define the JFA type struct; will be initialized later
-PyTypeObject PyBobLearnEMJFABase_Type = {
-  PyVarObject_HEAD_INIT(0,0)
-  0
-};
-
-bool init_BobLearnEMJFABase(PyObject* module)
-{
-  // initialize the type struct
-  PyBobLearnEMJFABase_Type.tp_name      = JFABase_doc.name();
-  PyBobLearnEMJFABase_Type.tp_basicsize = sizeof(PyBobLearnEMJFABaseObject);
-  PyBobLearnEMJFABase_Type.tp_flags     = Py_TPFLAGS_DEFAULT;
-  PyBobLearnEMJFABase_Type.tp_doc       = JFABase_doc.doc();
-
-  // set the functions
-  PyBobLearnEMJFABase_Type.tp_new         = PyType_GenericNew;
-  PyBobLearnEMJFABase_Type.tp_init        = reinterpret_cast<initproc>(PyBobLearnEMJFABase_init);
-  PyBobLearnEMJFABase_Type.tp_dealloc     = reinterpret_cast<destructor>(PyBobLearnEMJFABase_delete);
-  PyBobLearnEMJFABase_Type.tp_richcompare = reinterpret_cast<richcmpfunc>(PyBobLearnEMJFABase_RichCompare);
-  PyBobLearnEMJFABase_Type.tp_methods     = PyBobLearnEMJFABase_methods;
-  PyBobLearnEMJFABase_Type.tp_getset      = PyBobLearnEMJFABase_getseters;
-  //PyBobLearnEMJFABase_Type.tp_call = reinterpret_cast<ternaryfunc>(PyBobLearnEMJFABase_forward);
-
-
-  // check that everything is fine
-  if (PyType_Ready(&PyBobLearnEMJFABase_Type) < 0) return false;
-
-  // add the type to the module
-  Py_INCREF(&PyBobLearnEMJFABase_Type);
-  return PyModule_AddObject(module, "JFABase", (PyObject*)&PyBobLearnEMJFABase_Type) >= 0;
-}
-
diff --git a/bob/learn/em/jfa_machine.cpp b/bob/learn/em/jfa_machine.cpp
deleted file mode 100644
index 95ceb9766959bbd31d55e269f31bc9c2c4b1dd93..0000000000000000000000000000000000000000
--- a/bob/learn/em/jfa_machine.cpp
+++ /dev/null
@@ -1,734 +0,0 @@
-/**
- * @date Wed Jan 28 17:03:15 2015 +0200
- * @author Tiago de Freitas Pereira <tiago.pereira@idiap.ch>
- *
- * @brief Python API for bob::learn::em
- *
- * Copyright (C) 2011-2014 Idiap Research Institute, Martigny, Switzerland
- */
-
-#include "main.h"
-
-/******************************************************************/
-/************ Constructor Section *********************************/
-/******************************************************************/
-
-static auto JFAMachine_doc = bob::extension::ClassDoc(
-  BOB_EXT_MODULE_PREFIX ".JFAMachine",
-  "A JFAMachine. An attached :py:class:`bob.learn.em.JFABase` should be provided for Joint Factor Analysis. The :py:class:`bob.learn.em.JFAMachine` carries information about the speaker factors :math:`y` and :math:`z`, whereas a :py:class:`bob.learn.em.JFABase` carries information about the matrices :math:`U`, :math:`V` and :math:`D`.\n\n"
-  "References: [Vogt2008]_ [McCool2013]_",
-  ""
-).add_constructor(
-  bob::extension::FunctionDoc(
-    "__init__",
-    "Constructor. Builds a new JFAMachine",
-    "",
-    true
-  )
-  .add_prototype("jfa_base","")
-  .add_prototype("other","")
-  .add_prototype("hdf5","")
-
-  .add_parameter("jfa_base", ":py:class:`bob.learn.em.JFABase`", "The JFABase associated with this machine")
-  .add_parameter("other", ":py:class:`bob.learn.em.JFAMachine`", "A JFAMachine object to be copied.")
-  .add_parameter("hdf5", ":py:class:`bob.io.base.HDF5File`", "An HDF5 file open for reading")
-
-);
-
-
-static int PyBobLearnEMJFAMachine_init_copy(PyBobLearnEMJFAMachineObject* self, PyObject* args, PyObject* kwargs) {
-
-  char** kwlist = JFAMachine_doc.kwlist(1);
-  PyBobLearnEMJFAMachineObject* o;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", kwlist, &PyBobLearnEMJFAMachine_Type, &o)){
-    JFAMachine_doc.print_usage();
-    return -1;
-  }
-
-  self->cxx.reset(new bob::learn::em::JFAMachine(*o->cxx));
-  return 0;
-}
-
-
-static int PyBobLearnEMJFAMachine_init_hdf5(PyBobLearnEMJFAMachineObject* self, PyObject* args, PyObject* kwargs) {
-
-  char** kwlist = JFAMachine_doc.kwlist(2);
-
-  PyBobIoHDF5FileObject* config = 0;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, &PyBobIoHDF5File_Converter, &config)){
-    JFAMachine_doc.print_usage();
-    return -1;
-  }
-  auto config_ = make_safe(config);
-  self->cxx.reset(new bob::learn::em::JFAMachine(*(config->f)));
-
-  return 0;
-}
-
-
-static int PyBobLearnEMJFAMachine_init_jfabase(PyBobLearnEMJFAMachineObject* self, PyObject* args, PyObject* kwargs) {
-
-  char** kwlist = JFAMachine_doc.kwlist(0);
-
-  PyBobLearnEMJFABaseObject* jfa_base;
-
-  //Here we have to select which keyword argument to read
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", kwlist, &PyBobLearnEMJFABase_Type, &jfa_base)){
-    JFAMachine_doc.print_usage();
-    return -1;
-  }
-
-  self->cxx.reset(new bob::learn::em::JFAMachine(jfa_base->cxx));
-  return 0;
-}
-
-
-static int PyBobLearnEMJFAMachine_init(PyBobLearnEMJFAMachineObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  // get the number of command line arguments
-  int nargs = (args?PyTuple_Size(args):0) + (kwargs?PyDict_Size(kwargs):0);
-
-  if(nargs == 1){
-    //Reading the input argument
-    PyObject* arg = 0;
-    if (PyTuple_Size(args))
-      arg = PyTuple_GET_ITEM(args, 0);
-    else {
-      PyObject* tmp = PyDict_Values(kwargs);
-      auto tmp_ = make_safe(tmp);
-      arg = PyList_GET_ITEM(tmp, 0);
-    }
-
-    // If the constructor input is Gaussian object
-    if (PyBobLearnEMJFAMachine_Check(arg))
-      return PyBobLearnEMJFAMachine_init_copy(self, args, kwargs);
-    // If the constructor input is a HDF5
-    else if (PyBobIoHDF5File_Check(arg))
-      return PyBobLearnEMJFAMachine_init_hdf5(self, args, kwargs);
-    // If the constructor input is a JFABase Object
-    else
-      return PyBobLearnEMJFAMachine_init_jfabase(self, args, kwargs);
-  }
-  else{
-    PyErr_Format(PyExc_RuntimeError, "number of arguments mismatch - %s requires only 1 argument, but you provided %d (see help)", Py_TYPE(self)->tp_name, nargs);
-    JFAMachine_doc.print_usage();
-    return -1;
-  }
-
-  BOB_CATCH_MEMBER("cannot create JFAMachine", -1)
-  return 0;
-}
-
-static void PyBobLearnEMJFAMachine_delete(PyBobLearnEMJFAMachineObject* self) {
-  self->cxx.reset();
-  Py_TYPE(self)->tp_free((PyObject*)self);
-}
-
-static PyObject* PyBobLearnEMJFAMachine_RichCompare(PyBobLearnEMJFAMachineObject* self, PyObject* other, int op) {
-  BOB_TRY
-
-  if (!PyBobLearnEMJFAMachine_Check(other)) {
-    PyErr_Format(PyExc_TypeError, "cannot compare `%s' with `%s'", Py_TYPE(self)->tp_name, Py_TYPE(other)->tp_name);
-    return 0;
-  }
-  auto other_ = reinterpret_cast<PyBobLearnEMJFAMachineObject*>(other);
-  switch (op) {
-    case Py_EQ:
-      if (*self->cxx==*other_->cxx) Py_RETURN_TRUE; else Py_RETURN_FALSE;
-    case Py_NE:
-      if (*self->cxx==*other_->cxx) Py_RETURN_FALSE; else Py_RETURN_TRUE;
-    default:
-      Py_INCREF(Py_NotImplemented);
-      return Py_NotImplemented;
-  }
-  BOB_CATCH_MEMBER("cannot compare JFAMachine objects", 0)
-}
-
-int PyBobLearnEMJFAMachine_Check(PyObject* o) {
-  return PyObject_IsInstance(o, reinterpret_cast<PyObject*>(&PyBobLearnEMJFAMachine_Type));
-}
-
-
-/******************************************************************/
-/************ Variables Section ***********************************/
-/******************************************************************/
-
-/***** shape *****/
-static auto shape = bob::extension::VariableDoc(
-  "shape",
-  "(int,int, int, int)",
-  "A tuple that represents the number of gaussians, dimensionality of each Gaussian, dimensionality of the rU (within client variability matrix) and dimensionality of the rV (between client variability matrix) ``(#Gaussians, #Inputs, #rU, #rV)``.",
-  ""
-);
-PyObject* PyBobLearnEMJFAMachine_getShape(PyBobLearnEMJFAMachineObject* self, void*) {
-  BOB_TRY
-  return Py_BuildValue("(i,i,i,i)", self->cxx->getNGaussians(), self->cxx->getNInputs(), self->cxx->getDimRu(), self->cxx->getDimRv());
-  BOB_CATCH_MEMBER("shape could not be read", 0)
-}
-
-/***** supervector_length *****/
-static auto supervector_length = bob::extension::VariableDoc(
-  "supervector_length",
-  "int",
-
-  "Returns the supervector length.",
-  "NGaussians x NInputs: Number of Gaussian components by the feature dimensionality. "
-  "An exception is thrown if no Universal Background Model has been set yet."
-  ""
-);
-PyObject* PyBobLearnEMJFAMachine_getSupervectorLength(PyBobLearnEMJFAMachineObject* self, void*) {
-  BOB_TRY
-  return Py_BuildValue("i", self->cxx->getSupervectorLength());
-  BOB_CATCH_MEMBER("supervector_length could not be read", 0)
-}
-
-
-/***** y *****/
-static auto Y = bob::extension::VariableDoc(
-  "y",
-  "array_like <float, 1D>",
-  "Returns the :math:`y` speaker factor. Eq (30) from [McCool2013]_",
-  ""
-);
-PyObject* PyBobLearnEMJFAMachine_getY(PyBobLearnEMJFAMachineObject* self, void*){
-  BOB_TRY
-  return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getY());
-  BOB_CATCH_MEMBER("`y` could not be read", 0)
-}
-int PyBobLearnEMJFAMachine_setY(PyBobLearnEMJFAMachineObject* self, PyObject* value, void*){
-  BOB_TRY
-  PyBlitzArrayObject* input;
-  if (!PyBlitzArray_Converter(value, &input)){
-    PyErr_Format(PyExc_RuntimeError, "%s %s expects a 1D array of floats", Py_TYPE(self)->tp_name, Y.name());
-    return -1;
-  }
-  auto o_ = make_safe(input);
-
-  // perform check on the input
-  if (input->type_num != NPY_FLOAT64){
-    PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, Y.name());
-    return -1;
-  }
-
-  if (input->ndim != 1){
-    PyErr_Format(PyExc_TypeError, "`%s' only processes 1D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, Y.name());
-    return -1;
-  }
-
-  if (input->shape[0] != (Py_ssize_t)self->cxx->getY().extent(0)) {
-    PyErr_Format(PyExc_TypeError, "`%s' 1D `input` array should have %" PY_FORMAT_SIZE_T "d, elements, not %" PY_FORMAT_SIZE_T "d for `%s`", Py_TYPE(self)->tp_name, (Py_ssize_t)self->cxx->getY().extent(0), (Py_ssize_t)input->shape[0], Y.name());
-    return -1;
-  }
-
-
-  auto b = PyBlitzArrayCxx_AsBlitz<double,1>(input, "y");
-  if (!b) return -1;
-  self->cxx->setY(*b);
-  return 0;
-  BOB_CATCH_MEMBER("`y` vector could not be set", -1)
-}
-
-
-/***** z *****/
-static auto Z = bob::extension::VariableDoc(
-  "z",
-  "array_like <float, 1D>",
-  "Returns the :math:`z` speaker factor. Eq (31) from [McCool2013]_",
-  ""
-);
-PyObject* PyBobLearnEMJFAMachine_getZ(PyBobLearnEMJFAMachineObject* self, void*){
-  BOB_TRY
-  return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getZ());
-  BOB_CATCH_MEMBER("`z` could not be read", 0)
-}
-int PyBobLearnEMJFAMachine_setZ(PyBobLearnEMJFAMachineObject* self, PyObject* value, void*){
-  BOB_TRY
-  PyBlitzArrayObject* input;
-  if (!PyBlitzArray_Converter(value, &input)){
-    PyErr_Format(PyExc_RuntimeError, "%s %s expects a 1D array of floats", Py_TYPE(self)->tp_name, Z.name());
-    return -1;
-  }
-  auto o_ = make_safe(input);
-
-  // perform check on the input
-  if (input->type_num != NPY_FLOAT64){
-    PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, Z.name());
-    return -1;
-  }
-
-  if (input->ndim != 1){
-    PyErr_Format(PyExc_TypeError, "`%s' only processes 1D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, Z.name());
-    return -1;
-  }
-
-  if (input->shape[0] != (Py_ssize_t)self->cxx->getZ().extent(0)) {
-    PyErr_Format(PyExc_TypeError, "`%s' 1D `input` array should have %" PY_FORMAT_SIZE_T "d, elements, not %" PY_FORMAT_SIZE_T "d for `%s`", Py_TYPE(self)->tp_name, (Py_ssize_t)self->cxx->getZ().extent(0), (Py_ssize_t)input->shape[0], Z.name());
-    return -1;
-  }
-
-  auto b = PyBlitzArrayCxx_AsBlitz<double,1>(input, "z");
-  if (!b) return -1;
-  self->cxx->setZ(*b);
-  return 0;
-  BOB_CATCH_MEMBER("`z` vector could not be set", -1)
-}
-
-
-/***** x *****/
-static auto X = bob::extension::VariableDoc(
-  "x",
-  "array_like <float, 1D>",
-  "Returns the :math:`X` session factor. Eq (29) from [McCool2013]_",
-  "The latent variable :math:`x` (last one computed). This is a feature provided for convenience, but this attribute is not 'part' of the machine. The session latent variable :math:`x` is indeed not class-specific, but depends on the sample considered. Furthermore, it is not saved into the machine or used when comparing machines."
-);
-PyObject* PyBobLearnEMJFAMachine_getX(PyBobLearnEMJFAMachineObject* self, void*){
-  BOB_TRY
-  return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getX());
-  BOB_CATCH_MEMBER("`x` could not be read", 0)
-}
-
-
-/***** jfa_base *****/
-static auto jfa_base = bob::extension::VariableDoc(
-  "jfa_base",
-  ":py:class:`bob.learn.em.JFABase`",
-  "The JFABase attached to this machine",
-  ""
-);
-PyObject* PyBobLearnEMJFAMachine_getJFABase(PyBobLearnEMJFAMachineObject* self, void*){
-  BOB_TRY
-
-  boost::shared_ptr<bob::learn::em::JFABase> jfa_base_o = self->cxx->getJFABase();
-
-  //Allocating the correspondent python object
-  PyBobLearnEMJFABaseObject* retval =
-    (PyBobLearnEMJFABaseObject*)PyBobLearnEMJFABase_Type.tp_alloc(&PyBobLearnEMJFABase_Type, 0);
-  retval->cxx = jfa_base_o;
-
-  return Py_BuildValue("N",retval);
-  BOB_CATCH_MEMBER("jfa_base could not be read", 0)
-}
-int PyBobLearnEMJFAMachine_setJFABase(PyBobLearnEMJFAMachineObject* self, PyObject* value, void*){
-  BOB_TRY
-
-  if (!PyBobLearnEMJFABase_Check(value)){
-    PyErr_Format(PyExc_RuntimeError, "%s %s expects a :py:class:`bob.learn.em.JFABase`", Py_TYPE(self)->tp_name, jfa_base.name());
-    return -1;
-  }
-
-  PyBobLearnEMJFABaseObject* jfa_base_o = 0;
-  PyArg_Parse(value, "O!", &PyBobLearnEMJFABase_Type,&jfa_base_o);
-
-  self->cxx->setJFABase(jfa_base_o->cxx);
-
-  return 0;
-  BOB_CATCH_MEMBER("jfa_base could not be set", -1)
-}
-
-
-
-
-static PyGetSetDef PyBobLearnEMJFAMachine_getseters[] = {
-  {
-   shape.name(),
-   (getter)PyBobLearnEMJFAMachine_getShape,
-   0,
-   shape.doc(),
-   0
-  },
-
-  {
-   supervector_length.name(),
-   (getter)PyBobLearnEMJFAMachine_getSupervectorLength,
-   0,
-   supervector_length.doc(),
-   0
-  },
-
-  {
-   jfa_base.name(),
-   (getter)PyBobLearnEMJFAMachine_getJFABase,
-   (setter)PyBobLearnEMJFAMachine_setJFABase,
-   jfa_base.doc(),
-   0
-  },
-
-  {
-   Y.name(),
-   (getter)PyBobLearnEMJFAMachine_getY,
-   (setter)PyBobLearnEMJFAMachine_setY,
-   Y.doc(),
-   0
-  },
-
-  {
-   Z.name(),
-   (getter)PyBobLearnEMJFAMachine_getZ,
-   (setter)PyBobLearnEMJFAMachine_setZ,
-   Z.doc(),
-   0
-  },
-
-  {
-   X.name(),
-   (getter)PyBobLearnEMJFAMachine_getX,
-   0,
-   X.doc(),
-   0
-  },
-
-
-  {0}  // Sentinel
-};
-
-
-/******************************************************************/
-/************ Functions Section ***********************************/
-/******************************************************************/
-
-
-/*** save ***/
-static auto save = bob::extension::FunctionDoc(
-  "save",
-  "Save the configuration of the JFAMachine to a given HDF5 file"
-)
-.add_prototype("hdf5")
-.add_parameter("hdf5", ":py:class:`bob.io.base.HDF5File`", "An HDF5 file open for writing");
-static PyObject* PyBobLearnEMJFAMachine_Save(PyBobLearnEMJFAMachineObject* self,  PyObject* args, PyObject* kwargs) {
-
-  BOB_TRY
-
-  // get list of arguments
-  char** kwlist = save.kwlist(0);
-  PyBobIoHDF5FileObject* hdf5;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, PyBobIoHDF5File_Converter, &hdf5)) return 0;
-
-  auto hdf5_ = make_safe(hdf5);
-  self->cxx->save(*hdf5->f);
-
-  BOB_CATCH_MEMBER("cannot save the data", 0)
-  Py_RETURN_NONE;
-}
-
-/*** load ***/
-static auto load = bob::extension::FunctionDoc(
-  "load",
-  "Load the configuration of the JFAMachine to a given HDF5 file"
-)
-.add_prototype("hdf5")
-.add_parameter("hdf5", ":py:class:`bob.io.base.HDF5File`", "An HDF5 file open for reading");
-static PyObject* PyBobLearnEMJFAMachine_Load(PyBobLearnEMJFAMachineObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  char** kwlist = load.kwlist(0);
-  PyBobIoHDF5FileObject* hdf5;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, PyBobIoHDF5File_Converter, &hdf5)) return 0;
-
-  auto hdf5_ = make_safe(hdf5);
-  self->cxx->load(*hdf5->f);
-
-  BOB_CATCH_MEMBER("cannot load the data", 0)
-  Py_RETURN_NONE;
-}
-
-
-/*** is_similar_to ***/
-static auto is_similar_to = bob::extension::FunctionDoc(
-  "is_similar_to",
-
-  "Compares this JFAMachine with the ``other`` one to be approximately the same.",
-  "The optional values ``r_epsilon`` and ``a_epsilon`` refer to the "
-  "relative and absolute precision for the ``weights``, ``biases`` "
-  "and any other values internal to this machine."
-)
-.add_prototype("other, [r_epsilon], [a_epsilon]","output")
-.add_parameter("other", ":py:class:`bob.learn.em.JFAMachine`", "A JFAMachine object to be compared.")
-.add_parameter("r_epsilon", "float", "Relative precision.")
-.add_parameter("a_epsilon", "float", "Absolute precision.")
-.add_return("output","bool","True if it is similar, otherwise false.");
-static PyObject* PyBobLearnEMJFAMachine_IsSimilarTo(PyBobLearnEMJFAMachineObject* self, PyObject* args, PyObject* kwds) {
-
-  /* Parses input arguments in a single shot */
-  char** kwlist = is_similar_to.kwlist(0);
-
-  //PyObject* other = 0;
-  PyBobLearnEMJFAMachineObject* other = 0;
-  double r_epsilon = 1.e-5;
-  double a_epsilon = 1.e-8;
-
-  if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|dd", kwlist,
-        &PyBobLearnEMJFAMachine_Type, &other,
-        &r_epsilon, &a_epsilon)){
-
-        is_similar_to.print_usage();
-        return 0;
-  }
-
-  if (self->cxx->is_similar_to(*other->cxx, r_epsilon, a_epsilon))
-    Py_RETURN_TRUE;
-  else
-    Py_RETURN_FALSE;
-}
-
-
-/*** estimate_x ***/
-static auto estimate_x = bob::extension::FunctionDoc(
-  "estimate_x",
-  "Estimates the session offset x (LPT assumption) given GMM statistics.",
-  "Estimates x from the GMM statistics considering the LPT assumption, that is the latent session variable x is approximated using the UBM",
-  true
-)
-.add_prototype("stats,input")
-.add_parameter("stats", ":py:class:`bob.learn.em.GMMStats`", "Statistics of the GMM")
-.add_parameter("input", "array_like <float, 1D>", "Input vector");
-static PyObject* PyBobLearnEMJFAMachine_estimateX(PyBobLearnEMJFAMachineObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  char** kwlist = estimate_x.kwlist(0);
-
-  PyBobLearnEMGMMStatsObject* stats = 0;
-  PyBlitzArrayObject* input           = 0;
-
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O&", kwlist, &PyBobLearnEMGMMStats_Type, &stats,
-                                                                 &PyBlitzArray_Converter,&input))
-    return 0;
-
-  //protects acquired resources through this scope
-  auto input_ = make_safe(input);
-
-  // perform check on the input
-  if (input->type_num != NPY_FLOAT64){
-    PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, estimate_x.name());
-    return 0;
-  }
-
-  if (input->ndim != 1){
-    PyErr_Format(PyExc_TypeError, "`%s' only processes 1D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, estimate_x.name());
-    return 0;
-  }
-
-  if (input->shape[0] != (Py_ssize_t)self->cxx->getNGaussians()) {
-    PyErr_Format(PyExc_TypeError, "`%s' 1D `input` array should have %" PY_FORMAT_SIZE_T "d, elements, not %" PY_FORMAT_SIZE_T "d for `%s`", Py_TYPE(self)->tp_name, self->cxx->getNInputs(), (Py_ssize_t)input->shape[0], estimate_x.name());
-    return 0;
-  }
-
-  self->cxx->estimateX(*stats->cxx, *PyBlitzArrayCxx_AsBlitz<double,1>(input));
-
-  BOB_CATCH_MEMBER("cannot estimate X", 0)
-  Py_RETURN_NONE;
-}
-
-
-/*** estimate_ux ***/
-static auto estimate_ux = bob::extension::FunctionDoc(
-  "estimate_ux",
-  "Estimates Ux (LPT assumption) given GMM statistics.",
-  "Estimates Ux from the GMM statistics considering the LPT assumption, that is the latent session variable x is approximated using the UBM.",
-  true
-)
-.add_prototype("stats,input")
-.add_parameter("stats", ":py:class:`bob.learn.em.GMMStats`", "Statistics of the GMM")
-.add_parameter("input", "array_like <float, 1D>", "Input vector");
-static PyObject* PyBobLearnEMJFAMachine_estimateUx(PyBobLearnEMJFAMachineObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  char** kwlist = estimate_ux.kwlist(0);
-
-  PyBobLearnEMGMMStatsObject* stats = 0;
-  PyBlitzArrayObject* input           = 0;
-
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O&", kwlist, &PyBobLearnEMGMMStats_Type, &stats,
-                                                                 &PyBlitzArray_Converter,&input))
-    return 0;
-
-  //protects acquired resources through this scope
-  auto input_ = make_safe(input);
-
-  // perform check on the input
-  if (input->type_num != NPY_FLOAT64){
-    PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, estimate_ux.name());
-    return 0;
-  }
-
-  if (input->ndim != 1){
-    PyErr_Format(PyExc_TypeError, "`%s' only processes 1D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, estimate_ux.name());
-    return 0;
-  }
-
-  if (input->shape[0] != (Py_ssize_t)self->cxx->getNGaussians()*(Py_ssize_t)self->cxx->getNInputs()) {
-    PyErr_Format(PyExc_TypeError, "`%s' 1D `input` array should have %" PY_FORMAT_SIZE_T "d, elements, not %" PY_FORMAT_SIZE_T "d for `%s`", Py_TYPE(self)->tp_name, self->cxx->getNInputs()*(Py_ssize_t)self->cxx->getNGaussians(), (Py_ssize_t)input->shape[0], estimate_ux.name());
-    return 0;
-  }
-
-  self->cxx->estimateUx(*stats->cxx, *PyBlitzArrayCxx_AsBlitz<double,1>(input));
-
-  BOB_CATCH_MEMBER("cannot estimate Ux", 0)
-  Py_RETURN_NONE;
-}
-
-
-/*** forward_ux ***/
-static auto forward_ux = bob::extension::FunctionDoc(
-  "forward_ux",
-  "Computes a score for the given UBM statistics and given the Ux vector",
-  "",
-  true
-)
-.add_prototype("stats,ux")
-.add_parameter("stats", ":py:class:`bob.learn.em.GMMStats`", "Statistics as input")
-.add_parameter("ux", "array_like <float, 1D>", "Input vector");
-static PyObject* PyBobLearnEMJFAMachine_ForwardUx(PyBobLearnEMJFAMachineObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  char** kwlist = forward_ux.kwlist(0);
-
-  PyBobLearnEMGMMStatsObject* stats = 0;
-  PyBlitzArrayObject* ux_input        = 0;
-
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O&", kwlist, &PyBobLearnEMGMMStats_Type, &stats,
-                                                                 &PyBlitzArray_Converter,&ux_input))
-    return 0;
-
-  //protects acquired resources through this scope
-  auto ux_input_ = make_safe(ux_input);
-
-  // perform check on the input
-  if (ux_input->type_num != NPY_FLOAT64){
-    PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, forward_ux.name());
-    return 0;
-  }
-
-  if (ux_input->ndim != 1){
-    PyErr_Format(PyExc_TypeError, "`%s' only processes 1D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, forward_ux.name());
-    return 0;
-  }
-
-  if (ux_input->shape[0] != (Py_ssize_t)self->cxx->getNGaussians()*(Py_ssize_t)self->cxx->getNInputs()) {
-    PyErr_Format(PyExc_TypeError, "`%s' 1D `input` array should have %" PY_FORMAT_SIZE_T "d, elements, not %" PY_FORMAT_SIZE_T "d for `%s`", Py_TYPE(self)->tp_name, (Py_ssize_t)self->cxx->getNGaussians()*(Py_ssize_t)self->cxx->getNInputs(), (Py_ssize_t)ux_input->shape[0], forward_ux.name());
-    return 0;
-  }
-
-  double score = self->cxx->forward(*stats->cxx, *PyBlitzArrayCxx_AsBlitz<double,1>(ux_input));
-
-  return Py_BuildValue("d", score);
-  BOB_CATCH_MEMBER("cannot forward_ux", 0)
-
-}
-
-
-/*** log_likelihood ***/
-static auto log_likelihood = bob::extension::FunctionDoc(
-  "log_likelihood",
-  "Computes the log-likelihood of the given samples",
-  ".. note:: the ``__call__`` function is an alias for this function.",
-  true
-)
-.add_prototype("stats")
-.add_parameter("stats", ":py:class:`bob.learn.em.GMMStats`", "Statistics as input");
-static PyObject* PyBobLearnEMJFAMachine_log_likelihood(PyBobLearnEMJFAMachineObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  char** kwlist = log_likelihood.kwlist(0);
-
-  PyBobLearnEMGMMStatsObject* stats = 0;
-
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", kwlist, &PyBobLearnEMGMMStats_Type, &stats))
-    return 0;
-
-  //protects acquired resources through this scope
-  double score = self->cxx->forward(*stats->cxx);
-
-  return Py_BuildValue("d", score);
-  BOB_CATCH_MEMBER("cannot log_likelihood", 0)
-
-}
-
-
-static PyMethodDef PyBobLearnEMJFAMachine_methods[] = {
-  {
-    save.name(),
-    (PyCFunction)PyBobLearnEMJFAMachine_Save,
-    METH_VARARGS|METH_KEYWORDS,
-    save.doc()
-  },
-  {
-    load.name(),
-    (PyCFunction)PyBobLearnEMJFAMachine_Load,
-    METH_VARARGS|METH_KEYWORDS,
-    load.doc()
-  },
-  {
-    is_similar_to.name(),
-    (PyCFunction)PyBobLearnEMJFAMachine_IsSimilarTo,
-    METH_VARARGS|METH_KEYWORDS,
-    is_similar_to.doc()
-  },
-
-  {
-    estimate_x.name(),
-    (PyCFunction)PyBobLearnEMJFAMachine_estimateX,
-    METH_VARARGS|METH_KEYWORDS,
-    estimate_x.doc()
-  },
-
-  {
-    estimate_ux.name(),
-    (PyCFunction)PyBobLearnEMJFAMachine_estimateUx,
-    METH_VARARGS|METH_KEYWORDS,
-    estimate_ux.doc()
-  },
-
-  {
-    forward_ux.name(),
-    (PyCFunction)PyBobLearnEMJFAMachine_ForwardUx,
-    METH_VARARGS|METH_KEYWORDS,
-    forward_ux.doc()
-  },
-  {
-    log_likelihood.name(),
-    (PyCFunction)PyBobLearnEMJFAMachine_log_likelihood,
-    METH_VARARGS|METH_KEYWORDS,
-    log_likelihood.doc()
-  },
-
-
-  {0} /* Sentinel */
-};
-
-
-/******************************************************************/
-/************ Module Section **************************************/
-/******************************************************************/
-
-// Define the JFA type struct; will be initialized later
-PyTypeObject PyBobLearnEMJFAMachine_Type = {
-  PyVarObject_HEAD_INIT(0,0)
-  0
-};
-
-bool init_BobLearnEMJFAMachine(PyObject* module)
-{
-  // initialize the type struct
-  PyBobLearnEMJFAMachine_Type.tp_name      = JFAMachine_doc.name();
-  PyBobLearnEMJFAMachine_Type.tp_basicsize = sizeof(PyBobLearnEMJFAMachineObject);
-  PyBobLearnEMJFAMachine_Type.tp_flags     = Py_TPFLAGS_DEFAULT;
-  PyBobLearnEMJFAMachine_Type.tp_doc       = JFAMachine_doc.doc();
-
-  // set the functions
-  PyBobLearnEMJFAMachine_Type.tp_new         = PyType_GenericNew;
-  PyBobLearnEMJFAMachine_Type.tp_init        = reinterpret_cast<initproc>(PyBobLearnEMJFAMachine_init);
-  PyBobLearnEMJFAMachine_Type.tp_dealloc     = reinterpret_cast<destructor>(PyBobLearnEMJFAMachine_delete);
-  PyBobLearnEMJFAMachine_Type.tp_richcompare = reinterpret_cast<richcmpfunc>(PyBobLearnEMJFAMachine_RichCompare);
-  PyBobLearnEMJFAMachine_Type.tp_methods     = PyBobLearnEMJFAMachine_methods;
-  PyBobLearnEMJFAMachine_Type.tp_getset      = PyBobLearnEMJFAMachine_getseters;
-  PyBobLearnEMJFAMachine_Type.tp_call        = reinterpret_cast<ternaryfunc>(PyBobLearnEMJFAMachine_log_likelihood);
-
-
-  // check that everything is fine
-  if (PyType_Ready(&PyBobLearnEMJFAMachine_Type) < 0) return false;
-
-  // add the type to the module
-  Py_INCREF(&PyBobLearnEMJFAMachine_Type);
-  return PyModule_AddObject(module, "JFAMachine", (PyObject*)&PyBobLearnEMJFAMachine_Type) >= 0;
-}
diff --git a/bob/learn/em/jfa_trainer.cpp b/bob/learn/em/jfa_trainer.cpp
deleted file mode 100644
index 5d9abadc96821d28cec456dc20b1ef49c5364376..0000000000000000000000000000000000000000
--- a/bob/learn/em/jfa_trainer.cpp
+++ /dev/null
@@ -1,1107 +0,0 @@
-/**
- * @author Tiago de Freitas Pereira <tiago.pereira@idiap.ch>
- * @date Sun 01 Fev 09:40:00 2015
- *
- * @brief Python API for bob::learn::em
- *
- * Copyright (C) 2011-2014 Idiap Research Institute, Martigny, Switzerland
- */
-
-#include "main.h"
-#include <boost/make_shared.hpp>
-
-/******************************************************************/
-/************ Constructor Section *********************************/
-/******************************************************************/
-
-static int extract_GMMStats_1d(PyObject *list,
-                             std::vector<boost::shared_ptr<bob::learn::em::GMMStats> >& training_data)
-{
-  for (int i=0; i<PyList_GET_SIZE(list); i++){
-
-    PyBobLearnEMGMMStatsObject* stats;
-    if (!PyArg_Parse(PyList_GetItem(list, i), "O!", &PyBobLearnEMGMMStats_Type, &stats)){
-      PyErr_Format(PyExc_RuntimeError, "Expected GMMStats objects");
-      return -1;
-    }
-    training_data.push_back(stats->cxx);
-  }
-  return 0;
-}
-
-static int extract_GMMStats_2d(PyObject *list,
-                             std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& training_data)
-{
-  for (int i=0; i<PyList_GET_SIZE(list); i++)
-  {
-    PyObject* another_list;
-    if(!PyArg_Parse(PyList_GetItem(list, i), "O!", &PyList_Type, &another_list)){
-      PyErr_Format(PyExc_RuntimeError, "Expected a list of lists of GMMStats objects [[GMMStats,GMMStats],[GMMStats,GMMStats].....[GMMStats,GMMStats]]");
-      return -1;
-    }
-
-    std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > another_training_data;
-    for (int j=0; j<PyList_GET_SIZE(another_list); j++){
-
-      PyBobLearnEMGMMStatsObject* stats;
-      if (!PyArg_Parse(PyList_GetItem(another_list, j), "O!", &PyBobLearnEMGMMStats_Type, &stats)){
-        PyErr_Format(PyExc_RuntimeError, "Expected GMMStats objects");
-        return -1;
-      }
-      another_training_data.push_back(stats->cxx);
-    }
-    training_data.push_back(another_training_data);
-  }
-  return 0;
-}
-
-template <int N>
-static PyObject* vector_as_list(const std::vector<blitz::Array<double,N> >& vec)
-{
-  PyObject* list = PyList_New(vec.size());
-  for(size_t i=0; i<vec.size(); i++){
-    blitz::Array<double,N> numpy_array = vec[i];
-    PyObject* numpy_py_object = PyBlitzArrayCxx_AsNumpy(numpy_array);
-    PyList_SET_ITEM(list, i, numpy_py_object);
-  }
-  return list;
-}
-
-template <int N>
-int list_as_vector(PyObject* list, std::vector<blitz::Array<double,N> >& vec)
-{
-  for (int i=0; i<PyList_GET_SIZE(list); i++)
-  {
-    PyBlitzArrayObject* blitz_object;
-    if (!PyArg_Parse(PyList_GetItem(list, i), "O&", &PyBlitzArray_Converter, &blitz_object)){
-      PyErr_Format(PyExc_RuntimeError, "Expected numpy array object");
-      return -1;
-    }
-    auto blitz_object_ = make_safe(blitz_object);
-    vec.push_back(*PyBlitzArrayCxx_AsBlitz<double,N>(blitz_object));
-  }
-  return 0;
-}
-
-
-
-static auto JFATrainer_doc = bob::extension::ClassDoc(
-  BOB_EXT_MODULE_PREFIX ".JFATrainer",
-  "Trains a Joint Factor Analysis (:ref:`JFA <jfa>`) on top of GMMs"
-  "",
-  ""
-).add_constructor(
-  bob::extension::FunctionDoc(
-    "__init__",
-    "Constructor. Builds a new JFATrainer",
-    "",
-    true
-  )
-  .add_prototype("other","")
-  .add_prototype("","")
-  .add_parameter("other", ":py:class:`bob.learn.em.JFATrainer`", "A JFATrainer object to be copied.")
-);
-
-
-static int PyBobLearnEMJFATrainer_init_copy(PyBobLearnEMJFATrainerObject* self, PyObject* args, PyObject* kwargs) {
-
-  char** kwlist = JFATrainer_doc.kwlist(0);
-  PyBobLearnEMJFATrainerObject* o;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", kwlist, &PyBobLearnEMJFATrainer_Type, &o)){
-    JFATrainer_doc.print_usage();
-    return -1;
-  }
-
-  self->cxx.reset(new bob::learn::em::JFATrainer(*o->cxx));
-  return 0;
-}
-
-
-static int PyBobLearnEMJFATrainer_init(PyBobLearnEMJFATrainerObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  // get the number of command line arguments
-  int nargs = (args?PyTuple_Size(args):0) + (kwargs?PyDict_Size(kwargs):0);
-
-  switch(nargs){
-    case 0:{
-      self->cxx.reset(new bob::learn::em::JFATrainer());
-      return 0;
-    }
-    case 1:{
-      // If the constructor input is JFATrainer object
-      return PyBobLearnEMJFATrainer_init_copy(self, args, kwargs);
-    }
-    default:{
-      PyErr_Format(PyExc_RuntimeError, "number of arguments mismatch - %s requires only 0 and 1 argument, but you provided %d (see help)", Py_TYPE(self)->tp_name, nargs);
-      JFATrainer_doc.print_usage();
-      return -1;
-    }
-  }
-  BOB_CATCH_MEMBER("cannot create JFATrainer", -1)
-  return 0;
-}
-
-
-static void PyBobLearnEMJFATrainer_delete(PyBobLearnEMJFATrainerObject* self) {
-  self->cxx.reset();
-  Py_TYPE(self)->tp_free((PyObject*)self);
-}
-
-
-int PyBobLearnEMJFATrainer_Check(PyObject* o) {
-  return PyObject_IsInstance(o, reinterpret_cast<PyObject*>(&PyBobLearnEMJFATrainer_Type));
-}
-
-
-static PyObject* PyBobLearnEMJFATrainer_RichCompare(PyBobLearnEMJFATrainerObject* self, PyObject* other, int op) {
-  BOB_TRY
-
-  if (!PyBobLearnEMJFATrainer_Check(other)) {
-    PyErr_Format(PyExc_TypeError, "cannot compare `%s' with `%s'", Py_TYPE(self)->tp_name, Py_TYPE(other)->tp_name);
-    return 0;
-  }
-  auto other_ = reinterpret_cast<PyBobLearnEMJFATrainerObject*>(other);
-  switch (op) {
-    case Py_EQ:
-      if (*self->cxx==*other_->cxx) Py_RETURN_TRUE; else Py_RETURN_FALSE;
-    case Py_NE:
-      if (*self->cxx==*other_->cxx) Py_RETURN_FALSE; else Py_RETURN_TRUE;
-    default:
-      Py_INCREF(Py_NotImplemented);
-      return Py_NotImplemented;
-  }
-  BOB_CATCH_MEMBER("cannot compare JFATrainer objects", 0)
-}
-
-
-/******************************************************************/
-/************ Variables Section ***********************************/
-/******************************************************************/
-
-static auto acc_v_a1 = bob::extension::VariableDoc(
-  "acc_v_a1",
-  "array_like <float, 3D>",
-  "Accumulator updated during the E-step",
-  ""
-);
-PyObject* PyBobLearnEMJFATrainer_get_acc_v_a1(PyBobLearnEMJFATrainerObject* self, void*){
-  BOB_TRY
-  return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getAccVA1());
-  BOB_CATCH_MEMBER("acc_v_a1 could not be read", 0)
-}
-int PyBobLearnEMJFATrainer_set_acc_v_a1(PyBobLearnEMJFATrainerObject* self, PyObject* value, void*){
-  BOB_TRY
-  PyBlitzArrayObject* input;
-  if (!PyBlitzArray_Converter(value, &input)){
-    PyErr_Format(PyExc_RuntimeError, "%s %s expects a 3D array of floats", Py_TYPE(self)->tp_name, acc_v_a1.name());
-    return -1;
-  }
-  auto o_ = make_safe(input);
-
-  // perform check on the input
-  if (input->type_num != NPY_FLOAT64){
-    PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, acc_v_a1.name());
-    return -1;
-  }
-
-  if (input->ndim != 3){
-    PyErr_Format(PyExc_TypeError, "`%s' only processes 3D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, acc_v_a1.name());
-    return -1;
-  }
-
-  if (input->shape[0] != (Py_ssize_t)self->cxx->getAccVA1().extent(0) && input->shape[1] != (Py_ssize_t)self->cxx->getAccVA1().extent(1) && input->shape[2] != (Py_ssize_t)self->cxx->getAccVA1().extent(2)) {
-    PyErr_Format(PyExc_TypeError, "`%s' 3D `input` array should have the shape [%" PY_FORMAT_SIZE_T "d, %" PY_FORMAT_SIZE_T "d, %" PY_FORMAT_SIZE_T "d] not [%" PY_FORMAT_SIZE_T "d, %" PY_FORMAT_SIZE_T "d, %" PY_FORMAT_SIZE_T "d] for `%s`", Py_TYPE(self)->tp_name, (Py_ssize_t)self->cxx->getAccVA1().extent(0), (Py_ssize_t)self->cxx->getAccVA1().extent(1), (Py_ssize_t)self->cxx->getAccVA1().extent(2), (Py_ssize_t)input->shape[0], (Py_ssize_t)input->shape[1], (Py_ssize_t)input->shape[2], acc_v_a1.name());
-    return -1;
-  }
-
-  auto b = PyBlitzArrayCxx_AsBlitz<double,3>(input, "acc_v_a1");
-  if (!b) return -1;
-  self->cxx->setAccVA1(*b);
-  return 0;
-  BOB_CATCH_MEMBER("acc_v_a1 could not be set", -1)
-}
-
-
-static auto acc_v_a2 = bob::extension::VariableDoc(
-  "acc_v_a2",
-  "array_like <float, 2D>",
-  "Accumulator updated during the E-step",
-  ""
-);
-PyObject* PyBobLearnEMJFATrainer_get_acc_v_a2(PyBobLearnEMJFATrainerObject* self, void*){
-  BOB_TRY
-  return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getAccVA2());
-  BOB_CATCH_MEMBER("acc_v_a2 could not be read", 0)
-}
-int PyBobLearnEMJFATrainer_set_acc_v_a2(PyBobLearnEMJFATrainerObject* self, PyObject* value, void*){
-  BOB_TRY
-  PyBlitzArrayObject* input;
-  if (!PyBlitzArray_Converter(value, &input)){
-    PyErr_Format(PyExc_RuntimeError, "%s %s expects a 2D array of floats", Py_TYPE(self)->tp_name, acc_v_a2.name());
-    return -1;
-  }
-  auto o_ = make_safe(input);
-
-  // perform check on the input
-  if (input->type_num != NPY_FLOAT64){
-    PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, acc_v_a2.name());
-    return -1;
-  }
-
-  if (input->ndim != 2){
-    PyErr_Format(PyExc_TypeError, "`%s' only processes 2D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, acc_v_a2.name());
-    return -1;
-  }
-
-  if (input->shape[0] != (Py_ssize_t)self->cxx->getAccVA2().extent(0) && input->shape[1] != (Py_ssize_t)self->cxx->getAccVA2().extent(1)) {
-    PyErr_Format(PyExc_TypeError, "`%s' 2D `input` array should have the shape [%" PY_FORMAT_SIZE_T "d, %" PY_FORMAT_SIZE_T "d] not [%" PY_FORMAT_SIZE_T "d, %" PY_FORMAT_SIZE_T "d] for `%s`", Py_TYPE(self)->tp_name, (Py_ssize_t)self->cxx->getAccVA2().extent(0), (Py_ssize_t)self->cxx->getAccVA2().extent(1), input->shape[0], input->shape[1], acc_v_a2.name());
-    return -1;
-  }
-
-
-  auto b = PyBlitzArrayCxx_AsBlitz<double,2>(input, "acc_v_a2");
-  if (!b) return -1;
-  self->cxx->setAccVA2(*b);
-  return 0;
-  BOB_CATCH_MEMBER("acc_v_a2 could not be set", -1)
-}
-
-
-static auto acc_u_a1 = bob::extension::VariableDoc(
-  "acc_u_a1",
-  "array_like <float, 3D>",
-  "Accumulator updated during the E-step",
-  ""
-);
-PyObject* PyBobLearnEMJFATrainer_get_acc_u_a1(PyBobLearnEMJFATrainerObject* self, void*){
-  BOB_TRY
-  return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getAccUA1());
-  BOB_CATCH_MEMBER("acc_u_a1 could not be read", 0)
-}
-int PyBobLearnEMJFATrainer_set_acc_u_a1(PyBobLearnEMJFATrainerObject* self, PyObject* value, void*){
-  BOB_TRY
-  PyBlitzArrayObject* input;
-  if (!PyBlitzArray_Converter(value, &input)){
-    PyErr_Format(PyExc_RuntimeError, "%s %s expects a 3D array of floats", Py_TYPE(self)->tp_name, acc_u_a1.name());
-    return -1;
-  }
-  auto o_ = make_safe(input);
-
-  // perform check on the input
-  if (input->type_num != NPY_FLOAT64){
-    PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, acc_u_a1.name());
-    return -1;
-  }
-
-  if (input->ndim != 3){
-    PyErr_Format(PyExc_TypeError, "`%s' only processes 3D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, acc_u_a1.name());
-    return -1;
-  }
-
-  if (input->shape[0] != (Py_ssize_t)self->cxx->getAccUA1().extent(0) && input->shape[1] != (Py_ssize_t)self->cxx->getAccUA1().extent(1) && input->shape[2] != (Py_ssize_t)self->cxx->getAccUA1().extent(2)) {
-    PyErr_Format(PyExc_TypeError, "`%s' 3D `input` array should have the shape [%" PY_FORMAT_SIZE_T "d, %" PY_FORMAT_SIZE_T "d, %" PY_FORMAT_SIZE_T "d] not [%" PY_FORMAT_SIZE_T "d, %" PY_FORMAT_SIZE_T "d, %" PY_FORMAT_SIZE_T "d] for `%s`", Py_TYPE(self)->tp_name, (Py_ssize_t)self->cxx->getAccUA1().extent(0), (Py_ssize_t)self->cxx->getAccUA1().extent(1), (Py_ssize_t)self->cxx->getAccUA1().extent(2), (Py_ssize_t)input->shape[0], (Py_ssize_t)input->shape[1], (Py_ssize_t)input->shape[2], acc_u_a1.name());
-    return -1;
-  }
-
-  auto b = PyBlitzArrayCxx_AsBlitz<double,3>(input, "acc_u_a1");
-  if (!b) return -1;
-  self->cxx->setAccUA1(*b);
-  return 0;
-  BOB_CATCH_MEMBER("acc_u_a1 could not be set", -1)
-}
-
-
-static auto acc_u_a2 = bob::extension::VariableDoc(
-  "acc_u_a2",
-  "array_like <float, 2D>",
-  "Accumulator updated during the E-step",
-  ""
-);
-PyObject* PyBobLearnEMJFATrainer_get_acc_u_a2(PyBobLearnEMJFATrainerObject* self, void*){
-  BOB_TRY
-  return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getAccUA2());
-  BOB_CATCH_MEMBER("acc_u_a2 could not be read", 0)
-}
-int PyBobLearnEMJFATrainer_set_acc_u_a2(PyBobLearnEMJFATrainerObject* self, PyObject* value, void*){
-  BOB_TRY
-  PyBlitzArrayObject* input;
-  if (!PyBlitzArray_Converter(value, &input)){
-    PyErr_Format(PyExc_RuntimeError, "%s %s expects a 2D array of floats", Py_TYPE(self)->tp_name, acc_u_a2.name());
-    return -1;
-  }
-  auto o_ = make_safe(input);
-
-  // perform check on the input
-  if (input->type_num != NPY_FLOAT64){
-    PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, acc_u_a2.name());
-    return -1;
-  }
-
-  if (input->ndim != 2){
-    PyErr_Format(PyExc_TypeError, "`%s' only processes 2D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, acc_u_a2.name());
-    return -1;
-  }
-
-  if (input->shape[0] != (Py_ssize_t)self->cxx->getAccUA2().extent(0) && input->shape[1] != (Py_ssize_t)self->cxx->getAccUA2().extent(1)) {
-    PyErr_Format(PyExc_TypeError, "`%s' 3D `input` array should have the shape [%" PY_FORMAT_SIZE_T "d, %" PY_FORMAT_SIZE_T "d] not [%" PY_FORMAT_SIZE_T "d, %" PY_FORMAT_SIZE_T "d] for `%s`", Py_TYPE(self)->tp_name, (Py_ssize_t)self->cxx->getAccUA2().extent(0), (Py_ssize_t)self->cxx->getAccUA2().extent(1), input->shape[0], input->shape[1], acc_u_a2.name());
-    return -1;
-  }
-
-  auto b = PyBlitzArrayCxx_AsBlitz<double,2>(input, "acc_u_a2");
-  if (!b) return -1;
-  self->cxx->setAccUA2(*b);
-  return 0;
-  BOB_CATCH_MEMBER("acc_u_a2 could not be set", -1)
-}
-
-
-static auto acc_d_a1 = bob::extension::VariableDoc(
-  "acc_d_a1",
-  "array_like <float, 1D>",
-  "Accumulator updated during the E-step",
-  ""
-);
-PyObject* PyBobLearnEMJFATrainer_get_acc_d_a1(PyBobLearnEMJFATrainerObject* self, void*){
-  BOB_TRY
-  return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getAccDA1());
-  BOB_CATCH_MEMBER("acc_d_a1 could not be read", 0)
-}
-int PyBobLearnEMJFATrainer_set_acc_d_a1(PyBobLearnEMJFATrainerObject* self, PyObject* value, void*){
-  BOB_TRY
-  PyBlitzArrayObject* input;
-  if (!PyBlitzArray_Converter(value, &input)){
-    PyErr_Format(PyExc_RuntimeError, "%s %s expects a 1D array of floats", Py_TYPE(self)->tp_name, acc_d_a1.name());
-    return -1;
-  }
-  auto o_ = make_safe(input);
-
-  // perform check on the input
-  if (input->type_num != NPY_FLOAT64){
-    PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, acc_d_a1.name());
-    return -1;
-  }
-
-  if (input->ndim != 1){
-    PyErr_Format(PyExc_TypeError, "`%s' only processes 1D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, acc_d_a1.name());
-    return -1;
-  }
-
-  if (input->shape[0] != (Py_ssize_t)self->cxx->getAccDA1().extent(0)) {
-    PyErr_Format(PyExc_TypeError, "`%s' 1D `input` array should have %" PY_FORMAT_SIZE_T "d elements, not %" PY_FORMAT_SIZE_T "d for `%s`", Py_TYPE(self)->tp_name, (Py_ssize_t)self->cxx->getAccDA1().extent(0), input->shape[0], acc_d_a1.name());
-    return -1;
-  }
-
-
-  auto b = PyBlitzArrayCxx_AsBlitz<double,1>(input, "acc_d_a1");
-  if (!b) return -1;
-  self->cxx->setAccDA1(*b);
-  return 0;
-  BOB_CATCH_MEMBER("acc_d_a1 could not be set", -1)
-}
-
-
-static auto acc_d_a2 = bob::extension::VariableDoc(
-  "acc_d_a2",
-  "array_like <float, 1D>",
-  "Accumulator updated during the E-step",
-  ""
-);
-PyObject* PyBobLearnEMJFATrainer_get_acc_d_a2(PyBobLearnEMJFATrainerObject* self, void*){
-  BOB_TRY
-  return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getAccDA2());
-  BOB_CATCH_MEMBER("acc_d_a2 could not be read", 0)
-}
-int PyBobLearnEMJFATrainer_set_acc_d_a2(PyBobLearnEMJFATrainerObject* self, PyObject* value, void*){
-  BOB_TRY
-  PyBlitzArrayObject* input;
-  if (!PyBlitzArray_Converter(value, &input)){
-    PyErr_Format(PyExc_RuntimeError, "%s %s expects a 1D array of floats", Py_TYPE(self)->tp_name, acc_d_a1.name());
-    return -1;
-  }
-  auto o_ = make_safe(input);
-
-  // perform check on the input
-  if (input->type_num != NPY_FLOAT64){
-    PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, acc_d_a2.name());
-    return -1;
-  }
-
-  if (input->ndim != 1){
-    PyErr_Format(PyExc_TypeError, "`%s' only processes 1D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, acc_d_a2.name());
-    return -1;
-  }
-
-  if (input->shape[0] != (Py_ssize_t)self->cxx->getAccDA2().extent(0)) {
-    PyErr_Format(PyExc_TypeError, "`%s' 1D `input` array should have %" PY_FORMAT_SIZE_T "d elements, not %" PY_FORMAT_SIZE_T "d for `%s`", Py_TYPE(self)->tp_name, (Py_ssize_t)self->cxx->getAccDA2().extent(0), input->shape[0], acc_d_a2.name());
-    return -1;
-  }
-
-  auto b = PyBlitzArrayCxx_AsBlitz<double,1>(input, "acc_d_a2");
-  if (!b) return -1;
-  self->cxx->setAccDA2(*b);
-  return 0;
-  BOB_CATCH_MEMBER("acc_d_a2 could not be set", -1)
-}
-
-
-static auto __X__ = bob::extension::VariableDoc(
-  "__X__",
-  "list",
-  "",
-  ""
-);
-PyObject* PyBobLearnEMJFATrainer_get_X(PyBobLearnEMJFATrainerObject* self, void*){
-  BOB_TRY
-  return vector_as_list(self->cxx->getX());
-  BOB_CATCH_MEMBER("__X__ could not be read", 0)
-}
-int PyBobLearnEMJFATrainer_set_X(PyBobLearnEMJFATrainerObject* self, PyObject* value, void*){
-  BOB_TRY
-
-  // Parses input arguments in a single shot
-  if (!PyList_Check(value)){
-    PyErr_Format(PyExc_TypeError, "Expected a list in `%s'", __X__.name());
-    return -1;
-  }
-
-  std::vector<blitz::Array<double,2> > data;
-  if(list_as_vector(value ,data)==0){
-    self->cxx->setX(data);
-  }
-
-  return 0;
-  BOB_CATCH_MEMBER("__X__ could not be written", -1)
-}
-
-
-
-static auto __Y__ = bob::extension::VariableDoc(
-  "__Y__",
-  "list",
-  "",
-  ""
-);
-PyObject* PyBobLearnEMJFATrainer_get_Y(PyBobLearnEMJFATrainerObject* self, void*){
-  BOB_TRY
-  return vector_as_list(self->cxx->getY());
-  BOB_CATCH_MEMBER("__Y__ could not be read", 0)
-}
-int PyBobLearnEMJFATrainer_set_Y(PyBobLearnEMJFATrainerObject* self, PyObject* value, void*){
-  BOB_TRY
-
-  // Parses input arguments in a single shot
-  if (!PyList_Check(value)){
-    PyErr_Format(PyExc_TypeError, "Expected a list in `%s'", __Y__.name());
-    return -1;
-  }
-
-  std::vector<blitz::Array<double,1> > data;
-  if(list_as_vector(value ,data)==0){
-    self->cxx->setY(data);
-  }
-
-  return 0;
-  BOB_CATCH_MEMBER("__Y__ could not be written", -1)
-}
-
-
-
-static auto __Z__ = bob::extension::VariableDoc(
-  "__Z__",
-  "list",
-  "",
-  ""
-);
-PyObject* PyBobLearnEMJFATrainer_get_Z(PyBobLearnEMJFATrainerObject* self, void*){
-  BOB_TRY
-  return vector_as_list(self->cxx->getZ());
-  BOB_CATCH_MEMBER("__Z__ could not be read", 0)
-}
-int PyBobLearnEMJFATrainer_set_Z(PyBobLearnEMJFATrainerObject* self, PyObject* value, void*){
-  BOB_TRY
-
-  // Parses input arguments in a single shot
-  if (!PyList_Check(value)){
-    PyErr_Format(PyExc_TypeError, "Expected a list in `%s'", __Z__.name());
-    return -1;
-  }
-
-  std::vector<blitz::Array<double,1> > data;
-  if(list_as_vector(value ,data)==0){
-    self->cxx->setZ(data);
-  }
-
-  return 0;
-  BOB_CATCH_MEMBER("__Z__ could not be written", -1)
-}
-
-
-
-static PyGetSetDef PyBobLearnEMJFATrainer_getseters[] = {
-  {
-   acc_v_a1.name(),
-   (getter)PyBobLearnEMJFATrainer_get_acc_v_a1,
-   (setter)PyBobLearnEMJFATrainer_set_acc_v_a1,
-   acc_v_a1.doc(),
-   0
-  },
-  {
-   acc_v_a2.name(),
-   (getter)PyBobLearnEMJFATrainer_get_acc_v_a2,
-   (setter)PyBobLearnEMJFATrainer_set_acc_v_a2,
-   acc_v_a2.doc(),
-   0
-  },
-  {
-   acc_u_a1.name(),
-   (getter)PyBobLearnEMJFATrainer_get_acc_u_a1,
-   (setter)PyBobLearnEMJFATrainer_set_acc_u_a1,
-   acc_u_a1.doc(),
-   0
-  },
-  {
-   acc_u_a2.name(),
-   (getter)PyBobLearnEMJFATrainer_get_acc_u_a2,
-   (setter)PyBobLearnEMJFATrainer_set_acc_u_a2,
-   acc_u_a2.doc(),
-   0
-  },
-  {
-   acc_d_a1.name(),
-   (getter)PyBobLearnEMJFATrainer_get_acc_d_a1,
-   (setter)PyBobLearnEMJFATrainer_set_acc_d_a1,
-   acc_d_a1.doc(),
-   0
-  },
-  {
-   acc_d_a2.name(),
-   (getter)PyBobLearnEMJFATrainer_get_acc_d_a2,
-   (setter)PyBobLearnEMJFATrainer_set_acc_d_a2,
-   acc_d_a2.doc(),
-   0
-  },
-  {
-   __X__.name(),
-   (getter)PyBobLearnEMJFATrainer_get_X,
-   (setter)PyBobLearnEMJFATrainer_set_X,
-   __X__.doc(),
-   0
-  },
-  {
-   __Y__.name(),
-   (getter)PyBobLearnEMJFATrainer_get_Y,
-   (setter)PyBobLearnEMJFATrainer_set_Y,
-   __Y__.doc(),
-   0
-  },
-  {
-   __Z__.name(),
-   (getter)PyBobLearnEMJFATrainer_get_Z,
-   (setter)PyBobLearnEMJFATrainer_set_Z,
-   __Z__.doc(),
-   0
-  },
-
-
-
-  {0}  // Sentinel
-};
-
-
-/******************************************************************/
-/************ Functions Section ***********************************/
-/******************************************************************/
-
-/*** initialize ***/
-static auto initialize = bob::extension::FunctionDoc(
-  "initialize",
-  "Initialization before the EM steps",
-  "",
-  true
-)
-.add_prototype("jfa_base, stats, [rng]")
-.add_parameter("jfa_base", ":py:class:`bob.learn.em.JFABase`", "JFABase Object")
-.add_parameter("stats", ":py:class:`bob.learn.em.GMMStats`", "GMMStats Object")
-.add_parameter("rng", ":py:class:`bob.core.random.mt19937`", "The Mersenne Twister mt19937 random generator used for the initialization of subspaces/arrays before the EM loop.");
-static PyObject* PyBobLearnEMJFATrainer_initialize(PyBobLearnEMJFATrainerObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  /* Parses input arguments in a single shot */
-  char** kwlist = initialize.kwlist(0);
-
-  PyBobLearnEMJFABaseObject* jfa_base = 0;
-  PyObject* stats = 0;
-  PyBoostMt19937Object* rng = 0;
-
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O!|O!", kwlist, &PyBobLearnEMJFABase_Type, &jfa_base,
-                                                                 &PyList_Type, &stats,
-                                                                 &PyBoostMt19937_Type, &rng)) return 0;
-
-  if(rng){
-    self->cxx->setRng(rng->rng);
-  }
-
-  std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > > training_data;
-  if(extract_GMMStats_2d(stats ,training_data)==0)
-    self->cxx->initialize(*jfa_base->cxx, training_data);
-  else
-    return 0;
-
-  BOB_CATCH_MEMBER("cannot perform the initialize method", 0)
-
-  Py_RETURN_NONE;
-}
-
-
-/*** e_stepv ***/
-static auto e_step_v = bob::extension::FunctionDoc(
-  "e_step_v",
-  "Call the 1st e-step procedure (for the V subspace).",
-  "",
-  true
-)
-.add_prototype("jfa_base,stats")
-.add_parameter("jfa_base", ":py:class:`bob.learn.em.JFABase`", "JFABase Object")
-.add_parameter("stats", ":py:class:`bob.learn.em.GMMStats`", "GMMStats Object");
-static PyObject* PyBobLearnEMJFATrainer_e_step_v(PyBobLearnEMJFATrainerObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  //Parses input arguments in a single shot
-  char** kwlist = e_step_v.kwlist(0);
-
-  PyBobLearnEMJFABaseObject* jfa_base = 0;
-  PyObject* stats = 0;
-
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O!", kwlist, &PyBobLearnEMJFABase_Type, &jfa_base,
-                                                                 &PyList_Type, &stats)) return 0;
-
-  std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > > training_data;
-  if(extract_GMMStats_2d(stats ,training_data)==0)
-    self->cxx->eStep1(*jfa_base->cxx, training_data);
-  else
-    return 0;
-
-
-  BOB_CATCH_MEMBER("cannot perform the e_step_v method", 0)
-
-  Py_RETURN_NONE;
-}
-
-
-/*** m_step_v ***/
-static auto m_step_v = bob::extension::FunctionDoc(
-  "m_step_v",
-  "Call the 1st m-step procedure (for the V subspace).",
-  "",
-  true
-)
-.add_prototype("jfa_base,stats")
-.add_parameter("jfa_base", ":py:class:`bob.learn.em.JFABase`", "JFABase Object")
-.add_parameter("stats", ":py:class:`bob.learn.em.GMMStats`", "GMMStats Object");
-static PyObject* PyBobLearnEMJFATrainer_m_step_v(PyBobLearnEMJFATrainerObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  // Parses input arguments in a single shot
-  char** kwlist = m_step_v.kwlist(0);
-
-  PyBobLearnEMJFABaseObject* jfa_base = 0;
-  PyObject* stats = 0;
-
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O!", kwlist, &PyBobLearnEMJFABase_Type, &jfa_base,
-                                                                 &PyList_Type, &stats)) return 0;
-
-  std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > > training_data;
-  if(extract_GMMStats_2d(stats ,training_data)==0)
-    self->cxx->mStep1(*jfa_base->cxx, training_data);
-  else
-    return 0;
-
-  BOB_CATCH_MEMBER("cannot perform the m_step_v method", 0)
-
-  Py_RETURN_NONE;
-}
-
-
-/*** finalize_v ***/
-static auto finalize_v = bob::extension::FunctionDoc(
-  "finalize_v",
-  "Call the 1st finalize procedure (for the V subspace).",
-  "",
-  true
-)
-.add_prototype("jfa_base,stats")
-.add_parameter("jfa_base", ":py:class:`bob.learn.em.JFABase`", "JFABase Object")
-.add_parameter("stats", ":py:class:`bob.learn.em.GMMStats`", "GMMStats Object");
-static PyObject* PyBobLearnEMJFATrainer_finalize_v(PyBobLearnEMJFATrainerObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  //Parses input arguments in a single shot
-  char** kwlist = finalize_v.kwlist(0);
-
-  PyBobLearnEMJFABaseObject* jfa_base = 0;
-  PyObject* stats = 0;
-
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O!", kwlist, &PyBobLearnEMJFABase_Type, &jfa_base,
-                                                                 &PyList_Type, &stats)) return 0;
-
-  std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > > training_data;
-  if(extract_GMMStats_2d(stats ,training_data)==0)
-    self->cxx->finalize1(*jfa_base->cxx, training_data);
-  else
-    return 0;
-
-  BOB_CATCH_MEMBER("cannot perform the finalize_v method", 0)
-
-  Py_RETURN_NONE;
-}
-
-
-/*** e_step_u ***/
-static auto e_step_u = bob::extension::FunctionDoc(
-  "e_step_u",
-  "Call the 2nd e-step procedure (for the U subspace).",
-  "",
-  true
-)
-.add_prototype("jfa_base,stats")
-.add_parameter("jfa_base", ":py:class:`bob.learn.em.JFABase`", "JFABase Object")
-.add_parameter("stats", ":py:class:`bob.learn.em.GMMStats`", "GMMStats Object");
-static PyObject* PyBobLearnEMJFATrainer_e_step_u(PyBobLearnEMJFATrainerObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  // Parses input arguments in a single shot
-  char** kwlist = e_step_u.kwlist(0);
-
-  PyBobLearnEMJFABaseObject* jfa_base = 0;
-  PyObject* stats = 0;
-
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O!", kwlist, &PyBobLearnEMJFABase_Type, &jfa_base,
-                                                                 &PyList_Type, &stats)) return 0;
-
-  std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > > training_data;
-  if(extract_GMMStats_2d(stats ,training_data)==0)
-    self->cxx->eStep2(*jfa_base->cxx, training_data);
-  else
-    return 0;
-
-  BOB_CATCH_MEMBER("cannot perform the e_step_u method", 0)
-
-  Py_RETURN_NONE;
-}
-
-
-/*** m_step_u ***/
-static auto m_step_u = bob::extension::FunctionDoc(
-  "m_step_u",
-  "Call the 2nd m-step procedure (for the U subspace).",
-  "",
-  true
-)
-.add_prototype("jfa_base,stats")
-.add_parameter("jfa_base", ":py:class:`bob.learn.em.JFABase`", "JFABase Object")
-.add_parameter("stats", ":py:class:`bob.learn.em.GMMStats`", "GMMStats Object");
-static PyObject* PyBobLearnEMJFATrainer_m_step_u(PyBobLearnEMJFATrainerObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  // Parses input arguments in a single shot
-  char** kwlist = m_step_u.kwlist(0);
-
-  PyBobLearnEMJFABaseObject* jfa_base = 0;
-  PyObject* stats = 0;
-
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O!", kwlist, &PyBobLearnEMJFABase_Type, &jfa_base,
-                                                                 &PyList_Type, &stats)) return 0;
-
-  std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > > training_data;
-  if(extract_GMMStats_2d(stats ,training_data)==0)
-    self->cxx->mStep2(*jfa_base->cxx, training_data);
-  else
-    return 0;
-
-  BOB_CATCH_MEMBER("cannot perform the m_step_u method", 0)
-
-  Py_RETURN_NONE;
-}
-
-
-/*** finalize_u ***/
-static auto finalize_u = bob::extension::FunctionDoc(
-  "finalize_u",
-  "Call the 2nd finalize procedure (for the U subspace).",
-  "",
-  true
-)
-.add_prototype("jfa_base,stats")
-.add_parameter("jfa_base", ":py:class:`bob.learn.em.JFABase`", "JFABase Object")
-.add_parameter("stats", ":py:class:`bob.learn.em.GMMStats`", "GMMStats Object");
-static PyObject* PyBobLearnEMJFATrainer_finalize_u(PyBobLearnEMJFATrainerObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  // Parses input arguments in a single shot
-  char** kwlist = finalize_u.kwlist(0);
-
-  PyBobLearnEMJFABaseObject* jfa_base = 0;
-  PyObject* stats = 0;
-
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O!", kwlist, &PyBobLearnEMJFABase_Type, &jfa_base,
-                                                                 &PyList_Type, &stats)) return 0;
-
-  std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > > training_data;
-  if(extract_GMMStats_2d(stats ,training_data)==0)
-    self->cxx->finalize2(*jfa_base->cxx, training_data);
-  else
-    return 0;
-
-  BOB_CATCH_MEMBER("cannot perform the finalize_u method", 0)
-
-  Py_RETURN_NONE;
-}
-
-
-/*** e_step_d ***/
-static auto e_step_d = bob::extension::FunctionDoc(
-  "e_step_d",
-  "Call the 3rd e-step procedure (for the d subspace).",
-  "",
-  true
-)
-.add_prototype("jfa_base,stats")
-.add_parameter("jfa_base", ":py:class:`bob.learn.em.JFABase`", "JFABase Object")
-.add_parameter("stats", ":py:class:`bob.learn.em.GMMStats`", "GMMStats Object");
-static PyObject* PyBobLearnEMJFATrainer_e_step_d(PyBobLearnEMJFATrainerObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  // Parses input arguments in a single shot
-  char** kwlist = e_step_d.kwlist(0);
-
-  PyBobLearnEMJFABaseObject* jfa_base = 0;
-  PyObject* stats = 0;
-
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O!", kwlist, &PyBobLearnEMJFABase_Type, &jfa_base,
-                                                                 &PyList_Type, &stats)) return 0;
-
-  std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > > training_data;
-  if(extract_GMMStats_2d(stats ,training_data)==0)
-    self->cxx->eStep3(*jfa_base->cxx, training_data);
-  else
-    return 0;
-
-  BOB_CATCH_MEMBER("cannot perform the e_step_d method", 0)
-
-  Py_RETURN_NONE;
-}
-
-
-/*** m_step_d ***/
-static auto m_step_d = bob::extension::FunctionDoc(
-  "m_step_d",
-  "Call the 3rd m-step procedure (for the d subspace).",
-  "",
-  true
-)
-.add_prototype("jfa_base,stats")
-.add_parameter("jfa_base", ":py:class:`bob.learn.em.JFABase`", "JFABase Object")
-.add_parameter("stats", ":py:class:`bob.learn.em.GMMStats`", "GMMStats Object");
-static PyObject* PyBobLearnEMJFATrainer_m_step_d(PyBobLearnEMJFATrainerObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  // Parses input arguments in a single shot
-  char** kwlist = m_step_d.kwlist(0);
-
-  PyBobLearnEMJFABaseObject* jfa_base = 0;
-  PyObject* stats = 0;
-
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O!", kwlist, &PyBobLearnEMJFABase_Type, &jfa_base,
-                                                                 &PyList_Type, &stats)) return 0;
-
-  std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > > training_data;
-  if(extract_GMMStats_2d(stats ,training_data)==0)
-    self->cxx->mStep3(*jfa_base->cxx, training_data);
-  else
-    return 0;
-
-  BOB_CATCH_MEMBER("cannot perform the m_step_d method", 0)
-
-  Py_RETURN_NONE;
-}
-
-
-/*** finalize_d ***/
-static auto finalize_d = bob::extension::FunctionDoc(
-  "finalize_d",
-  "Call the 3rd finalize procedure (for the d subspace).",
-  "",
-  true
-)
-.add_prototype("jfa_base,stats")
-.add_parameter("jfa_base", ":py:class:`bob.learn.em.JFABase`", "JFABase Object")
-.add_parameter("stats", ":py:class:`bob.learn.em.GMMStats`", "GMMStats Object");
-static PyObject* PyBobLearnEMJFATrainer_finalize_d(PyBobLearnEMJFATrainerObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  // Parses input arguments in a single shot
-  char** kwlist = finalize_d.kwlist(0);
-
-  PyBobLearnEMJFABaseObject* jfa_base = 0;
-  PyObject* stats = 0;
-
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O!", kwlist, &PyBobLearnEMJFABase_Type, &jfa_base,
-                                                                 &PyList_Type, &stats)) return 0;
-
-  std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > > training_data;
-  if(extract_GMMStats_2d(stats ,training_data)==0)
-    self->cxx->finalize3(*jfa_base->cxx, training_data);
-  else
-    return 0;
-
-  BOB_CATCH_MEMBER("cannot perform the finalize_d method", 0)
-
-  Py_RETURN_NONE;
-}
-
-
-/*** enroll ***/
-static auto enroll = bob::extension::FunctionDoc(
-  "enroll",
-  "",
-  "",
-  true
-)
-.add_prototype("jfa_machine,features,n_iter")
-.add_parameter("jfa_machine", ":py:class:`bob.learn.em.JFAMachine`", "JFAMachine Object")
-.add_parameter("features", "[:py:class:`bob.learn.em.GMMStats`]", "")
-.add_parameter("n_iter", "int", "Number of iterations");
-static PyObject* PyBobLearnEMJFATrainer_enroll(PyBobLearnEMJFATrainerObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  // Parses input arguments in a single shot
-  char** kwlist = enroll.kwlist(0);
-
-  PyBobLearnEMJFAMachineObject* jfa_machine = 0;
-  PyObject* stats = 0;
-  int n_iter = 1;
-
-
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O!i", kwlist, &PyBobLearnEMJFAMachine_Type, &jfa_machine,
-                                                                  &PyList_Type, &stats, &n_iter)) return 0;
-
-  std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > training_data;
-  if(extract_GMMStats_1d(stats ,training_data)==0)
-    self->cxx->enroll(*jfa_machine->cxx, training_data, n_iter);
-  else
-    return 0;
-
-  BOB_CATCH_MEMBER("cannot perform the enroll method", 0)
-
-  Py_RETURN_NONE;
-}
-
-
-
-static PyMethodDef PyBobLearnEMJFATrainer_methods[] = {
-  {
-    initialize.name(),
-    (PyCFunction)PyBobLearnEMJFATrainer_initialize,
-    METH_VARARGS|METH_KEYWORDS,
-    initialize.doc()
-  },
-  {
-    e_step_v.name(),
-    (PyCFunction)PyBobLearnEMJFATrainer_e_step_v,
-    METH_VARARGS|METH_KEYWORDS,
-    e_step_v.doc()
-  },
-  {
-    e_step_u.name(),
-    (PyCFunction)PyBobLearnEMJFATrainer_e_step_u,
-    METH_VARARGS|METH_KEYWORDS,
-    e_step_u.doc()
-  },
-  {
-    e_step_d.name(),
-    (PyCFunction)PyBobLearnEMJFATrainer_e_step_d,
-    METH_VARARGS|METH_KEYWORDS,
-    e_step_d.doc()
-  },
-  {
-    m_step_v.name(),
-    (PyCFunction)PyBobLearnEMJFATrainer_m_step_v,
-    METH_VARARGS|METH_KEYWORDS,
-    m_step_v.doc()
-  },
-  {
-    m_step_u.name(),
-    (PyCFunction)PyBobLearnEMJFATrainer_m_step_u,
-    METH_VARARGS|METH_KEYWORDS,
-    m_step_u.doc()
-  },
-  {
-    m_step_d.name(),
-    (PyCFunction)PyBobLearnEMJFATrainer_m_step_d,
-    METH_VARARGS|METH_KEYWORDS,
-    m_step_d.doc()
-  },
-  {
-    finalize_v.name(),
-    (PyCFunction)PyBobLearnEMJFATrainer_finalize_v,
-    METH_VARARGS|METH_KEYWORDS,
-    finalize_v.doc()
-  },
-  {
-    finalize_u.name(),
-    (PyCFunction)PyBobLearnEMJFATrainer_finalize_u,
-    METH_VARARGS|METH_KEYWORDS,
-    finalize_u.doc()
-  },
-  {
-    finalize_d.name(),
-    (PyCFunction)PyBobLearnEMJFATrainer_finalize_d,
-    METH_VARARGS|METH_KEYWORDS,
-    finalize_d.doc()
-  },
-  {
-    enroll.name(),
-    (PyCFunction)PyBobLearnEMJFATrainer_enroll,
-    METH_VARARGS|METH_KEYWORDS,
-    enroll.doc()
-  },
-  {0} /* Sentinel */
-};
-
-
-/******************************************************************/
-/************ Module Section **************************************/
-/******************************************************************/
-
-// Define the Gaussian type struct; will be initialized later
-PyTypeObject PyBobLearnEMJFATrainer_Type = {
-  PyVarObject_HEAD_INIT(0,0)
-  0
-};
-
-bool init_BobLearnEMJFATrainer(PyObject* module)
-{
-  // initialize the type JFATrainer
-  PyBobLearnEMJFATrainer_Type.tp_name      = JFATrainer_doc.name();
-  PyBobLearnEMJFATrainer_Type.tp_basicsize = sizeof(PyBobLearnEMJFATrainerObject);
-  PyBobLearnEMJFATrainer_Type.tp_flags     = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE;//Enable the class inheritance;
-  PyBobLearnEMJFATrainer_Type.tp_doc       = JFATrainer_doc.doc();
-
-  // set the functions
-  PyBobLearnEMJFATrainer_Type.tp_new          = PyType_GenericNew;
-  PyBobLearnEMJFATrainer_Type.tp_init         = reinterpret_cast<initproc>(PyBobLearnEMJFATrainer_init);
-  PyBobLearnEMJFATrainer_Type.tp_dealloc      = reinterpret_cast<destructor>(PyBobLearnEMJFATrainer_delete);
-  PyBobLearnEMJFATrainer_Type.tp_richcompare = reinterpret_cast<richcmpfunc>(PyBobLearnEMJFATrainer_RichCompare);
-  PyBobLearnEMJFATrainer_Type.tp_methods      = PyBobLearnEMJFATrainer_methods;
-  PyBobLearnEMJFATrainer_Type.tp_getset       = PyBobLearnEMJFATrainer_getseters;
-  //PyBobLearnEMJFATrainer_Type.tp_call         = reinterpret_cast<ternaryfunc>(PyBobLearnEMJFATrainer_compute_likelihood);
-
-
-  // check that everything is fine
-  if (PyType_Ready(&PyBobLearnEMJFATrainer_Type) < 0) return false;
-
-  // add the type to the module
-  Py_INCREF(&PyBobLearnEMJFATrainer_Type);
-  return PyModule_AddObject(module, "JFATrainer", (PyObject*)&PyBobLearnEMJFATrainer_Type) >= 0;
-}
diff --git a/bob/learn/em/kmeans_machine.cpp b/bob/learn/em/kmeans_machine.cpp
deleted file mode 100644
index 9fc87269c50f4c89aa9289c66988d7404568431a..0000000000000000000000000000000000000000
--- a/bob/learn/em/kmeans_machine.cpp
+++ /dev/null
@@ -1,865 +0,0 @@
-/**
- * @author Tiago de Freitas Pereira <tiago.pereira@idiap.ch>
- * @date Fri 26 Dec 16:18:00 2014
- *
- * @brief Python API for bob::learn::em
- *
- * Copyright (C) 2011-2014 Idiap Research Institute, Martigny, Switzerland
- */
-
-#include "main.h"
-
-/******************************************************************/
-/************ Constructor Section *********************************/
-/******************************************************************/
-
-static auto KMeansMachine_doc = bob::extension::ClassDoc(
-  BOB_EXT_MODULE_PREFIX ".KMeansMachine",
-  "Statistical model for the :ref:`k-means <kmeans>` .\n"
-  "See Section 9.1 of Bishop, \"Pattern recognition and machine learning\", 2006"
-).add_constructor(
-  bob::extension::FunctionDoc(
-    "__init__",
-    "Creates a KMeansMachine",
-    "",
-    true
-  )
-  .add_prototype("n_means,n_inputs","")
-  .add_prototype("other","")
-  .add_prototype("hdf5","")
-  .add_prototype("","")
-
-  .add_parameter("n_means", "int", "Number of means")
-  .add_parameter("n_inputs", "int", "Dimension of the feature vector")
-  .add_parameter("other", ":py:class:`bob.learn.em.KMeansMachine`", "A KMeansMachine object to be copied.")
-  .add_parameter("hdf5", ":py:class:`bob.io.base.HDF5File`", "An HDF5 file open for reading")
-
-);
-
-
-static int PyBobLearnEMKMeansMachine_init_number(PyBobLearnEMKMeansMachineObject* self, PyObject* args, PyObject* kwargs) {
-
-  char** kwlist = KMeansMachine_doc.kwlist(0);
-  int n_inputs    = 1;
-  int n_means = 1;
-  //Parsing the input argments
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ii", kwlist, &n_means, &n_inputs))
-    return -1;
-
-  if(n_means < 0){
-    PyErr_Format(PyExc_TypeError, "means argument must be greater than or equal to zero");
-    KMeansMachine_doc.print_usage();
-    return -1;
-  }
-
-  if(n_inputs < 0){
-    PyErr_Format(PyExc_TypeError, "input argument must be greater than or equal to zero");
-    KMeansMachine_doc.print_usage();
-    return -1;
-   }
-
-  self->cxx.reset(new bob::learn::em::KMeansMachine(n_means, n_inputs));
-  return 0;
-}
-
-
-static int PyBobLearnEMKMeansMachine_init_copy(PyBobLearnEMKMeansMachineObject* self, PyObject* args, PyObject* kwargs) {
-
-  char** kwlist = KMeansMachine_doc.kwlist(1);
-  PyBobLearnEMKMeansMachineObject* tt;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", kwlist, &PyBobLearnEMKMeansMachine_Type, &tt)){
-    KMeansMachine_doc.print_usage();
-    return -1;
-  }
-
-  self->cxx.reset(new bob::learn::em::KMeansMachine(*tt->cxx));
-  return 0;
-}
-
-
-static int PyBobLearnEMKMeansMachine_init_hdf5(PyBobLearnEMKMeansMachineObject* self, PyObject* args, PyObject* kwargs) {
-
-  char** kwlist = KMeansMachine_doc.kwlist(2);
-
-  PyBobIoHDF5FileObject* config = 0;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, &PyBobIoHDF5File_Converter, &config)){
-    KMeansMachine_doc.print_usage();
-    return -1;
-  }
-  auto config_ = make_safe(config);
-  self->cxx.reset(new bob::learn::em::KMeansMachine(*(config->f)));
-
-  return 0;
-}
-
-
-static int PyBobLearnEMKMeansMachine_init(PyBobLearnEMKMeansMachineObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  // get the number of command line arguments
-  int nargs = (args?PyTuple_Size(args):0) + (kwargs?PyDict_Size(kwargs):0);
-
-  switch (nargs) {
-
-    case 0: //default initializer ()
-      self->cxx.reset(new bob::learn::em::KMeansMachine());
-      return 0;
-
-    case 1:{
-      //Reading the input argument
-      PyObject* arg = 0;
-      if (PyTuple_Size(args))
-        arg = PyTuple_GET_ITEM(args, 0);
-      else {
-        PyObject* tmp = PyDict_Values(kwargs);
-        auto tmp_ = make_safe(tmp);
-        arg = PyList_GET_ITEM(tmp, 0);
-      }
-
-      // If the constructor input is Gaussian object
-     if (PyBobLearnEMKMeansMachine_Check(arg))
-       return PyBobLearnEMKMeansMachine_init_copy(self, args, kwargs);
-      // If the constructor input is a HDF5
-     else if (PyBobIoHDF5File_Check(arg))
-       return PyBobLearnEMKMeansMachine_init_hdf5(self, args, kwargs);
-    }
-    case 2:
-      return PyBobLearnEMKMeansMachine_init_number(self, args, kwargs);
-    default:
-      PyErr_Format(PyExc_RuntimeError, "number of arguments mismatch - %s requires 0, 1 or 2 arguments, but you provided %d (see help)", Py_TYPE(self)->tp_name, nargs);
-      KMeansMachine_doc.print_usage();
-      return -1;
-  }
-  BOB_CATCH_MEMBER("cannot create KMeansMachine", -1)
-  return 0;
-}
-
-
-
-static void PyBobLearnEMKMeansMachine_delete(PyBobLearnEMKMeansMachineObject* self) {
-  self->cxx.reset();
-  Py_TYPE(self)->tp_free((PyObject*)self);
-}
-
-static PyObject* PyBobLearnEMKMeansMachine_RichCompare(PyBobLearnEMKMeansMachineObject* self, PyObject* other, int op) {
-  BOB_TRY
-
-  if (!PyBobLearnEMKMeansMachine_Check(other)) {
-    PyErr_Format(PyExc_TypeError, "cannot compare `%s' with `%s'", Py_TYPE(self)->tp_name, Py_TYPE(other)->tp_name);
-    return 0;
-  }
-  auto other_ = reinterpret_cast<PyBobLearnEMKMeansMachineObject*>(other);
-  switch (op) {
-    case Py_EQ:
-      if (*self->cxx==*other_->cxx) Py_RETURN_TRUE; else Py_RETURN_FALSE;
-    case Py_NE:
-      if (*self->cxx==*other_->cxx) Py_RETURN_FALSE; else Py_RETURN_TRUE;
-    default:
-      Py_INCREF(Py_NotImplemented);
-      return Py_NotImplemented;
-  }
-  BOB_CATCH_MEMBER("cannot compare KMeansMachine objects", 0)
-}
-
-int PyBobLearnEMKMeansMachine_Check(PyObject* o) {
-  return PyObject_IsInstance(o, reinterpret_cast<PyObject*>(&PyBobLearnEMKMeansMachine_Type));
-}
-
-
-/******************************************************************/
-/************ Variables Section ***********************************/
-/******************************************************************/
-
-/***** shape *****/
-static auto shape = bob::extension::VariableDoc(
-  "shape",
-  "(int,int)",
-  "A tuple that represents the number of means and dimensionality of the feature vector``(n_means, dim)``.",
-  ""
-);
-PyObject* PyBobLearnEMKMeansMachine_getShape(PyBobLearnEMKMeansMachineObject* self, void*) {
-  BOB_TRY
-  return Py_BuildValue("(i,i)", self->cxx->getNMeans(), self->cxx->getNInputs());
-  BOB_CATCH_MEMBER("shape could not be read", 0)
-}
-
-/***** MEAN *****/
-
-static auto means = bob::extension::VariableDoc(
-  "means",
-  "array_like <float, 2D>",
-  "The means",
-  ""
-);
-PyObject* PyBobLearnEMKMeansMachine_getMeans(PyBobLearnEMKMeansMachineObject* self, void*){
-  BOB_TRY
-  return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getMeans());
-  BOB_CATCH_MEMBER("means could not be read", 0)
-}
-int PyBobLearnEMKMeansMachine_setMeans(PyBobLearnEMKMeansMachineObject* self, PyObject* value, void*){
-  BOB_TRY
-  PyBlitzArrayObject* input;
-  if (!PyBlitzArray_Converter(value, &input)){
-    PyErr_Format(PyExc_RuntimeError, "%s %s expects a 2D array of floats", Py_TYPE(self)->tp_name, means.name());
-    return -1;
-  }
-  auto o_ = make_safe(input);
-
-  // perform check on the input
-  if (input->type_num != NPY_FLOAT64){
-    PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, means.name());
-    return 0;
-  }
-
-  if (input->ndim != 2){
-    PyErr_Format(PyExc_TypeError, "`%s' only processes 2D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, means.name());
-    return 0;
-  }
-
-  if (input->shape[1] != (Py_ssize_t)self->cxx->getNInputs()) {
-    PyErr_Format(PyExc_TypeError, "`%s' 2D `input` array should have the shape [N, %" PY_FORMAT_SIZE_T "d] not [N, %" PY_FORMAT_SIZE_T "d] for `%s`", Py_TYPE(self)->tp_name, self->cxx->getNInputs(), input->shape[0], means.name());
-    return 0;
-  }
-
-  auto b = PyBlitzArrayCxx_AsBlitz<double,2>(input, "means");
-  if (!b) return -1;
-  self->cxx->setMeans(*b);
-  return 0;
-  BOB_CATCH_MEMBER("means could not be set", -1)
-}
-
-
-static PyGetSetDef PyBobLearnEMKMeansMachine_getseters[] = {
-  {
-   shape.name(),
-   (getter)PyBobLearnEMKMeansMachine_getShape,
-   0,
-   shape.doc(),
-   0
-  },
-  {
-   means.name(),
-   (getter)PyBobLearnEMKMeansMachine_getMeans,
-   (setter)PyBobLearnEMKMeansMachine_setMeans,
-   means.doc(),
-   0
-  },
-  {0}  // Sentinel
-};
-
-
-/******************************************************************/
-/************ Functions Section ***********************************/
-/******************************************************************/
-
-
-/*** save ***/
-static auto save = bob::extension::FunctionDoc(
-  "save",
-  "Save the configuration of the KMeansMachine to a given HDF5 file"
-)
-.add_prototype("hdf5")
-.add_parameter("hdf5", ":py:class:`bob.io.base.HDF5File`", "An HDF5 file open for writing");
-static PyObject* PyBobLearnEMKMeansMachine_Save(PyBobLearnEMKMeansMachineObject* self,  PyObject* args, PyObject* kwargs) {
-
-  BOB_TRY
-
-  // get list of arguments
-  char** kwlist = save.kwlist(0);
-  PyBobIoHDF5FileObject* hdf5;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, PyBobIoHDF5File_Converter, &hdf5)) return 0;
-
-  auto hdf5_ = make_safe(hdf5);
-  self->cxx->save(*hdf5->f);
-
-  BOB_CATCH_MEMBER("cannot save the data", 0)
-  Py_RETURN_NONE;
-}
-
-/*** load ***/
-static auto load = bob::extension::FunctionDoc(
-  "load",
-  "Load the configuration of the KMeansMachine to a given HDF5 file"
-)
-.add_prototype("hdf5")
-.add_parameter("hdf5", ":py:class:`bob.io.base.HDF5File`", "An HDF5 file open for reading");
-static PyObject* PyBobLearnEMKMeansMachine_Load(PyBobLearnEMKMeansMachineObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  char** kwlist = load.kwlist(0);
-  PyBobIoHDF5FileObject* hdf5;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, PyBobIoHDF5File_Converter, &hdf5)) return 0;
-
-  auto hdf5_ = make_safe(hdf5);
-  self->cxx->load(*hdf5->f);
-
-  BOB_CATCH_MEMBER("cannot load the data", 0)
-  Py_RETURN_NONE;
-}
-
-
-/*** is_similar_to ***/
-static auto is_similar_to = bob::extension::FunctionDoc(
-  "is_similar_to",
-
-  "Compares this KMeansMachine with the ``other`` one to be approximately the same.",
-  "The optional values ``r_epsilon`` and ``a_epsilon`` refer to the "
-  "relative and absolute precision for the ``weights``, ``biases`` "
-  "and any other values internal to this machine."
-)
-.add_prototype("other, [r_epsilon], [a_epsilon]","output")
-.add_parameter("other", ":py:class:`bob.learn.em.KMeansMachine`", "A KMeansMachine object to be compared.")
-.add_parameter("r_epsilon", "float", "Relative precision.")
-.add_parameter("a_epsilon", "float", "Absolute precision.")
-.add_return("output","bool","True if it is similar, otherwise false.");
-static PyObject* PyBobLearnEMKMeansMachine_IsSimilarTo(PyBobLearnEMKMeansMachineObject* self, PyObject* args, PyObject* kwds) {
-
-  /* Parses input arguments in a single shot */
-  char** kwlist = is_similar_to.kwlist(0);
-
-  //PyObject* other = 0;
-  PyBobLearnEMKMeansMachineObject* other = 0;
-  double r_epsilon = 1.e-5;
-  double a_epsilon = 1.e-8;
-
-  if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|dd", kwlist,
-        &PyBobLearnEMKMeansMachine_Type, &other,
-        &r_epsilon, &a_epsilon)){
-
-        is_similar_to.print_usage();
-        return 0;
-  }
-
-  if (self->cxx->is_similar_to(*other->cxx, r_epsilon, a_epsilon))
-    Py_RETURN_TRUE;
-  else
-    Py_RETURN_FALSE;
-}
-
-
-/*** resize ***/
-static auto resize = bob::extension::FunctionDoc(
-  "resize",
-  "Allocates space for the statistics and resets to zero.",
-  0,
-  true
-)
-.add_prototype("n_means,n_inputs")
-.add_parameter("n_means", "int", "Number of means")
-.add_parameter("n_inputs", "int", "Dimensionality of the feature vector");
-static PyObject* PyBobLearnEMKMeansMachine_resize(PyBobLearnEMKMeansMachineObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  /* Parses input arguments in a single shot */
-  char** kwlist = resize.kwlist(0);
-
-  int n_means = 0;
-  int n_inputs = 0;
-
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ii", kwlist, &n_means, &n_inputs)) return 0;
-
-  if (n_means <= 0){
-    PyErr_Format(PyExc_TypeError, "n_means must be greater than zero");
-    resize.print_usage();
-    return 0;
-  }
-  if (n_inputs <= 0){
-    PyErr_Format(PyExc_TypeError, "n_inputs must be greater than zero");
-    resize.print_usage();
-    return 0;
-  }
-
-  self->cxx->resize(n_means, n_inputs);
-
-  BOB_CATCH_MEMBER("cannot perform the resize method", 0)
-
-  Py_RETURN_NONE;
-}
-
-/*** get_mean ***/
-static auto get_mean = bob::extension::FunctionDoc(
-  "get_mean",
-  "Get the i'th mean.",
-  ".. note:: An exception is thrown if i is out of range.",
-  true
-)
-.add_prototype("i","mean")
-.add_parameter("i", "int", "Index of the mean")
-.add_return("mean","array_like <float, 1D>","Mean array");
-static PyObject* PyBobLearnEMKMeansMachine_get_mean(PyBobLearnEMKMeansMachineObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  char** kwlist = get_mean.kwlist(0);
-
-  int i = 0;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, &i)) return 0;
-
-  return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getMean(i));
-
-  BOB_CATCH_MEMBER("cannot get the mean", 0)
-}
-
-
-/*** set_mean ***/
-static auto set_mean = bob::extension::FunctionDoc(
-  "set_mean",
-  "Set the i'th mean.",
-  ".. note:: An exception is thrown if i is out of range.",
-  true
-)
-.add_prototype("i,mean")
-.add_parameter("i", "int", "Index of the mean")
-.add_parameter("mean", "array_like <float, 1D>", "Mean array");
-static PyObject* PyBobLearnEMKMeansMachine_set_mean(PyBobLearnEMKMeansMachineObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  char** kwlist = set_mean.kwlist(0);
-
-  int i = 0;
-  PyBlitzArrayObject* mean = 0;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "iO&", kwlist, &i, &PyBlitzArray_Converter, &mean)) return 0;
-
-  //protects acquired resources through this scope
-  auto mean_ = make_safe(mean);
-
-  // perform check on the input
-  if (mean->type_num != NPY_FLOAT64){
-    PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, set_mean.name());
-    return 0;
-  }
-
-  if (mean->ndim != 1){
-    PyErr_Format(PyExc_TypeError, "`%s' only processes 1D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, set_mean.name());
-    return 0;
-  }
-
-  if (mean->shape[0] != (Py_ssize_t)self->cxx->getNInputs()){
-    PyErr_Format(PyExc_TypeError, "`%s' 1D `input` array should have %" PY_FORMAT_SIZE_T "d elements, not %" PY_FORMAT_SIZE_T "d for `%s`", Py_TYPE(self)->tp_name, self->cxx->getNInputs(), mean->shape[0], set_mean.name());
-    return 0;
-  }
-
-  //setting the mean
-  self->cxx->setMean(i, *PyBlitzArrayCxx_AsBlitz<double,1>(mean));
-
-  BOB_CATCH_MEMBER("cannot set the mean", 0)
-
-  Py_RETURN_NONE;
-}
-
-
-
-/*** get_distance_from_mean ***/
-static auto get_distance_from_mean = bob::extension::FunctionDoc(
-  "get_distance_from_mean",
-  "Return the power of two of the square Euclidean distance of the sample, x, to the i'th mean.",
-  ".. note:: An exception is thrown if i is out of range.",
-  true
-)
-.add_prototype("input,i","output")
-.add_parameter("input", "array_like <float, 1D>", "The data sample (feature vector)")
-.add_parameter("i", "int", "The index of the mean")
-.add_return("output","float","Square Euclidean distance of the sample, x, to the i'th mean");
-static PyObject* PyBobLearnEMKMeansMachine_get_distance_from_mean(PyBobLearnEMKMeansMachineObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  char** kwlist = get_distance_from_mean.kwlist(0);
-
-  PyBlitzArrayObject* input = 0;
-  int i = 0;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&i", kwlist, &PyBlitzArray_Converter, &input, &i)){
-    return 0;
-  }
-
-  //protects acquired resources through this scope
-  auto input_ = make_safe(input);
-
-  // perform check on the input
-  if (input->type_num != NPY_FLOAT64){
-    PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, get_distance_from_mean.name());
-    return 0;
-  }
-
-  if (input->ndim != 1){
-    PyErr_Format(PyExc_TypeError, "`%s' only processes 1D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, get_distance_from_mean.name());
-    return 0;
-  }
-
-  if (input->shape[0] != (Py_ssize_t)self->cxx->getNInputs()){
-    PyErr_Format(PyExc_TypeError, "`%s' 1D `input` array should have %" PY_FORMAT_SIZE_T "d elements, not %" PY_FORMAT_SIZE_T "d for `%s`", Py_TYPE(self)->tp_name, self->cxx->getNInputs(), input->shape[0], get_distance_from_mean.name());
-    return 0;
-  }
-
-  double output = self->cxx->getDistanceFromMean(*PyBlitzArrayCxx_AsBlitz<double,1>(input),i);
-  return Py_BuildValue("d", output);
-
-  BOB_CATCH_MEMBER("cannot compute the likelihood", 0)
-}
-
-
-/*** get_closest_mean ***/
-static auto get_closest_mean = bob::extension::FunctionDoc(
-  "get_closest_mean",
-  "Calculate the index of the mean that is closest (in terms of square Euclidean distance) to the data sample, x.",
-  "",
-  true
-)
-.add_prototype("input","output")
-.add_parameter("input", "array_like <float, 1D>", "The data sample (feature vector)")
-.add_return("output", "(int, int)", "Tuple containing the closest mean and the minimum distance from the input");
-static PyObject* PyBobLearnEMKMeansMachine_get_closest_mean(PyBobLearnEMKMeansMachineObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  char** kwlist = get_closest_mean.kwlist(0);
-
-  PyBlitzArrayObject* input = 0;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, &PyBlitzArray_Converter, &input)) return 0;
-
-  //protects acquired resources through this scope
-  auto input_ = make_safe(input);
-
-  size_t closest_mean = 0;
-  double min_distance = -1;
-
-  // perform check on the input
-  if (input->type_num != NPY_FLOAT64){
-    PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, get_closest_mean.name());
-    return 0;
-  }
-
-  if (input->ndim != 1){
-    PyErr_Format(PyExc_TypeError, "`%s' only processes 1D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, get_closest_mean.name());
-    return 0;
-  }
-
-  if (input->shape[0] != (Py_ssize_t)self->cxx->getNInputs()){
-    PyErr_Format(PyExc_TypeError, "`%s' 1D `input` array should have %" PY_FORMAT_SIZE_T "d elements, not %" PY_FORMAT_SIZE_T "d for `%s`", Py_TYPE(self)->tp_name, self->cxx->getNInputs(), input->shape[0], get_closest_mean.name());
-    return 0;
-  }
-
-  self->cxx->getClosestMean(*PyBlitzArrayCxx_AsBlitz<double,1>(input), closest_mean, min_distance);
-
-  return Py_BuildValue("(i,d)", closest_mean, min_distance);
-
-  BOB_CATCH_MEMBER("cannot compute the closest mean", 0)
-}
-
-
-/*** get_min_distance ***/
-static auto get_min_distance = bob::extension::FunctionDoc(
-  "get_min_distance",
-  "Output the minimum (Square Euclidean) distance between the input and the closest mean ",
-  "",
-  true
-)
-.add_prototype("input","output")
-.add_parameter("input", "array_like <float, 1D>", "The data sample (feature vector)")
-.add_return("output", "float", "The minimum distance");
-static PyObject* PyBobLearnEMKMeansMachine_get_min_distance(PyBobLearnEMKMeansMachineObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  char** kwlist = get_min_distance.kwlist(0);
-
-  PyBlitzArrayObject* input = 0;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, &PyBlitzArray_Converter, &input)) return 0;
-
-  //protects acquired resources through this scope
-  auto input_ = make_safe(input);
-  double min_distance = 0;
-
-  // perform check on the input
-  if (input->type_num != NPY_FLOAT64){
-    PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, get_min_distance.name());
-    return 0;
-  }
-
-  if (input->ndim != 1){
-    PyErr_Format(PyExc_TypeError, "`%s' only processes 1D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, get_min_distance.name());
-    return 0;
-  }
-
-  if (input->shape[0] != (Py_ssize_t)self->cxx->getNInputs()){
-    PyErr_Format(PyExc_TypeError, "`%s' 1D `input` array should have %" PY_FORMAT_SIZE_T "d elements, not %" PY_FORMAT_SIZE_T "d for `%s`", Py_TYPE(self)->tp_name, self->cxx->getNInputs(), input->shape[0], get_min_distance.name());
-    return 0;
-  }
-
-  min_distance = self->cxx->getMinDistance(*PyBlitzArrayCxx_AsBlitz<double,1>(input));
-
-  return Py_BuildValue("d", min_distance);
-
-  BOB_CATCH_MEMBER("cannot compute the min distance", 0)
-}
-
-/**** get_variances_and_weights_for_each_cluster ***/
-static auto get_variances_and_weights_for_each_cluster = bob::extension::FunctionDoc(
-  "get_variances_and_weights_for_each_cluster",
-  "For each mean, find the subset of the samples that is closest to that mean, and calculate"
-  " 1) the variance of that subset (the cluster variance)"
-  " 2) the proportion of the samples represented by that subset (the cluster weight)",
-  "",
-  true
-)
-.add_prototype("input","output")
-.add_parameter("input", "array_like <float, 2D>", "The data sample (feature vector)")
-.add_return("output", "(array_like <float, 2D>, array_like <float, 1D>)", "A tuple with the variances and the weights respectively");
-static PyObject* PyBobLearnEMKMeansMachine_get_variances_and_weights_for_each_cluster(PyBobLearnEMKMeansMachineObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  char** kwlist =  get_variances_and_weights_for_each_cluster.kwlist(0);
-
-  PyBlitzArrayObject* input = 0;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, &PyBlitzArray_Converter, &input)) return 0;
-
-  //protects acquired resources through this scope
-  auto input_ = make_safe(input);
-
-  // perform check on the input
-  if (input->type_num != NPY_FLOAT64){
-    PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, get_variances_and_weights_for_each_cluster.name());
-    return 0;
-  }
-
-  if (input->ndim != 2){
-    PyErr_Format(PyExc_TypeError, "`%s' only processes 2D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, get_variances_and_weights_for_each_cluster.name());
-    return 0;
-  }
-
-  if (input->shape[1] != (Py_ssize_t)self->cxx->getNInputs() ) {
-    PyErr_Format(PyExc_TypeError, "`%s' 2D `input` array should have the shape [N, %" PY_FORMAT_SIZE_T "d] not [N, %" PY_FORMAT_SIZE_T "d] for `%s`", Py_TYPE(self)->tp_name, self->cxx->getNInputs(), input->shape[1], get_variances_and_weights_for_each_cluster.name());
-    return 0;
-  }
-
-  blitz::Array<double,2> variances(self->cxx->getNMeans(),self->cxx->getNInputs());
-  blitz::Array<double,1> weights(self->cxx->getNMeans());
-
-  self->cxx->getVariancesAndWeightsForEachCluster(*PyBlitzArrayCxx_AsBlitz<double,2>(input),variances,weights);
-
-  return Py_BuildValue("(N,N)",PyBlitzArrayCxx_AsConstNumpy(variances), PyBlitzArrayCxx_AsConstNumpy(weights));
-
-  BOB_CATCH_MEMBER("cannot compute the variances and weights for each cluster", 0)
-}
-
-
-/**** __get_variances_and_weights_for_each_cluster_init__ ***/
-static auto __get_variances_and_weights_for_each_cluster_init__ = bob::extension::FunctionDoc(
-  "__get_variances_and_weights_for_each_cluster_init__",
-  "Methods consecutively called by getVariancesAndWeightsForEachCluster()"
-  "This should help for the parallelization on several nodes by splitting the data and calling"
-  "getVariancesAndWeightsForEachClusterAcc() for each split. In this case, there is a need to sum"
-  "with the m_cache_means, variances, and weights variables before performing the merge on one"
-  "node using getVariancesAndWeightsForEachClusterFin().",
-  "",
-  true
-)
-.add_prototype("variances,weights","")
-.add_parameter("variances", "array_like <float, 2D>", "Variance array")
-.add_parameter("weights", "array_like <float, 1D>", "Weight array");
-static PyObject* PyBobLearnEMKMeansMachine_get_variances_and_weights_for_each_cluster_init(PyBobLearnEMKMeansMachineObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  char** kwlist =  __get_variances_and_weights_for_each_cluster_init__.kwlist(0);
-
-  PyBlitzArrayObject* variances = 0;
-  PyBlitzArrayObject* weights   = 0;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&O&", kwlist, &PyBlitzArray_Converter, &variances,  &PyBlitzArray_Converter, &weights)) return 0;
-
-  //protects acquired resources through this scope
-  auto weights_   = make_safe(weights);
-  auto variances_ = make_safe(variances);
-
-  self->cxx->getVariancesAndWeightsForEachClusterInit(*PyBlitzArrayCxx_AsBlitz<double,2>(variances), *PyBlitzArrayCxx_AsBlitz<double,1>(weights));
-  Py_RETURN_NONE;
-
-  BOB_CATCH_MEMBER("cannot compute the variances and weights for each cluster", 0)
-}
-
-
-/**** __get_variances_and_weights_for_each_cluster_acc__ ***/
-static auto __get_variances_and_weights_for_each_cluster_acc__ = bob::extension::FunctionDoc(
-  "__get_variances_and_weights_for_each_cluster_acc__",
-  "Methods consecutively called by getVariancesAndWeightsForEachCluster()"
-  "This should help for the parallelization on several nodes by splitting the data and calling"
-  "getVariancesAndWeightsForEachClusterAcc() for each split. In this case, there is a need to sum"
-  "with the m_cache_means, variances, and weights variables before performing the merge on one"
-  "node using getVariancesAndWeightsForEachClusterFin().",
-  "",
-  true
-)
-.add_prototype("data,variances,weights","")
-.add_parameter("data", "array_like <float, 2D>", "data array")
-.add_parameter("variances", "array_like <float, 2D>", "Variance array")
-.add_parameter("weights", "array_like <float, 1D>", "Weight array");
-static PyObject* PyBobLearnEMKMeansMachine_get_variances_and_weights_for_each_cluster_acc(PyBobLearnEMKMeansMachineObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  char** kwlist =  __get_variances_and_weights_for_each_cluster_acc__.kwlist(0);
-
-  PyBlitzArrayObject* data      = 0;
-  PyBlitzArrayObject* variances = 0;
-  PyBlitzArrayObject* weights   = 0;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&O&O&", kwlist, &PyBlitzArray_Converter, &data, &PyBlitzArray_Converter, &variances, &PyBlitzArray_Converter, &weights)) return 0;
-
-  //protects acquired resources through this scope
-  auto data_      = make_safe(data);
-  auto weights_   = make_safe(weights);
-  auto variances_ = make_safe(variances);
-
-  self->cxx->getVariancesAndWeightsForEachClusterAcc(*PyBlitzArrayCxx_AsBlitz<double,2>(data), *PyBlitzArrayCxx_AsBlitz<double,2>(variances), *PyBlitzArrayCxx_AsBlitz<double,1>(weights));
-  Py_RETURN_NONE;
-
-  BOB_CATCH_MEMBER("cannot compute the variances and weights for each cluster", 0)
-}
-
-
-/**** __get_variances_and_weights_for_each_cluster_fin__ ***/
-static auto __get_variances_and_weights_for_each_cluster_fin__ = bob::extension::FunctionDoc(
-  "__get_variances_and_weights_for_each_cluster_fin__",
-  "Methods consecutively called by getVariancesAndWeightsForEachCluster()"
-  "This should help for the parallelization on several nodes by splitting the data and calling"
-  "getVariancesAndWeightsForEachClusterAcc() for each split. In this case, there is a need to sum"
-  "with the m_cache_means, variances, and weights variables before performing the merge on one"
-  "node using getVariancesAndWeightsForEachClusterFin().",
-  "",
-  true
-)
-.add_prototype("variances,weights","")
-.add_parameter("variances", "array_like <float, 2D>", "Variance array")
-.add_parameter("weights", "array_like <float, 1D>", "Weight array");
-static PyObject* PyBobLearnEMKMeansMachine_get_variances_and_weights_for_each_cluster_fin(PyBobLearnEMKMeansMachineObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  char** kwlist =  __get_variances_and_weights_for_each_cluster_fin__.kwlist(0);
-
-  PyBlitzArrayObject* variances = 0;
-  PyBlitzArrayObject* weights   = 0;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&O&", kwlist, &PyBlitzArray_Converter, &variances,  &PyBlitzArray_Converter, &weights)) return 0;
-
-  //protects acquired resources through this scope
-  auto weights_   = make_safe(weights);
-  auto variances_ = make_safe(variances);
-
-  self->cxx->getVariancesAndWeightsForEachClusterFin(*PyBlitzArrayCxx_AsBlitz<double,2>(variances), *PyBlitzArrayCxx_AsBlitz<double,1>(weights));
-  Py_RETURN_NONE;
-
-  BOB_CATCH_MEMBER("cannot compute the variances and weights for each cluster", 0)
-}
-
-
-static PyMethodDef PyBobLearnEMKMeansMachine_methods[] = {
-  {
-    save.name(),
-    (PyCFunction)PyBobLearnEMKMeansMachine_Save,
-    METH_VARARGS|METH_KEYWORDS,
-    save.doc()
-  },
-  {
-    load.name(),
-    (PyCFunction)PyBobLearnEMKMeansMachine_Load,
-    METH_VARARGS|METH_KEYWORDS,
-    load.doc()
-  },
-  {
-    is_similar_to.name(),
-    (PyCFunction)PyBobLearnEMKMeansMachine_IsSimilarTo,
-    METH_VARARGS|METH_KEYWORDS,
-    is_similar_to.doc()
-  },
-  {
-    resize.name(),
-    (PyCFunction)PyBobLearnEMKMeansMachine_resize,
-    METH_VARARGS|METH_KEYWORDS,
-    resize.doc()
-  },
-  {
-    get_mean.name(),
-    (PyCFunction)PyBobLearnEMKMeansMachine_get_mean,
-    METH_VARARGS|METH_KEYWORDS,
-    get_mean.doc()
-  },
-  {
-    set_mean.name(),
-    (PyCFunction)PyBobLearnEMKMeansMachine_set_mean,
-    METH_VARARGS|METH_KEYWORDS,
-    set_mean.doc()
-  },
-  {
-    get_distance_from_mean.name(),
-    (PyCFunction)PyBobLearnEMKMeansMachine_get_distance_from_mean,
-    METH_VARARGS|METH_KEYWORDS,
-    get_distance_from_mean.doc()
-  },
-  {
-    get_closest_mean.name(),
-    (PyCFunction)PyBobLearnEMKMeansMachine_get_closest_mean,
-    METH_VARARGS|METH_KEYWORDS,
-    get_closest_mean.doc()
-  },
-  {
-    get_min_distance.name(),
-    (PyCFunction)PyBobLearnEMKMeansMachine_get_min_distance,
-    METH_VARARGS|METH_KEYWORDS,
-    get_min_distance.doc()
-  },
-  {
-    get_variances_and_weights_for_each_cluster.name(),
-    (PyCFunction)PyBobLearnEMKMeansMachine_get_variances_and_weights_for_each_cluster,
-    METH_VARARGS|METH_KEYWORDS,
-    get_variances_and_weights_for_each_cluster.doc()
-  },
-  {
-    __get_variances_and_weights_for_each_cluster_init__.name(),
-    (PyCFunction)PyBobLearnEMKMeansMachine_get_variances_and_weights_for_each_cluster_init,
-    METH_VARARGS|METH_KEYWORDS,
-    __get_variances_and_weights_for_each_cluster_init__.doc()
-  },
-  {
-    __get_variances_and_weights_for_each_cluster_acc__.name(),
-    (PyCFunction)PyBobLearnEMKMeansMachine_get_variances_and_weights_for_each_cluster_acc,
-    METH_VARARGS|METH_KEYWORDS,
-    __get_variances_and_weights_for_each_cluster_acc__.doc()
-  },
-  {
-    __get_variances_and_weights_for_each_cluster_fin__.name(),
-    (PyCFunction)PyBobLearnEMKMeansMachine_get_variances_and_weights_for_each_cluster_fin,
-    METH_VARARGS|METH_KEYWORDS,
-    __get_variances_and_weights_for_each_cluster_fin__.doc()
-  },
-
-  {0} /* Sentinel */
-};
-
-
-/******************************************************************/
-/************ Module Section **************************************/
-/******************************************************************/
-
-// Define the Gaussian type struct; will be initialized later
-PyTypeObject PyBobLearnEMKMeansMachine_Type = {
-  PyVarObject_HEAD_INIT(0,0)
-  0
-};
-
-bool init_BobLearnEMKMeansMachine(PyObject* module)
-{
-  // initialize the type struct
-  PyBobLearnEMKMeansMachine_Type.tp_name = KMeansMachine_doc.name();
-  PyBobLearnEMKMeansMachine_Type.tp_basicsize = sizeof(PyBobLearnEMKMeansMachineObject);
-  PyBobLearnEMKMeansMachine_Type.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE;
-  PyBobLearnEMKMeansMachine_Type.tp_doc = KMeansMachine_doc.doc();
-
-  // set the functions
-  PyBobLearnEMKMeansMachine_Type.tp_new = PyType_GenericNew;
-  PyBobLearnEMKMeansMachine_Type.tp_init = reinterpret_cast<initproc>(PyBobLearnEMKMeansMachine_init);
-  PyBobLearnEMKMeansMachine_Type.tp_dealloc = reinterpret_cast<destructor>(PyBobLearnEMKMeansMachine_delete);
-  PyBobLearnEMKMeansMachine_Type.tp_richcompare = reinterpret_cast<richcmpfunc>(PyBobLearnEMKMeansMachine_RichCompare);
-  PyBobLearnEMKMeansMachine_Type.tp_methods = PyBobLearnEMKMeansMachine_methods;
-  PyBobLearnEMKMeansMachine_Type.tp_getset = PyBobLearnEMKMeansMachine_getseters;
-  //PyBobLearnEMGMMMachine_Type.tp_call = reinterpret_cast<ternaryfunc>(PyBobLearnEMGMMMachine_loglikelihood);
-
-
-  // check that everything is fine
-  if (PyType_Ready(&PyBobLearnEMKMeansMachine_Type) < 0) return false;
-
-  // add the type to the module
-  Py_INCREF(&PyBobLearnEMKMeansMachine_Type);
-  return PyModule_AddObject(module, "KMeansMachine", (PyObject*)&PyBobLearnEMKMeansMachine_Type) >= 0;
-}
diff --git a/bob/learn/em/kmeans_trainer.cpp b/bob/learn/em/kmeans_trainer.cpp
deleted file mode 100644
index d03148cdb70482dfce55ddc807f9a7fb0ede96ff..0000000000000000000000000000000000000000
--- a/bob/learn/em/kmeans_trainer.cpp
+++ /dev/null
@@ -1,567 +0,0 @@
-/**
- * @author Tiago de Freitas Pereira <tiago.pereira@idiap.ch>
- * @date Tue 13 Jan 16:50:00 2015
- *
- * @brief Python API for bob::learn::em
- *
- * Copyright (C) 2011-2014 Idiap Research Institute, Martigny, Switzerland
- */
-
-#include "main.h"
-#include <boost/assign.hpp>
-
-/******************************************************************/
-/************ Constructor Section *********************************/
-/******************************************************************/
-
-// InitializationMethod type conversion
-
-#if BOOST_VERSION >= 104700
-  static const std::map<std::string, bob::learn::em::KMeansTrainer::InitializationMethod> IM = boost::assign::map_list_of
-  ("RANDOM",  bob::learn::em::KMeansTrainer::InitializationMethod::RANDOM)
-  ("RANDOM_NO_DUPLICATE", bob::learn::em::KMeansTrainer::InitializationMethod::RANDOM_NO_DUPLICATE)
-  ("KMEANS_PLUS_PLUS", bob::learn::em::KMeansTrainer::InitializationMethod::KMEANS_PLUS_PLUS)
-  ;
-#else
-  static const std::map<std::string, bob::learn::em::KMeansTrainer::InitializationMethod> IM = boost::assign::map_list_of
-  ("RANDOM",  bob::learn::em::KMeansTrainer::InitializationMethod::RANDOM),
-  ("RANDOM_NO_DUPLICATE", bob::learn::em::KMeansTrainer::InitializationMethod::RANDOM_NO_DUPLICATE)
-  ;
-#endif
-
-static inline bob::learn::em::KMeansTrainer::InitializationMethod string2IM(const std::string& o){            /* converts string to InitializationMethod type */
-  auto it = IM.find(o);
-  if (it == IM.end()) throw std::runtime_error("The given InitializationMethod '" + o + "' is not known; choose one of ('RANDOM', 'RANDOM_NO_DUPLICATE', 'KMEANS_PLUS_PLUS')");
-  else return it->second;
-}
-static inline const std::string& IM2string(bob::learn::em::KMeansTrainer::InitializationMethod o){            /* converts InitializationMethod type to string */
-  for (auto it = IM.begin(); it != IM.end(); ++it) if (it->second == o) return it->first;
-  throw std::runtime_error("The given InitializationMethod type is not known");
-}
-
-
-static auto KMeansTrainer_doc = bob::extension::ClassDoc(
-  BOB_EXT_MODULE_PREFIX ".KMeansTrainer",
-  "Trains a KMeans clustering :ref:`k-means <kmeans>`."
-  "This class implements the expectation-maximization algorithm for a k-means."
-  "See Section 9.1 of Bishop, \"Pattern recognition and machine learning\", 2006"
-  "It uses a random initialization of the means followed by the expectation-maximization algorithm"
-).add_constructor(
-  bob::extension::FunctionDoc(
-    "__init__",
-    "Creates a KMeansTrainer",
-    "",
-    true
-  )
-  .add_prototype("initialization_method","")
-  .add_prototype("other","")
-  .add_prototype("","")
-
-  .add_parameter("initialization_method", "str", "The initialization method of the means.\nPossible values are: 'RANDOM', 'RANDOM_NO_DUPLICATE', 'KMEANS_PLUS_PLUS' ")
-  .add_parameter("other", ":py:class:`bob.learn.em.KMeansTrainer`", "A KMeansTrainer object to be copied.")
-
-);
-
-
-static int PyBobLearnEMKMeansTrainer_init_copy(PyBobLearnEMKMeansTrainerObject* self, PyObject* args, PyObject* kwargs) {
-
-  char** kwlist = KMeansTrainer_doc.kwlist(1);
-  PyBobLearnEMKMeansTrainerObject* tt;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", kwlist, &PyBobLearnEMKMeansTrainer_Type, &tt)){
-    KMeansTrainer_doc.print_usage();
-    return -1;
-  }
-
-  self->cxx.reset(new bob::learn::em::KMeansTrainer(*tt->cxx));
-  return 0;
-}
-
-static int PyBobLearnEMKMeansTrainer_init_str(PyBobLearnEMKMeansTrainerObject* self, PyObject* args, PyObject* kwargs) {
-
-  char** kwlist = KMeansTrainer_doc.kwlist(0);
-  char* value;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist, &value)){
-    KMeansTrainer_doc.print_usage();
-    return -1;
-  }
-  self->cxx.reset(new bob::learn::em::KMeansTrainer(string2IM(std::string(value))));
-  return 0;
-}
-
-
-static int PyBobLearnEMKMeansTrainer_init(PyBobLearnEMKMeansTrainerObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  int nargs = (args?PyTuple_Size(args):0) + (kwargs?PyDict_Size(kwargs):0);
-
-  switch (nargs) {
-
-    case 0:{ //default initializer ()
-      self->cxx.reset(new bob::learn::em::KMeansTrainer());
-      return 0;
-    }
-    case 1:{
-      //Reading the input argument
-      PyObject* arg = 0;
-      if (PyTuple_Size(args))
-        arg = PyTuple_GET_ITEM(args, 0);
-      else {
-        PyObject* tmp = PyDict_Values(kwargs);
-        auto tmp_ = make_safe(tmp);
-        arg = PyList_GET_ITEM(tmp, 0);
-      }
-
-      // If the constructor input is KMeansTrainer object
-      if (PyBobLearnEMKMeansTrainer_Check(arg))
-        return PyBobLearnEMKMeansTrainer_init_copy(self, args, kwargs);
-      else if(PyString_Check(arg))
-        return PyBobLearnEMKMeansTrainer_init_str(self, args, kwargs);
-    }
-    default:{
-      PyErr_Format(PyExc_RuntimeError, "number of arguments mismatch - %s requires 0 or 1 arguments, but you provided %d (see help)", Py_TYPE(self)->tp_name, nargs);
-      KMeansTrainer_doc.print_usage();
-      return -1;
-    }
-  }
-  BOB_CATCH_MEMBER("cannot create KMeansTrainer", -1)
-  return 0;
-}
-
-
-static void PyBobLearnEMKMeansTrainer_delete(PyBobLearnEMKMeansTrainerObject* self) {
-  self->cxx.reset();
-  Py_TYPE(self)->tp_free((PyObject*)self);
-}
-
-
-int PyBobLearnEMKMeansTrainer_Check(PyObject* o) {
-  return PyObject_IsInstance(o, reinterpret_cast<PyObject*>(&PyBobLearnEMKMeansTrainer_Type));
-}
-
-
-static PyObject* PyBobLearnEMKMeansTrainer_RichCompare(PyBobLearnEMKMeansTrainerObject* self, PyObject* other, int op) {
-  BOB_TRY
-
-  if (!PyBobLearnEMKMeansTrainer_Check(other)) {
-    PyErr_Format(PyExc_TypeError, "cannot compare `%s' with `%s'", Py_TYPE(self)->tp_name, Py_TYPE(other)->tp_name);
-    return 0;
-  }
-  auto other_ = reinterpret_cast<PyBobLearnEMKMeansTrainerObject*>(other);
-  switch (op) {
-    case Py_EQ:
-      if (*self->cxx==*other_->cxx) Py_RETURN_TRUE; else Py_RETURN_FALSE;
-    case Py_NE:
-      if (*self->cxx==*other_->cxx) Py_RETURN_FALSE; else Py_RETURN_TRUE;
-    default:
-      Py_INCREF(Py_NotImplemented);
-      return Py_NotImplemented;
-  }
-  BOB_CATCH_MEMBER("cannot compare KMeansTrainer objects", 0)
-}
-
-
-/******************************************************************/
-/************ Variables Section ***********************************/
-/******************************************************************/
-
-/***** initialization_method *****/
-static auto initialization_method = bob::extension::VariableDoc(
-  "initialization_method",
-  "str",
-  "Initialization method.",
-  "Possible values:\n"
-  " `RANDOM`: Random initialization \n\n"
-  " `RANDOM_NO_DUPLICATE`: Random initialization without repetition \n\n"
-  " `KMEANS_PLUS_PLUS`: Apply the kmeans++ initialization http://en.wikipedia.org/wiki/K-means%2B%2B  \n\n"
-);
-PyObject* PyBobLearnEMKMeansTrainer_getInitializationMethod(PyBobLearnEMKMeansTrainerObject* self, void*) {
-  BOB_TRY
-  return Py_BuildValue("s", IM2string(self->cxx->getInitializationMethod()).c_str());
-  BOB_CATCH_MEMBER("initialization method could not be read", 0)
-}
-int PyBobLearnEMKMeansTrainer_setInitializationMethod(PyBobLearnEMKMeansTrainerObject* self, PyObject* value, void*) {
-  BOB_TRY
-
-  if (!PyString_Check(value)){
-    PyErr_Format(PyExc_RuntimeError, "%s %s expects an str", Py_TYPE(self)->tp_name, initialization_method.name());
-    return -1;
-  }
-  self->cxx->setInitializationMethod(string2IM(PyString_AS_STRING(value)));
-
-  return 0;
-  BOB_CATCH_MEMBER("initialization method could not be set", -1)
-}
-
-
-/***** zeroeth_order_statistics *****/
-static auto zeroeth_order_statistics = bob::extension::VariableDoc(
-  "zeroeth_order_statistics",
-  "array_like <float, 1D>",
-  "Returns the internal statistics. Useful to parallelize the E-step",
-  ""
-);
-PyObject* PyBobLearnEMKMeansTrainer_getZeroethOrderStatistics(PyBobLearnEMKMeansTrainerObject* self, void*){
-  BOB_TRY
-  return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getZeroethOrderStats());
-  BOB_CATCH_MEMBER("zeroeth_order_statistics could not be read", 0)
-}
-int PyBobLearnEMKMeansTrainer_setZeroethOrderStatistics(PyBobLearnEMKMeansTrainerObject* self, PyObject* value, void*){
-  BOB_TRY
-  PyBlitzArrayObject* o;
-  if (!PyBlitzArray_Converter(value, &o)){
-    PyErr_Format(PyExc_RuntimeError, "%s %s expects a 1D array of floats", Py_TYPE(self)->tp_name, zeroeth_order_statistics.name());
-    return -1;
-  }
-  auto o_ = make_safe(o);
-  auto b = PyBlitzArrayCxx_AsBlitz<double,1>(o, "zeroeth_order_statistics");
-  if (!b) return -1;
-  self->cxx->setZeroethOrderStats(*b);
-  return 0;
-  BOB_CATCH_MEMBER("zeroeth_order_statistics could not be set", -1)
-}
-
-
-/***** first_order_statistics *****/
-static auto first_order_statistics = bob::extension::VariableDoc(
-  "first_order_statistics",
-  "array_like <float, 2D>",
-  "Returns the internal statistics. Useful to parallelize the E-step",
-  ""
-);
-PyObject* PyBobLearnEMKMeansTrainer_getFirstOrderStatistics(PyBobLearnEMKMeansTrainerObject* self, void*){
-  BOB_TRY
-  return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getFirstOrderStats());
-  BOB_CATCH_MEMBER("first_order_statistics could not be read", 0)
-}
-int PyBobLearnEMKMeansTrainer_setFirstOrderStatistics(PyBobLearnEMKMeansTrainerObject* self, PyObject* value, void*){
-  BOB_TRY
-  PyBlitzArrayObject* o;
-  if (!PyBlitzArray_Converter(value, &o)){
-    PyErr_Format(PyExc_RuntimeError, "%s %s expects a 1D array of floats", Py_TYPE(self)->tp_name, first_order_statistics.name());
-    return -1;
-  }
-  auto o_ = make_safe(o);
-  auto b = PyBlitzArrayCxx_AsBlitz<double,2>(o, "first_order_statistics");
-  if (!b) return -1;
-  self->cxx->setFirstOrderStats(*b);
-  return 0;
-  BOB_CATCH_MEMBER("first_order_statistics could not be set", -1)
-}
-
-
-/***** average_min_distance *****/
-static auto average_min_distance = bob::extension::VariableDoc(
-  "average_min_distance",
-  "str",
-  "Average min (square Euclidean) distance. Useful to parallelize the E-step.",
-  ""
-);
-PyObject* PyBobLearnEMKMeansTrainer_getAverageMinDistance(PyBobLearnEMKMeansTrainerObject* self, void*) {
-  BOB_TRY
-  return Py_BuildValue("d", self->cxx->getAverageMinDistance());
-  BOB_CATCH_MEMBER("Average Min Distance method could not be read", 0)
-}
-int PyBobLearnEMKMeansTrainer_setAverageMinDistance(PyBobLearnEMKMeansTrainerObject* self, PyObject* value, void*) {
-  BOB_TRY
-
-  if (!PyBob_NumberCheck(value)){
-    PyErr_Format(PyExc_RuntimeError, "%s %s expects an double", Py_TYPE(self)->tp_name, average_min_distance.name());
-    return -1;
-  }
-  self->cxx->setAverageMinDistance(PyFloat_AS_DOUBLE(value));
-
-  return 0;
-  BOB_CATCH_MEMBER("Average Min Distance could not be set", -1)
-}
-
-
-
-
-static PyGetSetDef PyBobLearnEMKMeansTrainer_getseters[] = {
-  {
-   initialization_method.name(),
-   (getter)PyBobLearnEMKMeansTrainer_getInitializationMethod,
-   (setter)PyBobLearnEMKMeansTrainer_setInitializationMethod,
-   initialization_method.doc(),
-   0
-  },
-  {
-   zeroeth_order_statistics.name(),
-   (getter)PyBobLearnEMKMeansTrainer_getZeroethOrderStatistics,
-   (setter)PyBobLearnEMKMeansTrainer_setZeroethOrderStatistics,
-   zeroeth_order_statistics.doc(),
-   0
-  },
-  {
-   first_order_statistics.name(),
-   (getter)PyBobLearnEMKMeansTrainer_getFirstOrderStatistics,
-   (setter)PyBobLearnEMKMeansTrainer_setFirstOrderStatistics,
-   first_order_statistics.doc(),
-   0
-  },
-  {
-   average_min_distance.name(),
-   (getter)PyBobLearnEMKMeansTrainer_getAverageMinDistance,
-   (setter)PyBobLearnEMKMeansTrainer_setAverageMinDistance,
-   average_min_distance.doc(),
-   0
-  },
-  {0}  // Sentinel
-};
-
-
-/******************************************************************/
-/************ Functions Section ***********************************/
-/******************************************************************/
-
-/*** initialize ***/
-static auto initialize = bob::extension::FunctionDoc(
-  "initialize",
-  "Initialise the means randomly",
-  "Data is split into as many chunks as there are means, then each mean is set to a random example within each chunk.",
-  true
-)
-.add_prototype("kmeans_machine, data, [rng]")
-.add_parameter("kmeans_machine", ":py:class:`bob.learn.em.KMeansMachine`", "KMeansMachine Object")
-.add_parameter("data", "array_like <float, 2D>", "Input data")
-.add_parameter("rng", ":py:class:`bob.core.random.mt19937`", "The Mersenne Twister mt19937 random generator used for the initialization of subspaces/arrays before the EM loop.");
-static PyObject* PyBobLearnEMKMeansTrainer_initialize(PyBobLearnEMKMeansTrainerObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  /* Parses input arguments in a single shot */
-  char** kwlist = initialize.kwlist(0);
-
-  PyBobLearnEMKMeansMachineObject* kmeans_machine = 0;
-  PyBlitzArrayObject* data                        = 0;
-  PyBoostMt19937Object* rng = 0;
-
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O&|O!", kwlist, &PyBobLearnEMKMeansMachine_Type, &kmeans_machine,
-                                                                 &PyBlitzArray_Converter, &data,
-                                                                  &PyBoostMt19937_Type, &rng)) return 0;
-  auto data_ = make_safe(data);
-
-  // perform check on the input
-  if (data->type_num != NPY_FLOAT64){
-    PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, initialize.name());
-    return 0;
-  }
-
-  if (data->ndim != 2){
-    PyErr_Format(PyExc_TypeError, "`%s' only processes 2D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, initialize.name());
-    return 0;
-  }
-
-  if (data->shape[1] != (Py_ssize_t)kmeans_machine->cxx->getNInputs() ) {
-    PyErr_Format(PyExc_TypeError, "`%s' 2D `input` array should have the shape [N, %" PY_FORMAT_SIZE_T "d] not [N, %" PY_FORMAT_SIZE_T "d] for `%s`", Py_TYPE(self)->tp_name, kmeans_machine->cxx->getNInputs(), data->shape[1], initialize.name());
-    return 0;
-  }
-
-  if(rng){
-    self->cxx->setRng(rng->rng);
-  }
-
-  self->cxx->initialize(*kmeans_machine->cxx, *PyBlitzArrayCxx_AsBlitz<double,2>(data));
-
-  BOB_CATCH_MEMBER("cannot perform the initialize method", 0)
-
-  Py_RETURN_NONE;
-}
-
-
-/*** e_step ***/
-static auto e_step = bob::extension::FunctionDoc(
-  "e_step",
-  "Compute the E-step, which is basically the distances ",
-  "Accumulate across the dataset:"
-  " -zeroeth and first order statistics"
-  " -average (Square Euclidean) distance from the closest mean",
-  true
-)
-.add_prototype("kmeans_machine,data")
-.add_parameter("kmeans_machine", ":py:class:`bob.learn.em.KMeansMachine`", "KMeansMachine Object")
-.add_parameter("data", "array_like <float, 2D>", "Input data");
-static PyObject* PyBobLearnEMKMeansTrainer_e_step(PyBobLearnEMKMeansTrainerObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  /* Parses input arguments in a single shot */
-  char** kwlist = e_step.kwlist(0);
-
-  PyBobLearnEMKMeansMachineObject* kmeans_machine;
-  PyBlitzArrayObject* data = 0;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O&", kwlist, &PyBobLearnEMKMeansMachine_Type, &kmeans_machine,
-                                                                 &PyBlitzArray_Converter, &data)) return 0;
-  auto data_ = make_safe(data);
-
-  if (data->type_num != NPY_FLOAT64){
-    PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, e_step.name());
-    return 0;
-  }
-
-  if (data->ndim != 2){
-    PyErr_Format(PyExc_TypeError, "`%s' only processes 2D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, e_step.name());
-    return 0;
-  }
-
-  if (data->shape[1] != (Py_ssize_t)kmeans_machine->cxx->getNInputs() ) {
-    PyErr_Format(PyExc_TypeError, "`%s' 2D `input` array should have the shape [N, %" PY_FORMAT_SIZE_T "d] not [N, %" PY_FORMAT_SIZE_T "d] for `%s`", Py_TYPE(self)->tp_name, kmeans_machine->cxx->getNInputs(), data->shape[1], e_step.name());
-    return 0;
-  }
-
-  auto state = PyEval_SaveThread();
-  self->cxx->eStep(*kmeans_machine->cxx, *PyBlitzArrayCxx_AsBlitz<double,2>(data));
-  PyEval_RestoreThread(state);
-
-  BOB_CATCH_MEMBER("cannot perform the e_step method", 0)
-
-  Py_RETURN_NONE;
-}
-
-
-/*** m_step ***/
-static auto m_step = bob::extension::FunctionDoc(
-  "m_step",
-  "Updates the mean based on the statistics from the E-step",
-  0,
-  true
-)
-.add_prototype("kmeans_machine, [data]")
-.add_parameter("kmeans_machine", ":py:class:`bob.learn.em.KMeansMachine`", "KMeansMachine Object")
-.add_parameter("data", "object", "Ignored.");
-static PyObject* PyBobLearnEMKMeansTrainer_m_step(PyBobLearnEMKMeansTrainerObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  /* Parses input arguments in a single shot */
-  char** kwlist = m_step.kwlist(0);
-
-  PyBobLearnEMKMeansMachineObject* kmeans_machine;
-  PyObject* data = 0;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!|O", kwlist, &PyBobLearnEMKMeansMachine_Type, &kmeans_machine,
-                                                                 &data)) return 0;
-  self->cxx->mStep(*kmeans_machine->cxx);
-
-  BOB_CATCH_MEMBER("cannot perform the m_step method", 0)
-
-  Py_RETURN_NONE;
-}
-
-
-/*** computeLikelihood ***/
-static auto compute_likelihood = bob::extension::FunctionDoc(
-  "compute_likelihood",
-  "This functions returns the average min (Square Euclidean) distance (average distance to the closest mean)",
-  0,
-  true
-)
-.add_prototype("kmeans_machine")
-.add_parameter("kmeans_machine", ":py:class:`bob.learn.em.KMeansMachine`", "KMeansMachine Object");
-static PyObject* PyBobLearnEMKMeansTrainer_compute_likelihood(PyBobLearnEMKMeansTrainerObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  /* Parses input arguments in a single shot */
-  char** kwlist = compute_likelihood.kwlist(0);
-
-  PyBobLearnEMKMeansMachineObject* kmeans_machine;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", kwlist, &PyBobLearnEMKMeansMachine_Type, &kmeans_machine)) return 0;
-
-  double value = self->cxx->computeLikelihood(*kmeans_machine->cxx);
-  return Py_BuildValue("d", value);
-
-  BOB_CATCH_MEMBER("cannot perform the computeLikelihood method", 0)
-}
-
-
-/*** reset_accumulators ***/
-static auto reset_accumulators = bob::extension::FunctionDoc(
-  "reset_accumulators",
-  "Reset the statistics accumulators to the correct size and a value of zero.",
-  0,
-  true
-)
-.add_prototype("kmeans_machine")
-.add_parameter("kmeans_machine", ":py:class:`bob.learn.em.KMeansMachine`", "KMeansMachine Object");
-static PyObject* PyBobLearnEMKMeansTrainer_reset_accumulators(PyBobLearnEMKMeansTrainerObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  /* Parses input arguments in a single shot */
-  char** kwlist = reset_accumulators.kwlist(0);
-
-  PyBobLearnEMKMeansMachineObject* kmeans_machine;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", kwlist, &PyBobLearnEMKMeansMachine_Type, &kmeans_machine)) return 0;
-
-  self->cxx->resetAccumulators(*kmeans_machine->cxx);
-  Py_RETURN_NONE;
-
-  BOB_CATCH_MEMBER("cannot perform the reset_accumulators method", 0)
-}
-
-
-static PyMethodDef PyBobLearnEMKMeansTrainer_methods[] = {
-  {
-    initialize.name(),
-    (PyCFunction)PyBobLearnEMKMeansTrainer_initialize,
-    METH_VARARGS|METH_KEYWORDS,
-    initialize.doc()
-  },
-  {
-    e_step.name(),
-    (PyCFunction)PyBobLearnEMKMeansTrainer_e_step,
-    METH_VARARGS|METH_KEYWORDS,
-    e_step.doc()
-  },
-  {
-    m_step.name(),
-    (PyCFunction)PyBobLearnEMKMeansTrainer_m_step,
-    METH_VARARGS|METH_KEYWORDS,
-    m_step.doc()
-  },
-  {
-    compute_likelihood.name(),
-    (PyCFunction)PyBobLearnEMKMeansTrainer_compute_likelihood,
-    METH_VARARGS|METH_KEYWORDS,
-    compute_likelihood.doc()
-  },
-  {
-    reset_accumulators.name(),
-    (PyCFunction)PyBobLearnEMKMeansTrainer_reset_accumulators,
-    METH_VARARGS|METH_KEYWORDS,
-    reset_accumulators.doc()
-  },
-  {0} /* Sentinel */
-};
-
-
-/******************************************************************/
-/************ Module Section **************************************/
-/******************************************************************/
-
-// Define the Gaussian type struct; will be initialized later
-PyTypeObject PyBobLearnEMKMeansTrainer_Type = {
-  PyVarObject_HEAD_INIT(0,0)
-  0
-};
-
-bool init_BobLearnEMKMeansTrainer(PyObject* module)
-{
-  // initialize the type struct
-  PyBobLearnEMKMeansTrainer_Type.tp_name = KMeansTrainer_doc.name();
-  PyBobLearnEMKMeansTrainer_Type.tp_basicsize = sizeof(PyBobLearnEMKMeansTrainerObject);
-  PyBobLearnEMKMeansTrainer_Type.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE;//Enable the class inheritance
-  PyBobLearnEMKMeansTrainer_Type.tp_doc = KMeansTrainer_doc.doc();
-
-  // set the functions
-  PyBobLearnEMKMeansTrainer_Type.tp_new = PyType_GenericNew;
-  PyBobLearnEMKMeansTrainer_Type.tp_init = reinterpret_cast<initproc>(PyBobLearnEMKMeansTrainer_init);
-  PyBobLearnEMKMeansTrainer_Type.tp_dealloc = reinterpret_cast<destructor>(PyBobLearnEMKMeansTrainer_delete);
-  PyBobLearnEMKMeansTrainer_Type.tp_richcompare = reinterpret_cast<richcmpfunc>(PyBobLearnEMKMeansTrainer_RichCompare);
-  PyBobLearnEMKMeansTrainer_Type.tp_methods = PyBobLearnEMKMeansTrainer_methods;
-  PyBobLearnEMKMeansTrainer_Type.tp_getset = PyBobLearnEMKMeansTrainer_getseters;
-  PyBobLearnEMKMeansTrainer_Type.tp_call = reinterpret_cast<ternaryfunc>(PyBobLearnEMKMeansTrainer_compute_likelihood);
-
-
-  // check that everything is fine
-  if (PyType_Ready(&PyBobLearnEMKMeansTrainer_Type) < 0) return false;
-
-  // add the type to the module
-  Py_INCREF(&PyBobLearnEMKMeansTrainer_Type);
-  return PyModule_AddObject(module, "KMeansTrainer", (PyObject*)&PyBobLearnEMKMeansTrainer_Type) >= 0;
-}
diff --git a/bob/learn/em/linear_scoring.cpp b/bob/learn/em/linear_scoring.cpp
deleted file mode 100644
index f29b59370b12ef35635c78ec6a267dec756f8ac9..0000000000000000000000000000000000000000
--- a/bob/learn/em/linear_scoring.cpp
+++ /dev/null
@@ -1,266 +0,0 @@
-/**
- * @author Tiago de Freitas Pereira <tiago.pereira@idiap.ch>
- * @date Wed 05 Feb 16:10:48 2015
- *
- * @brief Python API for bob::learn::em
- *
- * Copyright (C) 2011-2014 Idiap Research Institute, Martigny, Switzerland
- */
-
-#include "main.h"
-
-/*Convert a PyObject to a a list of GMMStats*/
-//template<class R, class P1, class P2>
-static int extract_gmmstats_list(PyObject *list,
-                             std::vector<boost::shared_ptr<const bob::learn::em::GMMStats> >& training_data)
-{
-  for (int i=0; i<PyList_GET_SIZE(list); i++){
-
-    PyBobLearnEMGMMStatsObject* stats;
-    if (!PyArg_Parse(PyList_GetItem(list, i), "O!", &PyBobLearnEMGMMStats_Type, &stats)){
-      PyErr_Format(PyExc_RuntimeError, "Expected GMMStats objects");
-      return -1;
-    }
-    training_data.push_back(stats->cxx);
-  }
-  return 0;
-}
-
-static int extract_gmmmachine_list(PyObject *list,
-                             std::vector<boost::shared_ptr<const bob::learn::em::GMMMachine> >& training_data)
-{
-  for (int i=0; i<PyList_GET_SIZE(list); i++){
-
-    PyBobLearnEMGMMMachineObject* stats;
-    if (!PyArg_Parse(PyList_GetItem(list, i), "O!", &PyBobLearnEMGMMMachine_Type, &stats)){
-      PyErr_Format(PyExc_RuntimeError, "Expected GMMMachine objects");
-      return -1;
-    }
-    training_data.push_back(stats->cxx);
-  }
-  return 0;
-}
-
-
-
-/*Convert a PyObject to a list of blitz Array*/
-template <int N>
-int extract_array_list(PyObject* list, std::vector<blitz::Array<double,N> >& vec)
-{
-
-  if(list==0)
-    return 0;
-
-  for (int i=0; i<PyList_GET_SIZE(list); i++)
-  {
-    PyBlitzArrayObject* blitz_object;
-    if (!PyArg_Parse(PyList_GetItem(list, i), "O&", &PyBlitzArray_Converter, &blitz_object)){
-      PyErr_Format(PyExc_RuntimeError, "Expected numpy array object");
-      return -1;
-    }
-    auto blitz_object_ = make_safe(blitz_object);
-    vec.push_back(*PyBlitzArrayCxx_AsBlitz<double,N>(blitz_object));
-  }
-  return 0;
-}
-
-/* converts PyObject to bool and returns false if object is NULL */
-static inline bool f(PyObject* o){return o != 0 && PyObject_IsTrue(o) > 0;}
-
-
-/*** linear_scoring ***/
-bob::extension::FunctionDoc linear_scoring1 = bob::extension::FunctionDoc(
-  "linear_scoring",
-  "The :ref:`Linear scoring <linearscoring>` is an approximation to the log-likelihood ratio that was shown to be as accurate and up to two orders of magnitude more efficient to compute [Glembek2009]_."
-  "",
-  0,
-  true
-)
-.add_prototype("models, ubm, test_stats, test_channelOffset, frame_length_normalisation", "output")
-.add_parameter("models", "[:py:class:`bob.learn.em.GMMMachine`]", "")
-.add_parameter("ubm", ":py:class:`bob.learn.em.GMMMachine`", "")
-.add_parameter("test_stats", "[:py:class:`bob.learn.em.GMMStats`]", "")
-.add_parameter("test_channelOffset", "[array_like<float,1>]", "")
-.add_parameter("frame_length_normalisation", "bool", "")
-.add_return("output","array_like<float,1>","Score");
-
-
-bob::extension::FunctionDoc linear_scoring2 = bob::extension::FunctionDoc(
-  "linear_scoring",
-  "",
-  0,
-  true
-)
-.add_prototype("models, ubm_mean, ubm_variance, test_stats, test_channelOffset, frame_length_normalisation", "output")
-.add_parameter("models", "list(array_like<float,1>)", "")
-.add_parameter("ubm_mean", "list(array_like<float,1>)", "")
-.add_parameter("ubm_variance", "list(array_like<float,1>)", "")
-.add_parameter("test_stats", "list(:py:class:`bob.learn.em.GMMStats`)", "")
-.add_parameter("test_channelOffset", "list(array_like<float,1>)", "")
-.add_parameter("frame_length_normalisation", "bool", "")
-.add_return("output","array_like<float,1>","Score");
-
-
-
-bob::extension::FunctionDoc linear_scoring3 = bob::extension::FunctionDoc(
-  "linear_scoring",
-  "",
-  0,
-  true
-)
-.add_prototype("model, ubm_mean, ubm_variance, test_stats, test_channelOffset, frame_length_normalisation", "output")
-.add_parameter("model", "array_like<float,1>", "")
-.add_parameter("ubm_mean", "array_like<float,1>", "")
-.add_parameter("ubm_variance", "array_like<float,1>", "")
-.add_parameter("test_stats", ":py:class:`bob.learn.em.GMMStats`", "")
-.add_parameter("test_channelOffset", "array_like<float,1>", "")
-.add_parameter("frame_length_normalisation", "bool", "")
-.add_return("output","array_like<float,1>","Score");
-
-PyObject* PyBobLearnEM_linear_scoring(PyObject*, PyObject* args, PyObject* kwargs) {
-
-  //Cheking the number of arguments
-  int nargs = (args?PyTuple_Size(args):0) + (kwargs?PyDict_Size(kwargs):0);
-
-  //Reading the first input argument
-  PyObject* arg = 0;
-  if (PyTuple_Size(args))
-    arg = PyTuple_GET_ITEM(args, 0);
-  else {
-    PyObject* tmp = PyDict_Values(kwargs);
-    auto tmp_ = make_safe(tmp);
-    arg = PyList_GET_ITEM(tmp, 0);
-  }
-
-  //Checking the signature of the method (list of GMMMachine as input)
-  if ((PyList_Check(arg)) && PyBobLearnEMGMMMachine_Check(PyList_GetItem(arg, 0)) && (nargs >= 3) && (nargs<=5) ){
-
-    char** kwlist = linear_scoring1.kwlist(0);
-
-    PyObject* gmm_list_o                 = 0;
-    PyBobLearnEMGMMMachineObject* ubm  = 0;
-    PyObject* stats_list_o               = 0;
-    PyObject* channel_offset_list_o      = 0;
-    PyObject* frame_length_normalisation = Py_False;
-
-    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O!O!|O!O!", kwlist, &PyList_Type, &gmm_list_o,
-                                                                       &PyBobLearnEMGMMMachine_Type, &ubm,
-                                                                       &PyList_Type, &stats_list_o,
-                                                                       &PyList_Type, &channel_offset_list_o,
-                                                                       &PyBool_Type, &frame_length_normalisation)){
-      linear_scoring1.print_usage();
-      return 0;
-    }
-
-    std::vector<boost::shared_ptr<const bob::learn::em::GMMStats> > stats_list;
-    if(extract_gmmstats_list(stats_list_o ,stats_list)!=0)
-      Py_RETURN_NONE;
-
-    std::vector<boost::shared_ptr<const bob::learn::em::GMMMachine> > gmm_list;
-    if(extract_gmmmachine_list(gmm_list_o ,gmm_list)!=0)
-      Py_RETURN_NONE;
-
-    std::vector<blitz::Array<double,1> > channel_offset_list;
-    if(extract_array_list(channel_offset_list_o ,channel_offset_list)!=0)
-      Py_RETURN_NONE;
-
-    blitz::Array<double, 2> scores = blitz::Array<double, 2>(gmm_list.size(), stats_list.size());
-    if(channel_offset_list.size()==0)
-      bob::learn::em::linearScoring(gmm_list, *ubm->cxx, stats_list, f(frame_length_normalisation),scores);
-    else
-      bob::learn::em::linearScoring(gmm_list, *ubm->cxx, stats_list, channel_offset_list, f(frame_length_normalisation),scores);
-
-    return PyBlitzArrayCxx_AsConstNumpy(scores);
-  }
-
-  //Checking the signature of the method (list of arrays as input
-  else if ((PyList_Check(arg)) && PyArray_Check(PyList_GetItem(arg, 0)) && (nargs >= 4) && (nargs<=6) ){
-
-    char** kwlist = linear_scoring2.kwlist(0);
-
-    PyObject* model_supervector_list_o        = 0;
-    PyBlitzArrayObject* ubm_means             = 0;
-    PyBlitzArrayObject* ubm_variances         = 0;
-    PyObject* stats_list_o                    = 0;
-    PyObject* channel_offset_list_o           = 0;
-    PyObject* frame_length_normalisation      = Py_False;
-
-    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O&O&O!|O!O!", kwlist, &PyList_Type, &model_supervector_list_o,
-                                                                       &PyBlitzArray_Converter, &ubm_means,
-                                                                       &PyBlitzArray_Converter, &ubm_variances,
-                                                                       &PyList_Type, &stats_list_o,
-                                                                       &PyList_Type, &channel_offset_list_o,
-                                                                       &PyBool_Type, &frame_length_normalisation)){
-      linear_scoring2.print_usage();
-      return 0;
-    }
-
-    //protects acquired resources through this scope
-    auto ubm_means_ = make_safe(ubm_means);
-    auto ubm_variances_ = make_safe(ubm_variances);
-
-    std::vector<blitz::Array<double,1> > model_supervector_list;
-    if(extract_array_list(model_supervector_list_o ,model_supervector_list)!=0)
-      Py_RETURN_NONE;
-
-    std::vector<boost::shared_ptr<const bob::learn::em::GMMStats> > stats_list;
-    if(extract_gmmstats_list(stats_list_o ,stats_list)!=0)
-      Py_RETURN_NONE;
-
-    std::vector<blitz::Array<double,1> > channel_offset_list;
-    if(extract_array_list(channel_offset_list_o ,channel_offset_list)!=0)
-      Py_RETURN_NONE;
-
-    blitz::Array<double, 2> scores = blitz::Array<double, 2>(model_supervector_list.size(), stats_list.size());
-    if(channel_offset_list.size()==0)
-      bob::learn::em::linearScoring(model_supervector_list, *PyBlitzArrayCxx_AsBlitz<double,1>(ubm_means),*PyBlitzArrayCxx_AsBlitz<double,1>(ubm_variances), stats_list, f(frame_length_normalisation),scores);
-    else
-      bob::learn::em::linearScoring(model_supervector_list, *PyBlitzArrayCxx_AsBlitz<double,1>(ubm_means),*PyBlitzArrayCxx_AsBlitz<double,1>(ubm_variances), stats_list, channel_offset_list, f(frame_length_normalisation),scores);
-
-    return PyBlitzArrayCxx_AsConstNumpy(scores);
-
-  }
-
-  //Checking the signature of the method (list of arrays as input
-  else if (PyArray_Check(arg) && (nargs >= 5) && (nargs<=6) ){
-
-    char** kwlist = linear_scoring3.kwlist(0);
-
-    PyBlitzArrayObject* model                 = 0;
-    PyBlitzArrayObject* ubm_means             = 0;
-    PyBlitzArrayObject* ubm_variances         = 0;
-    PyBobLearnEMGMMStatsObject* stats       = 0;
-    PyBlitzArrayObject* channel_offset        = 0;
-    PyObject* frame_length_normalisation      = Py_False;
-
-    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&O&O&O!O&|O!", kwlist, &PyBlitzArray_Converter, &model,
-                                                                       &PyBlitzArray_Converter, &ubm_means,
-                                                                       &PyBlitzArray_Converter, &ubm_variances,
-                                                                       &PyBobLearnEMGMMStats_Type, &stats,
-                                                                       &PyBlitzArray_Converter, &channel_offset,
-                                                                       &PyBool_Type, &frame_length_normalisation)){
-      linear_scoring3.print_usage();
-      return 0;
-    }
-
-    //protects acquired resources through this scope
-    auto model_ = make_safe(model);
-    auto ubm_means_ = make_safe(ubm_means);
-    auto ubm_variances_ = make_safe(ubm_variances);
-    auto channel_offset_ = make_safe(channel_offset);
-
-    double score = bob::learn::em::linearScoring(*PyBlitzArrayCxx_AsBlitz<double,1>(model), *PyBlitzArrayCxx_AsBlitz<double,1>(ubm_means),*PyBlitzArrayCxx_AsBlitz<double,1>(ubm_variances), *stats->cxx, *PyBlitzArrayCxx_AsBlitz<double,1>(channel_offset), f(frame_length_normalisation));
-
-    return Py_BuildValue("d",score);
-  }
-
-
-  else{
-    PyErr_Format(PyExc_RuntimeError, "number of arguments mismatch - linear_scoring requires 5 or 6 arguments, but you provided %d (see help)", nargs);
-    linear_scoring1.print_usage();
-    linear_scoring2.print_usage();
-    linear_scoring3.print_usage();
-    return 0;
-  }
-
-}
diff --git a/bob/learn/em/main.cpp b/bob/learn/em/main.cpp
deleted file mode 100644
index c2e7211f47fb44321375a2ffd56640c29c31f97c..0000000000000000000000000000000000000000
--- a/bob/learn/em/main.cpp
+++ /dev/null
@@ -1,124 +0,0 @@
-/**
- * @author Tiago de Freitas Pereira <tiago.pereira@idiap.ch>
- * @date Fri Nov 21 12:39:21 CET 2014
- *
- * @brief Bindings to bob::learn::em routines
- */
-
-#ifdef NO_IMPORT_ARRAY
-#undef NO_IMPORT_ARRAY
-#endif
-#include "main.h"
-
-static PyMethodDef module_methods[] = {
-  {
-    linear_scoring1.name(),
-    (PyCFunction)PyBobLearnEM_linear_scoring,
-    METH_VARARGS|METH_KEYWORDS,
-    linear_scoring1.doc()
-  },
-
-  {0}//Sentinel
-};
-
-
-PyDoc_STRVAR(module_docstr, "Bob EM based Machine Learning Routines");
-
-int PyBobLearnEM_APIVersion = BOB_LEARN_EM_API_VERSION;
-
-
-#if PY_VERSION_HEX >= 0x03000000
-static PyModuleDef module_definition = {
-  PyModuleDef_HEAD_INIT,
-  BOB_EXT_MODULE_NAME,
-  module_docstr,
-  -1,
-  module_methods,
-  0, 0, 0, 0
-};
-#endif
-
-static PyObject* create_module (void) {
-
-# if PY_VERSION_HEX >= 0x03000000
-  PyObject* module = PyModule_Create(&module_definition);
-  auto module_ = make_xsafe(module);
-  const char* ret = "O";
-# else
-  PyObject* module = Py_InitModule3(BOB_EXT_MODULE_NAME, module_methods, module_docstr);
-  const char* ret = "N";
-# endif
-  if (!module) return 0;
-
-  if (!init_BobLearnEMGaussian(module)) return 0;
-  if (!init_BobLearnEMGMMStats(module)) return 0;
-  if (!init_BobLearnEMGMMMachine(module)) return 0;
-  if (!init_BobLearnEMKMeansMachine(module)) return 0;
-  if (!init_BobLearnEMKMeansTrainer(module)) return 0;
-  if (!init_BobLearnEMMLGMMTrainer(module)) return 0;
-  if (!init_BobLearnEMMAPGMMTrainer(module)) return 0;
-
-  if (!init_BobLearnEMJFABase(module)) return 0;
-  if (!init_BobLearnEMJFAMachine(module)) return 0;
-  if (!init_BobLearnEMJFATrainer(module)) return 0;
-
-  if (!init_BobLearnEMISVBase(module)) return 0;
-  if (!init_BobLearnEMISVMachine(module)) return 0;
-  if (!init_BobLearnEMISVTrainer(module)) return 0;
-
-  if (!init_BobLearnEMIVectorMachine(module)) return 0;
-  if (!init_BobLearnEMIVectorTrainer(module)) return 0;
-
-  if (!init_BobLearnEMPLDABase(module)) return 0;
-  if (!init_BobLearnEMPLDAMachine(module)) return 0;
-  if (!init_BobLearnEMPLDATrainer(module)) return 0;
-
-  if (!init_BobLearnEMEMPCATrainer(module)) return 0;
-
-
-  static void* PyBobLearnEM_API[PyBobLearnEM_API_pointers];
-
-  /* exhaustive list of C APIs */
-
-  /**************
-   * Versioning *
-   **************/
-
-  PyBobLearnEM_API[PyBobLearnEM_APIVersion_NUM] = (void *)&PyBobLearnEM_APIVersion;
-
-
-#if PY_VERSION_HEX >= 0x02070000
-
-  /* defines the PyCapsule */
-
-  PyObject* c_api_object = PyCapsule_New((void *)PyBobLearnEM_API,
-      BOB_EXT_MODULE_PREFIX "." BOB_EXT_MODULE_NAME "._C_API", 0);
-
-#else
-
-  PyObject* c_api_object = PyCObject_FromVoidPtr((void *)PyBobLearnEM_API, 0);
-
-#endif
-
-  if (!c_api_object) return 0;
-
-  if (PyModule_AddObject(module, "_C_API", c_api_object) < 0) return 0;
-
-  /* imports bob.learn.em's C-API dependencies */
-  if (import_bob_blitz() < 0) return 0;
-  if (import_bob_core_logging() < 0) return 0;
-  if (import_bob_core_random() < 0) return 0;
-  if (import_bob_io_base() < 0) return 0;
-  if (import_bob_sp() < 0) return 0;
-  if (import_bob_learn_activation() < 0) return 0;
-  if (import_bob_learn_linear() < 0) return 0;
-
-  return Py_BuildValue(ret, module);
-}
-
-PyMODINIT_FUNC BOB_EXT_ENTRY_NAME (void) {
-# if PY_VERSION_HEX >= 0x03000000
-  return
-# endif
-    create_module();
-}
diff --git a/bob/learn/em/main.h b/bob/learn/em/main.h
deleted file mode 100644
index 7d2cb43eca159beb3b16bc03430a5c8dc9039584..0000000000000000000000000000000000000000
--- a/bob/learn/em/main.h
+++ /dev/null
@@ -1,274 +0,0 @@
-/**
- * @author Tiago de Freitas Pereira <tiago.pereira@idiap.ch>
- * @date Fri Nov 21 10:31:25 CET 2014
- *
- * @brief Header file for bindings to bob::learn::em
- */
-
-#ifndef BOB_LEARN_EM_MAIN_H
-#define BOB_LEARN_EM_MAIN_H
-
-#include <bob.blitz/cppapi.h>
-#include <bob.blitz/cleanup.h>
-#include <bob.core/api.h>
-#include <bob.core/random_api.h>
-#include <bob.io.base/api.h>
-#include <bob.sp/api.h>
-#include <bob.learn.activation/api.h>
-#include <bob.learn.linear/api.h>
-
-#include <bob.extension/documentation.h>
-
-#define BOB_LEARN_EM_MODULE
-#include <bob.learn.em/api.h>
-
-#include <bob.learn.em/Gaussian.h>
-#include <bob.learn.em/GMMStats.h>
-#include <bob.learn.em/GMMMachine.h>
-#include <bob.learn.em/KMeansMachine.h>
-
-#include <bob.learn.em/KMeansTrainer.h>
-//#include <bob.learn.em/GMMBaseTrainer.h>
-#include <bob.learn.em/ML_GMMTrainer.h>
-#include <bob.learn.em/MAP_GMMTrainer.h>
-
-#include <bob.learn.em/JFABase.h>
-#include <bob.learn.em/JFAMachine.h>
-#include <bob.learn.em/JFATrainer.h>
-
-#include <bob.learn.em/ISVBase.h>
-#include <bob.learn.em/ISVMachine.h>
-#include <bob.learn.em/ISVTrainer.h>
-
-
-#include <bob.learn.em/IVectorMachine.h>
-#include <bob.learn.em/IVectorTrainer.h>
-
-#include <bob.learn.em/EMPCATrainer.h>
-
-#include <bob.learn.em/PLDAMachine.h>
-#include <bob.learn.em/PLDATrainer.h>
-
-
-/// inserts the given key, value pair into the given dictionaries
-static inline int insert_item_string(PyObject* dict, PyObject* entries, const char* key, Py_ssize_t value){
-  auto v = make_safe(Py_BuildValue("n", value));
-  if (PyDict_SetItemString(dict, key, v.get()) < 0) return -1;
-  return PyDict_SetItemString(entries, key, v.get());
-}
-
-// Gaussian
-typedef struct {
-  PyObject_HEAD
-  boost::shared_ptr<bob::learn::em::Gaussian> cxx;
-} PyBobLearnEMGaussianObject;
-
-extern PyTypeObject PyBobLearnEMGaussian_Type;
-bool init_BobLearnEMGaussian(PyObject* module);
-int PyBobLearnEMGaussian_Check(PyObject* o);
-
-
-// GMMStats
-typedef struct {
-  PyObject_HEAD
-  boost::shared_ptr<bob::learn::em::GMMStats> cxx;
-} PyBobLearnEMGMMStatsObject;
-
-extern PyTypeObject PyBobLearnEMGMMStats_Type;
-bool init_BobLearnEMGMMStats(PyObject* module);
-int PyBobLearnEMGMMStats_Check(PyObject* o);
-
-
-// GMMMachine
-typedef struct {
-  PyObject_HEAD
-  boost::shared_ptr<bob::learn::em::GMMMachine> cxx;
-} PyBobLearnEMGMMMachineObject;
-
-extern PyTypeObject PyBobLearnEMGMMMachine_Type;
-bool init_BobLearnEMGMMMachine(PyObject* module);
-int PyBobLearnEMGMMMachine_Check(PyObject* o);
-
-
-// KMeansMachine
-typedef struct {
-  PyObject_HEAD
-  boost::shared_ptr<bob::learn::em::KMeansMachine> cxx;
-} PyBobLearnEMKMeansMachineObject;
-
-extern PyTypeObject PyBobLearnEMKMeansMachine_Type;
-bool init_BobLearnEMKMeansMachine(PyObject* module);
-int PyBobLearnEMKMeansMachine_Check(PyObject* o);
-
-
-// KMeansTrainer
-typedef struct {
-  PyObject_HEAD
-  boost::shared_ptr<bob::learn::em::KMeansTrainer> cxx;
-} PyBobLearnEMKMeansTrainerObject;
-
-extern PyTypeObject PyBobLearnEMKMeansTrainer_Type;
-bool init_BobLearnEMKMeansTrainer(PyObject* module);
-int PyBobLearnEMKMeansTrainer_Check(PyObject* o);
-
-
-// ML_GMMTrainer
-typedef struct {
-  PyObject_HEAD
-  boost::shared_ptr<bob::learn::em::ML_GMMTrainer> cxx;
-} PyBobLearnEMMLGMMTrainerObject;
-
-extern PyTypeObject PyBobLearnEMMLGMMTrainer_Type;
-bool init_BobLearnEMMLGMMTrainer(PyObject* module);
-int PyBobLearnEMMLGMMTrainer_Check(PyObject* o);
-
-
-// MAP_GMMTrainer
-typedef struct {
-  PyObject_HEAD
-  boost::shared_ptr<bob::learn::em::MAP_GMMTrainer> cxx;
-} PyBobLearnEMMAPGMMTrainerObject;
-
-extern PyTypeObject PyBobLearnEMMAPGMMTrainer_Type;
-bool init_BobLearnEMMAPGMMTrainer(PyObject* module);
-int PyBobLearnEMMAPGMMTrainer_Check(PyObject* o);
-
-
-// JFABase
-typedef struct {
-  PyObject_HEAD
-  boost::shared_ptr<bob::learn::em::JFABase> cxx;
-} PyBobLearnEMJFABaseObject;
-
-extern PyTypeObject PyBobLearnEMJFABase_Type;
-bool init_BobLearnEMJFABase(PyObject* module);
-int PyBobLearnEMJFABase_Check(PyObject* o);
-
-
-// ISVBase
-typedef struct {
-  PyObject_HEAD
-  boost::shared_ptr<bob::learn::em::ISVBase> cxx;
-} PyBobLearnEMISVBaseObject;
-
-extern PyTypeObject PyBobLearnEMISVBase_Type;
-bool init_BobLearnEMISVBase(PyObject* module);
-int PyBobLearnEMISVBase_Check(PyObject* o);
-
-
-// JFAMachine
-typedef struct {
-  PyObject_HEAD
-  boost::shared_ptr<bob::learn::em::JFAMachine> cxx;
-} PyBobLearnEMJFAMachineObject;
-
-extern PyTypeObject PyBobLearnEMJFAMachine_Type;
-bool init_BobLearnEMJFAMachine(PyObject* module);
-int PyBobLearnEMJFAMachine_Check(PyObject* o);
-
-// JFATrainer
-typedef struct {
-  PyObject_HEAD
-  boost::shared_ptr<bob::learn::em::JFATrainer> cxx;
-} PyBobLearnEMJFATrainerObject;
-
-
-extern PyTypeObject PyBobLearnEMJFATrainer_Type;
-bool init_BobLearnEMJFATrainer(PyObject* module);
-int PyBobLearnEMJFATrainer_Check(PyObject* o);
-
-// ISVMachine
-typedef struct {
-  PyObject_HEAD
-  boost::shared_ptr<bob::learn::em::ISVMachine> cxx;
-} PyBobLearnEMISVMachineObject;
-
-extern PyTypeObject PyBobLearnEMISVMachine_Type;
-bool init_BobLearnEMISVMachine(PyObject* module);
-int PyBobLearnEMISVMachine_Check(PyObject* o);
-
-// ISVTrainer
-typedef struct {
-  PyObject_HEAD
-  boost::shared_ptr<bob::learn::em::ISVTrainer> cxx;
-} PyBobLearnEMISVTrainerObject;
-
-extern PyTypeObject PyBobLearnEMISVTrainer_Type;
-bool init_BobLearnEMISVTrainer(PyObject* module);
-int PyBobLearnEMISVTrainer_Check(PyObject* o);
-
-// IVectorMachine
-typedef struct {
-  PyObject_HEAD
-  boost::shared_ptr<bob::learn::em::IVectorMachine> cxx;
-} PyBobLearnEMIVectorMachineObject;
-
-extern PyTypeObject PyBobLearnEMIVectorMachine_Type;
-bool init_BobLearnEMIVectorMachine(PyObject* module);
-int PyBobLearnEMIVectorMachine_Check(PyObject* o);
-
-
-// IVectorTrainer
-typedef struct {
-  PyObject_HEAD
-  boost::shared_ptr<bob::learn::em::IVectorTrainer> cxx;
-} PyBobLearnEMIVectorTrainerObject;
-
-extern PyTypeObject PyBobLearnEMIVectorTrainer_Type;
-bool init_BobLearnEMIVectorTrainer(PyObject* module);
-int PyBobLearnEMIVectorTrainer_Check(PyObject* o);
-
-
-// PLDABase
-typedef struct {
-  PyObject_HEAD
-  boost::shared_ptr<bob::learn::em::PLDABase> cxx;
-} PyBobLearnEMPLDABaseObject;
-
-extern PyTypeObject PyBobLearnEMPLDABase_Type;
-bool init_BobLearnEMPLDABase(PyObject* module);
-int PyBobLearnEMPLDABase_Check(PyObject* o);
-
-
-// PLDAMachine
-typedef struct {
-  PyObject_HEAD
-  boost::shared_ptr<bob::learn::em::PLDAMachine> cxx;
-} PyBobLearnEMPLDAMachineObject;
-
-extern PyTypeObject PyBobLearnEMPLDAMachine_Type;
-bool init_BobLearnEMPLDAMachine(PyObject* module);
-int PyBobLearnEMPLDAMachine_Check(PyObject* o);
-
-
-// PLDATrainer
-typedef struct {
-  PyObject_HEAD
-  boost::shared_ptr<bob::learn::em::PLDATrainer> cxx;
-} PyBobLearnEMPLDATrainerObject;
-
-extern PyTypeObject PyBobLearnEMPLDATrainer_Type;
-bool init_BobLearnEMPLDATrainer(PyObject* module);
-int PyBobLearnEMPLDATrainer_Check(PyObject* o);
-
-
-
-// EMPCATrainer
-typedef struct {
-  PyObject_HEAD
-  boost::shared_ptr<bob::learn::em::EMPCATrainer> cxx;
-} PyBobLearnEMEMPCATrainerObject;
-
-extern PyTypeObject PyBobLearnEMEMPCATrainer_Type;
-bool init_BobLearnEMEMPCATrainer(PyObject* module);
-int PyBobLearnEMEMPCATrainer_Check(PyObject* o);
-
-
-
-//Linear scoring
-PyObject* PyBobLearnEM_linear_scoring(PyObject*, PyObject* args, PyObject* kwargs);
-extern bob::extension::FunctionDoc linear_scoring1;
-extern bob::extension::FunctionDoc linear_scoring2;
-extern bob::extension::FunctionDoc linear_scoring3;
-
-#endif // BOB_LEARN_EM_MAIN_H
diff --git a/bob/learn/em/map_gmm_trainer.cpp b/bob/learn/em/map_gmm_trainer.cpp
deleted file mode 100644
index 5616892a52878ff8077b458bb80ee46d820e84b9..0000000000000000000000000000000000000000
--- a/bob/learn/em/map_gmm_trainer.cpp
+++ /dev/null
@@ -1,508 +0,0 @@
-/**
- * @author Tiago de Freitas Pereira <tiago.pereira@idiap.ch>
- * @date Web 23 Jan 16:42:00 2015
- *
- * @brief Python API for bob::learn::em
- *
- * Copyright (C) 2011-2014 Idiap Research Institute, Martigny, Switzerland
- */
-
-#include "main.h"
-
-/******************************************************************/
-/************ Constructor Section *********************************/
-/******************************************************************/
-
-static inline bool f(PyObject* o){return o != 0 && PyObject_IsTrue(o) > 0;}  /* converts PyObject to bool and returns false if object is NULL */
-
-static auto MAP_GMMTrainer_doc = bob::extension::ClassDoc(
-  BOB_EXT_MODULE_PREFIX ".MAP_GMMTrainer",
-  "This class implements the maximum a posteriori (:ref:`MAP <map>`) M-step of the expectation-maximization algorithm for a GMM Machine. The prior parameters are encoded in the form of a GMM (e.g. a universal background model). The EM algorithm thus performs GMM adaptation."
-).add_constructor(
-  bob::extension::FunctionDoc(
-    "__init__",
-    "Creates a MAP_GMMTrainer",
-    "Additionally to the copy constructor, there are two different ways to call this constructor, one using the ``relevance_factor`` and one using the ``alpha``, both which have the same signature. "
-    "Hence, the only way to differentiate the two functions is by using keyword arguments.",
-    true
-  )
-
-  .add_prototype("prior_gmm, relevance_factor, [update_means], [update_variances], [update_weights], [mean_var_update_responsibilities_threshold]","")
-  .add_prototype("prior_gmm, alpha, [update_means], [update_variances], [update_weights], [mean_var_update_responsibilities_threshold]","")
-  .add_prototype("other","")
-
-  .add_parameter("prior_gmm", ":py:class:`bob.learn.em.GMMMachine`", "The prior GMM to be adapted (Universal Background Model UBM).")
-  .add_parameter("relevance_factor", "float", "If set the Reynolds Adaptation procedure will be  applied. See Eq (14) from [Reynolds2000]_")
-  .add_parameter("alpha", "float", "Set directly the alpha parameter (Eq (14) from [Reynolds2000]_), ignoring zeroth order statistics as a weighting factor.")
-
-  .add_parameter("update_means", "bool", "[Default: ``True``] Update means on each iteration")
-  .add_parameter("update_variances", "bool", "[Default: ``True``] Update variances on each iteration")
-  .add_parameter("update_weights", "bool", "[Default: ``True``] Update weights on each iteration")
-  .add_parameter("mean_var_update_responsibilities_threshold", "float", "[Default: min_float] Threshold over the responsibilities of the Gaussians Equations 9.24, 9.25 of Bishop, `Pattern recognition and machine learning`, 2006 require a division by the responsibilities, which might be equal to zero because of numerical issue. This threshold is used to avoid such divisions.")
-
-  .add_parameter("other", ":py:class:`bob.learn.em.MAP_GMMTrainer`", "A MAP_GMMTrainer object to be copied.")
-);
-
-
-static int PyBobLearnEMMAPGMMTrainer_init_copy(PyBobLearnEMMAPGMMTrainerObject* self, PyObject* args, PyObject* kwargs) {
-
-  char** kwlist = MAP_GMMTrainer_doc.kwlist(2);
-  PyBobLearnEMMAPGMMTrainerObject* o;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", kwlist, &PyBobLearnEMMAPGMMTrainer_Type, &o)){
-    MAP_GMMTrainer_doc.print_usage();
-    return -1;
-  }
-
-  self->cxx.reset(new bob::learn::em::MAP_GMMTrainer(*o->cxx));
-  return 0;
-}
-
-
-static int PyBobLearnEMMAPGMMTrainer_init_base_trainer(PyBobLearnEMMAPGMMTrainerObject* self, PyObject* args, PyObject* kwargs) {
-
-  char** kwlist1 = MAP_GMMTrainer_doc.kwlist(0);
-  char** kwlist2 = MAP_GMMTrainer_doc.kwlist(1);
-
-  PyBobLearnEMGMMMachineObject* gmm_machine;
-  bool reynolds_adaptation   = false;
-  double alpha = 0.5;
-  double relevance_factor = 4.0;
-  double aux = 0;
-
-  PyObject* update_means     = Py_True;
-  PyObject* update_variances = Py_False;
-  PyObject* update_weights   = Py_False;
-  double mean_var_update_responsibilities_threshold = std::numeric_limits<double>::epsilon();
-
-  PyObject* keyword_relevance_factor = Py_BuildValue("s", kwlist1[1]);
-  PyObject* keyword_alpha            = Py_BuildValue("s", kwlist2[1]);
-
-  auto keyword_relevance_factor_ = make_safe(keyword_relevance_factor);
-  auto keyword_alpha_            = make_safe(keyword_alpha);
-
-  //Here we have to select which keyword argument to read
-  if (kwargs && PyDict_Contains(kwargs, keyword_relevance_factor)){
-    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!d|O!O!O!d", kwlist1,
-          &PyBobLearnEMGMMMachine_Type, &gmm_machine,
-          &aux,
-          &PyBool_Type, &update_means,
-          &PyBool_Type, &update_variances,
-          &PyBool_Type, &update_weights,
-          &mean_var_update_responsibilities_threshold))
-      return -1;
-    reynolds_adaptation = true;
-  } else if (kwargs && PyDict_Contains(kwargs, keyword_alpha)){
-    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!d|O!O!O!d", kwlist2,
-          &PyBobLearnEMGMMMachine_Type, &gmm_machine,
-          &aux,
-          &PyBool_Type, &update_means,
-          &PyBool_Type, &update_variances,
-          &PyBool_Type, &update_weights,
-          &mean_var_update_responsibilities_threshold))
-      return -1;
-    reynolds_adaptation = false;
-  } else {
-    PyErr_Format(PyExc_RuntimeError, "%s. One of the two keyword arguments '%s' or '%s' must be present.", Py_TYPE(self)->tp_name, kwlist1[1], kwlist2[1]);
-    MAP_GMMTrainer_doc.print_usage();
-    return -1;
-  }
-
-  if (reynolds_adaptation)
-    relevance_factor = aux;
-  else
-    alpha = aux;
-
-
-  self->cxx.reset(new bob::learn::em::MAP_GMMTrainer(f(update_means), f(update_variances), f(update_weights),
-                                                       mean_var_update_responsibilities_threshold,
-                                                       reynolds_adaptation, relevance_factor, alpha, gmm_machine->cxx));
-  return 0;
-
-}
-
-
-
-static int PyBobLearnEMMAPGMMTrainer_init(PyBobLearnEMMAPGMMTrainerObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  // get the number of command line arguments
-  int nargs = (args?PyTuple_Size(args):0) + (kwargs?PyDict_Size(kwargs):0);
-
-  if (nargs==0){
-    PyErr_Format(PyExc_RuntimeError, "number of arguments mismatch - %s (see help)", Py_TYPE(self)->tp_name);
-    MAP_GMMTrainer_doc.print_usage();
-    return -1;
-  }
-
-  //Reading the input argument
-  PyObject* arg = 0;
-  if (PyTuple_Size(args))
-    arg = PyTuple_GET_ITEM(args, 0);
-  else {
-    PyObject* tmp = PyDict_Values(kwargs);
-    auto tmp_ = make_safe(tmp);
-    arg = PyList_GET_ITEM(tmp, 0);
-  }
-
-  // If the constructor input is GMMBaseTrainer object
-  if(PyBobLearnEMMAPGMMTrainer_Check(arg))
-    return PyBobLearnEMMAPGMMTrainer_init_copy(self, args, kwargs);
-  else{
-    return PyBobLearnEMMAPGMMTrainer_init_base_trainer(self, args, kwargs);
-  }
-
-  BOB_CATCH_MEMBER("cannot create MAP_GMMTrainer", -1)
-  return 0;
-}
-
-
-static void PyBobLearnEMMAPGMMTrainer_delete(PyBobLearnEMMAPGMMTrainerObject* self) {
-  self->cxx.reset();
-  Py_TYPE(self)->tp_free((PyObject*)self);
-}
-
-
-int PyBobLearnEMMAPGMMTrainer_Check(PyObject* o) {
-  return PyObject_IsInstance(o, reinterpret_cast<PyObject*>(&PyBobLearnEMMAPGMMTrainer_Type));
-}
-
-
-static PyObject* PyBobLearnEMMAPGMMTrainer_RichCompare(PyBobLearnEMMAPGMMTrainerObject* self, PyObject* other, int op) {
-  BOB_TRY
-
-  if (!PyBobLearnEMMAPGMMTrainer_Check(other)) {
-    PyErr_Format(PyExc_TypeError, "cannot compare `%s' with `%s'", Py_TYPE(self)->tp_name, Py_TYPE(other)->tp_name);
-    return 0;
-  }
-  auto other_ = reinterpret_cast<PyBobLearnEMMAPGMMTrainerObject*>(other);
-  switch (op) {
-    case Py_EQ:
-      if (*self->cxx==*other_->cxx) Py_RETURN_TRUE; else Py_RETURN_FALSE;
-    case Py_NE:
-      if (*self->cxx==*other_->cxx) Py_RETURN_FALSE; else Py_RETURN_TRUE;
-    default:
-      Py_INCREF(Py_NotImplemented);
-      return Py_NotImplemented;
-  }
-  BOB_CATCH_MEMBER("cannot compare MAP_GMMTrainer objects", 0)
-}
-
-
-/******************************************************************/
-/************ Variables Section ***********************************/
-/******************************************************************/
-
-/***** relevance_factor *****/
-static auto relevance_factor = bob::extension::VariableDoc(
-  "relevance_factor",
-  "float",
-  "If set the reynolds_adaptation parameters, will apply the Reynolds Adaptation Factor. See Eq (14) from [Reynolds2000]_",
-  ""
-);
-PyObject* PyBobLearnEMMAPGMMTrainer_getRelevanceFactor(PyBobLearnEMMAPGMMTrainerObject* self, void*){
-  BOB_TRY
-  return Py_BuildValue("d",self->cxx->getRelevanceFactor());
-  BOB_CATCH_MEMBER("relevance_factor could not be read", 0)
-}
-int PyBobLearnEMMAPGMMTrainer_setRelevanceFactor(PyBobLearnEMMAPGMMTrainerObject* self, PyObject* value, void*){
-  BOB_TRY
-
-  if(!PyBob_NumberCheck(value)){
-    PyErr_Format(PyExc_RuntimeError, "%s %s expects a float", Py_TYPE(self)->tp_name, relevance_factor.name());
-    return -1;
-  }
-
-  self->cxx->setRelevanceFactor(PyFloat_AS_DOUBLE(value));
-  return 0;
-  BOB_CATCH_MEMBER("relevance_factor could not be set", -1)
-}
-
-
-/***** alpha *****/
-static auto alpha = bob::extension::VariableDoc(
-  "alpha",
-  "float",
-  "Set directly the alpha parameter (Eq (14) from [Reynolds2000]_), ignoring zeroth order statistics as a weighting factor.",
-  ""
-);
-PyObject* PyBobLearnEMMAPGMMTrainer_getAlpha(PyBobLearnEMMAPGMMTrainerObject* self, void*){
-  BOB_TRY
-  return Py_BuildValue("d",self->cxx->getAlpha());
-  BOB_CATCH_MEMBER("alpha could not be read", 0)
-}
-int PyBobLearnEMMAPGMMTrainer_setAlpha(PyBobLearnEMMAPGMMTrainerObject* self, PyObject* value, void*){
-  BOB_TRY
-
-  if(!PyBob_NumberCheck(value)){
-    PyErr_Format(PyExc_RuntimeError, "%s %s expects a float", Py_TYPE(self)->tp_name, alpha.name());
-    return -1;
-  }
-
-  self->cxx->setAlpha(PyFloat_AS_DOUBLE(value));
-  return 0;
-  BOB_CATCH_MEMBER("alpha could not be set", -1)
-}
-
-
-static auto gmm_statistics = bob::extension::VariableDoc(
-  "gmm_statistics",
-  ":py:class:`GMMStats`",
-  "The GMM statistics that were used internally in the E- and M-steps",
-  "Setting and getting the internal GMM statistics might be useful to parallelize the GMM training."
-);
-PyObject* PyBobLearnEMMAPGMMTrainer_get_gmm_statistics(PyBobLearnEMMAPGMMTrainerObject* self, void*){
-  BOB_TRY
-  PyBobLearnEMGMMStatsObject* stats = (PyBobLearnEMGMMStatsObject*)PyBobLearnEMGMMStats_Type.tp_alloc(&PyBobLearnEMGMMStats_Type, 0);
-  stats->cxx = self->cxx->base_trainer().getGMMStats();
-  return Py_BuildValue("N", stats);
-  BOB_CATCH_MEMBER("gmm_statistics could not be read", 0)
-}
-int PyBobLearnEMMAPGMMTrainer_set_gmm_statistics(PyBobLearnEMMAPGMMTrainerObject* self, PyObject* value, void*){
-  BOB_TRY
-  if (!PyBobLearnEMGMMStats_Check(value)){
-    PyErr_Format(PyExc_RuntimeError, "%s %s expects a GMMStats object", Py_TYPE(self)->tp_name, gmm_statistics.name());
-    return -1;
-  }
-  self->cxx->base_trainer().setGMMStats(reinterpret_cast<PyBobLearnEMGMMStatsObject*>(value)->cxx);
-  return 0;
-  BOB_CATCH_MEMBER("gmm_statistics could not be set", -1)
-}
-
-
-static PyGetSetDef PyBobLearnEMMAPGMMTrainer_getseters[] = {
-  {
-    alpha.name(),
-    (getter)PyBobLearnEMMAPGMMTrainer_getAlpha,
-    (setter)PyBobLearnEMMAPGMMTrainer_setAlpha,
-    alpha.doc(),
-    0
-  },
-  {
-    relevance_factor.name(),
-    (getter)PyBobLearnEMMAPGMMTrainer_getRelevanceFactor,
-    (setter)PyBobLearnEMMAPGMMTrainer_setRelevanceFactor,
-    relevance_factor.doc(),
-    0
-  },
-  {
-    gmm_statistics.name(),
-    (getter)PyBobLearnEMMAPGMMTrainer_get_gmm_statistics,
-    (setter)PyBobLearnEMMAPGMMTrainer_set_gmm_statistics,
-    gmm_statistics.doc(),
-    0
-  },
-  {0}  // Sentinel
-};
-
-
-/******************************************************************/
-/************ Functions Section ***********************************/
-/******************************************************************/
-
-/*** initialize ***/
-static auto initialize = bob::extension::FunctionDoc(
-  "initialize",
-  "Initialization before the EM steps",
-  "",
-  true
-)
-.add_prototype("gmm_machine, [data], [rng]")
-.add_parameter("gmm_machine", ":py:class:`bob.learn.em.GMMMachine`", "GMMMachine Object")
-.add_parameter("data", "object", "Ignored.")
-.add_parameter("rng", "object", "Ignored.");
-static PyObject* PyBobLearnEMMAPGMMTrainer_initialize(PyBobLearnEMMAPGMMTrainerObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  /* Parses input arguments in a single shot */
-  char** kwlist = initialize.kwlist(0);
-
-  PyBobLearnEMGMMMachineObject* gmm_machine = 0;
-  PyObject* data;
-  PyObject* rng;
-
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!|OO", kwlist, &PyBobLearnEMGMMMachine_Type, &gmm_machine,
-                                                                 &data, &rng)) return 0;
-
-  self->cxx->initialize(*gmm_machine->cxx);
-
-  BOB_CATCH_MEMBER("cannot perform the initialize method", 0)
-
-  Py_RETURN_NONE;
-}
-
-
-/*** e_step ***/
-static auto e_step = bob::extension::FunctionDoc(
-  "e_step",
-  "Calculates and saves statistics across the dataset and saves these as :py:attr:`gmm_statistics`. ",
-
-  "Calculates the average log likelihood of the observations given the GMM,"
-  "and returns this in average_log_likelihood."
-  "The statistics, :py:attr:`gmm_statistics`, will be used in the :py:meth:`m_step` that follows.",
-
-  true
-)
-.add_prototype("gmm_machine,data")
-.add_parameter("gmm_machine", ":py:class:`bob.learn.em.GMMMachine`", "GMMMachine Object")
-.add_parameter("data", "array_like <float, 2D>", "Input data");
-static PyObject* PyBobLearnEMMAPGMMTrainer_e_step(PyBobLearnEMMAPGMMTrainerObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  /* Parses input arguments in a single shot */
-  char** kwlist = e_step.kwlist(0);
-
-  PyBobLearnEMGMMMachineObject* gmm_machine;
-  PyBlitzArrayObject* data = 0;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O&", kwlist, &PyBobLearnEMGMMMachine_Type, &gmm_machine,
-                                                                 &PyBlitzArray_Converter, &data)) return 0;
-  auto data_ = make_safe(data);
-
-
-  // perform check on the input
-  if (data->type_num != NPY_FLOAT64){
-    PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, e_step.name());
-    return 0;
-  }
-
-  if (data->ndim != 2){
-    PyErr_Format(PyExc_TypeError, "`%s' only processes 2D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, e_step.name());
-    return 0;
-  }
-
-  if (data->shape[1] != (Py_ssize_t)gmm_machine->cxx->getNInputs() ) {
-    PyErr_Format(PyExc_TypeError, "`%s' 2D `input` array should have the shape [N, %" PY_FORMAT_SIZE_T "d] not [N, %" PY_FORMAT_SIZE_T "d] for `%s`", Py_TYPE(self)->tp_name, gmm_machine->cxx->getNInputs(), data->shape[1], e_step.name());
-    return 0;
-  }
-
-  auto state = PyEval_SaveThread();
-  self->cxx->eStep(*gmm_machine->cxx, *PyBlitzArrayCxx_AsBlitz<double,2>(data));
-  PyEval_RestoreThread(state);  
-
-  BOB_CATCH_MEMBER("cannot perform the e_step method", 0)
-
-  Py_RETURN_NONE;
-}
-
-
-/*** m_step ***/
-static auto m_step = bob::extension::FunctionDoc(
-  "m_step",
-
-   "Performs a maximum a posteriori (MAP) update of the GMM parameters using the accumulated statistics in :py:attr:`gmm_statistics` and the parameters of the prior model",
-  "",
-  true
-)
-.add_prototype("gmm_machine, [data]")
-.add_parameter("gmm_machine", ":py:class:`bob.learn.em.GMMMachine`", "GMMMachine Object")
-.add_parameter("data", "object", "Ignored.");
-static PyObject* PyBobLearnEMMAPGMMTrainer_m_step(PyBobLearnEMMAPGMMTrainerObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  /* Parses input arguments in a single shot */
-  char** kwlist = m_step.kwlist(0);
-
-  PyBobLearnEMGMMMachineObject* gmm_machine;
-  PyObject* data;
-
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!|O", kwlist, &PyBobLearnEMGMMMachine_Type, &gmm_machine,
-                                                                 &data)) return 0;
-
-  self->cxx->mStep(*gmm_machine->cxx);
-
-  BOB_CATCH_MEMBER("cannot perform the m_step method", 0)
-
-  Py_RETURN_NONE;
-}
-
-
-/*** computeLikelihood ***/
-static auto compute_likelihood = bob::extension::FunctionDoc(
-  "compute_likelihood",
-  "This functions returns the average min (Square Euclidean) distance (average distance to the closest mean)",
-  0,
-  true
-)
-.add_prototype("gmm_machine")
-.add_parameter("gmm_machine", ":py:class:`bob.learn.em.GMMMachine`", "GMMMachine Object");
-static PyObject* PyBobLearnEMMAPGMMTrainer_compute_likelihood(PyBobLearnEMMAPGMMTrainerObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  /* Parses input arguments in a single shot */
-  char** kwlist = compute_likelihood.kwlist(0);
-
-  PyBobLearnEMGMMMachineObject* gmm_machine;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", kwlist, &PyBobLearnEMGMMMachine_Type, &gmm_machine)) return 0;
-
-  double value = self->cxx->computeLikelihood(*gmm_machine->cxx);
-  return Py_BuildValue("d", value);
-
-  BOB_CATCH_MEMBER("cannot perform the computeLikelihood method", 0)
-}
-
-
-
-static PyMethodDef PyBobLearnEMMAPGMMTrainer_methods[] = {
-  {
-    initialize.name(),
-    (PyCFunction)PyBobLearnEMMAPGMMTrainer_initialize,
-    METH_VARARGS|METH_KEYWORDS,
-    initialize.doc()
-  },
-  {
-    e_step.name(),
-    (PyCFunction)PyBobLearnEMMAPGMMTrainer_e_step,
-    METH_VARARGS|METH_KEYWORDS,
-    e_step.doc()
-  },
-  {
-    m_step.name(),
-    (PyCFunction)PyBobLearnEMMAPGMMTrainer_m_step,
-    METH_VARARGS|METH_KEYWORDS,
-    m_step.doc()
-  },
-  {
-    compute_likelihood.name(),
-    (PyCFunction)PyBobLearnEMMAPGMMTrainer_compute_likelihood,
-    METH_VARARGS|METH_KEYWORDS,
-    compute_likelihood.doc()
-  },
-
-  {0} /* Sentinel */
-};
-
-
-/******************************************************************/
-/************ Module Section **************************************/
-/******************************************************************/
-
-// Define the Gaussian type struct; will be initialized later
-PyTypeObject PyBobLearnEMMAPGMMTrainer_Type = {
-  PyVarObject_HEAD_INIT(0,0)
-  0
-};
-
-bool init_BobLearnEMMAPGMMTrainer(PyObject* module)
-{
-  // initialize the type struct
-  PyBobLearnEMMAPGMMTrainer_Type.tp_name      = MAP_GMMTrainer_doc.name();
-  PyBobLearnEMMAPGMMTrainer_Type.tp_basicsize = sizeof(PyBobLearnEMMAPGMMTrainerObject);
-  PyBobLearnEMMAPGMMTrainer_Type.tp_flags     = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE;//Enable the class inheritance
-  PyBobLearnEMMAPGMMTrainer_Type.tp_doc       = MAP_GMMTrainer_doc.doc();
-
-  // set the functions
-  PyBobLearnEMMAPGMMTrainer_Type.tp_new          = PyType_GenericNew;
-  PyBobLearnEMMAPGMMTrainer_Type.tp_init         = reinterpret_cast<initproc>(PyBobLearnEMMAPGMMTrainer_init);
-  PyBobLearnEMMAPGMMTrainer_Type.tp_dealloc      = reinterpret_cast<destructor>(PyBobLearnEMMAPGMMTrainer_delete);
-  PyBobLearnEMMAPGMMTrainer_Type.tp_richcompare  = reinterpret_cast<richcmpfunc>(PyBobLearnEMMAPGMMTrainer_RichCompare);
-  PyBobLearnEMMAPGMMTrainer_Type.tp_methods      = PyBobLearnEMMAPGMMTrainer_methods;
-  PyBobLearnEMMAPGMMTrainer_Type.tp_getset       = PyBobLearnEMMAPGMMTrainer_getseters;
-  PyBobLearnEMMAPGMMTrainer_Type.tp_call         = reinterpret_cast<ternaryfunc>(PyBobLearnEMMAPGMMTrainer_compute_likelihood);
-
-
-  // check that everything is fine
-  if (PyType_Ready(&PyBobLearnEMMAPGMMTrainer_Type) < 0) return false;
-
-  // add the type to the module
-  Py_INCREF(&PyBobLearnEMMAPGMMTrainer_Type);
-  return PyModule_AddObject(module, "MAP_GMMTrainer", (PyObject*)&PyBobLearnEMMAPGMMTrainer_Type) >= 0;
-}
diff --git a/bob/learn/em/ml_gmm_trainer.cpp b/bob/learn/em/ml_gmm_trainer.cpp
deleted file mode 100644
index 7e7dc488fd7f11903dc32fea1d04030bd5d7c6b7..0000000000000000000000000000000000000000
--- a/bob/learn/em/ml_gmm_trainer.cpp
+++ /dev/null
@@ -1,393 +0,0 @@
-/**
- * @author Tiago de Freitas Pereira <tiago.pereira@idiap.ch>
- * @date Web 22 Jan 16:45:00 2015
- *
- * @brief Python API for bob::learn::em
- *
- * Copyright (C) 2011-2014 Idiap Research Institute, Martigny, Switzerland
- */
-
-#include "main.h"
-
-/******************************************************************/
-/************ Constructor Section *********************************/
-/******************************************************************/
-
-static inline bool f(PyObject* o){return o != 0 && PyObject_IsTrue(o) > 0;}  /* converts PyObject to bool and returns false if object is NULL */
-
-static auto ML_GMMTrainer_doc = bob::extension::ClassDoc(
-  BOB_EXT_MODULE_PREFIX ".ML_GMMTrainer",
-  "This class implements the maximum likelihood M-step (:ref:`MLE <mle>`) of the expectation-maximisation algorithm for a GMM Machine."
-).add_constructor(
-  bob::extension::FunctionDoc(
-    "__init__",
-    "Creates a ML_GMMTrainer",
-    "",
-    true
-  )
-  .add_prototype("update_means, [update_variances], [update_weights], [mean_var_update_responsibilities_threshold]","")
-  .add_prototype("other","")
-
-  .add_parameter("update_means", "bool", "Update means on each iteration")
-  .add_parameter("update_variances", "bool", "Update variances on each iteration")
-  .add_parameter("update_weights", "bool", "Update weights on each iteration")
-  .add_parameter("mean_var_update_responsibilities_threshold", "float", "Threshold over the responsibilities of the Gaussians Equations 9.24, 9.25 of Bishop, `Pattern recognition and machine learning`, 2006 require a division by the responsibilities, which might be equal to zero because of numerical issue. This threshold is used to avoid such divisions.")
-
-
-  .add_parameter("other", ":py:class:`bob.learn.em.ML_GMMTrainer`", "A ML_GMMTrainer object to be copied.")
-);
-
-
-static int PyBobLearnEMMLGMMTrainer_init_copy(PyBobLearnEMMLGMMTrainerObject* self, PyObject* args, PyObject* kwargs) {
-
-  char** kwlist = ML_GMMTrainer_doc.kwlist(1);
-  PyBobLearnEMMLGMMTrainerObject* o;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", kwlist, &PyBobLearnEMMLGMMTrainer_Type, &o)){
-    ML_GMMTrainer_doc.print_usage();
-    return -1;
-  }
-
-  self->cxx.reset(new bob::learn::em::ML_GMMTrainer(*o->cxx));
-  return 0;
-}
-
-
-static int PyBobLearnEMMLGMMTrainer_init_base_trainer(PyBobLearnEMMLGMMTrainerObject* self, PyObject* args, PyObject* kwargs) {
-
-  char** kwlist = ML_GMMTrainer_doc.kwlist(0);
-
-  PyObject* update_means     = Py_True;
-  PyObject* update_variances = Py_False;
-  PyObject* update_weights   = Py_False;
-  double mean_var_update_responsibilities_threshold = std::numeric_limits<double>::epsilon();
-
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|O!O!O!d", kwlist,
-                                   &PyBool_Type, &update_means,
-                                   &PyBool_Type, &update_variances,
-                                   &PyBool_Type, &update_weights,
-                                   &mean_var_update_responsibilities_threshold)){
-    ML_GMMTrainer_doc.print_usage();
-    return -1;
-  }
-
-  self->cxx.reset(new bob::learn::em::ML_GMMTrainer(f(update_means), f(update_variances), f(update_weights),
-                                                       mean_var_update_responsibilities_threshold));
-  return 0;
-}
-
-
-
-static int PyBobLearnEMMLGMMTrainer_init(PyBobLearnEMMLGMMTrainerObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  // get the number of command line arguments
-  int nargs = (args?PyTuple_Size(args):0) + (kwargs?PyDict_Size(kwargs):0);
-
-  if (nargs==0)
-    return PyBobLearnEMMLGMMTrainer_init_base_trainer(self, args, kwargs);
-  else{
-
-    //Reading the input argument
-    PyObject* arg = 0;
-    if (PyTuple_Size(args))
-      arg = PyTuple_GET_ITEM(args, 0);
-    else {
-      PyObject* tmp = PyDict_Values(kwargs);
-      auto tmp_ = make_safe(tmp);
-      arg = PyList_GET_ITEM(tmp, 0);
-    }
-
-    // If the constructor input is GMMBaseTrainer object
-    if (PyBobLearnEMMLGMMTrainer_Check(arg))
-      return PyBobLearnEMMLGMMTrainer_init_copy(self, args, kwargs);
-    else
-      return PyBobLearnEMMLGMMTrainer_init_base_trainer(self, args, kwargs);
-  }
-  BOB_CATCH_MEMBER("cannot create GMMBaseTrainer_init_bool", -1)
-  return 0;
-}
-
-
-static void PyBobLearnEMMLGMMTrainer_delete(PyBobLearnEMMLGMMTrainerObject* self) {
-  self->cxx.reset();
-  Py_TYPE(self)->tp_free((PyObject*)self);
-}
-
-
-int PyBobLearnEMMLGMMTrainer_Check(PyObject* o) {
-  return PyObject_IsInstance(o, reinterpret_cast<PyObject*>(&PyBobLearnEMMLGMMTrainer_Type));
-}
-
-
-static PyObject* PyBobLearnEMMLGMMTrainer_RichCompare(PyBobLearnEMMLGMMTrainerObject* self, PyObject* other, int op) {
-  BOB_TRY
-
-  if (!PyBobLearnEMMLGMMTrainer_Check(other)) {
-    PyErr_Format(PyExc_TypeError, "cannot compare `%s' with `%s'", Py_TYPE(self)->tp_name, Py_TYPE(other)->tp_name);
-    return 0;
-  }
-  auto other_ = reinterpret_cast<PyBobLearnEMMLGMMTrainerObject*>(other);
-  switch (op) {
-    case Py_EQ:
-      if (*self->cxx==*other_->cxx) Py_RETURN_TRUE; else Py_RETURN_FALSE;
-    case Py_NE:
-      if (*self->cxx==*other_->cxx) Py_RETURN_FALSE; else Py_RETURN_TRUE;
-    default:
-      Py_INCREF(Py_NotImplemented);
-      return Py_NotImplemented;
-  }
-  BOB_CATCH_MEMBER("cannot compare ML_GMMTrainer objects", 0)
-}
-
-
-/******************************************************************/
-/************ Variables Section ***********************************/
-/******************************************************************/
-
-static auto gmm_statistics = bob::extension::VariableDoc(
-  "gmm_statistics",
-  ":py:class:`GMMStats`",
-  "The GMM statistics that were used internally in the E- and M-steps",
-  "Setting and getting the internal GMM statistics might be useful to parallelize the GMM training."
-);
-PyObject* PyBobLearnEMMLGMMTrainer_get_gmm_statistics(PyBobLearnEMMLGMMTrainerObject* self, void*){
-  BOB_TRY
-  PyBobLearnEMGMMStatsObject* stats = (PyBobLearnEMGMMStatsObject*)PyBobLearnEMGMMStats_Type.tp_alloc(&PyBobLearnEMGMMStats_Type, 0);
-  stats->cxx = self->cxx->base_trainer().getGMMStats();
-  return Py_BuildValue("N", stats);
-  BOB_CATCH_MEMBER("gmm_statistics could not be read", 0)
-}
-int PyBobLearnEMMLGMMTrainer_set_gmm_statistics(PyBobLearnEMMLGMMTrainerObject* self, PyObject* value, void*){
-  BOB_TRY
-  if (!PyBobLearnEMGMMStats_Check(value)){
-    PyErr_Format(PyExc_RuntimeError, "%s %s expects a GMMStats object", Py_TYPE(self)->tp_name, gmm_statistics.name());
-    return -1;
-  }
-  self->cxx->base_trainer().setGMMStats(reinterpret_cast<PyBobLearnEMGMMStatsObject*>(value)->cxx);
-  return 0;
-  BOB_CATCH_MEMBER("gmm_statistics could not be set", -1)
-}
-
-
-static PyGetSetDef PyBobLearnEMMLGMMTrainer_getseters[] = {
-  {
-   gmm_statistics.name(),
-   (getter)PyBobLearnEMMLGMMTrainer_get_gmm_statistics,
-   (setter)PyBobLearnEMMLGMMTrainer_set_gmm_statistics,
-   gmm_statistics.doc(),
-   0
-  },
-  {0}  // Sentinel
-};
-
-
-/******************************************************************/
-/************ Functions Section ***********************************/
-/******************************************************************/
-
-/*** initialize ***/
-static auto initialize = bob::extension::FunctionDoc(
-  "initialize",
-  "Initialization before the EM steps",
-  "",
-  true
-)
-.add_prototype("gmm_machine, [data], [rng]")
-.add_parameter("gmm_machine", ":py:class:`bob.learn.em.GMMMachine`", "GMMMachine Object")
-.add_parameter("data", "object", "Ignored.")
-.add_parameter("rng", "object", "Ignored.");
-static PyObject* PyBobLearnEMMLGMMTrainer_initialize(PyBobLearnEMMLGMMTrainerObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  /* Parses input arguments in a single shot */
-  char** kwlist = initialize.kwlist(0);
-  PyBobLearnEMGMMMachineObject* gmm_machine = 0;
-  PyObject* data;
-  PyObject* rng;
-
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!|OO", kwlist, &PyBobLearnEMGMMMachine_Type, &gmm_machine,
-                                                                  &data, &rng)) return 0;
-
-  self->cxx->initialize(*gmm_machine->cxx);
-  BOB_CATCH_MEMBER("cannot perform the initialize method", 0)
-
-  Py_RETURN_NONE;
-}
-
-
-/*** e_step ***/
-static auto e_step = bob::extension::FunctionDoc(
-  "e_step",
-  "Calculates and saves statistics across the dataset,"
-  "and saves these as m_ss. ",
-
-  "Calculates the average log likelihood of the observations given the GMM,"
-  "and returns this in average_log_likelihood."
-  "The statistics, :py:attr:`gmm_statistics`, will be used in the :py:func:`m_step` that follows.",
-
-  true
-)
-.add_prototype("gmm_machine,data")
-.add_parameter("gmm_machine", ":py:class:`bob.learn.em.GMMMachine`", "GMMMachine Object")
-.add_parameter("data", "array_like <float, 2D>", "Input data");
-static PyObject* PyBobLearnEMMLGMMTrainer_e_step(PyBobLearnEMMLGMMTrainerObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  /* Parses input arguments in a single shot */
-  char** kwlist = e_step.kwlist(0);
-
-  PyBobLearnEMGMMMachineObject* gmm_machine;
-  PyBlitzArrayObject* data = 0;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O&", kwlist, &PyBobLearnEMGMMMachine_Type, &gmm_machine,
-                                                                 &PyBlitzArray_Converter, &data)) return 0;
-  auto data_ = make_safe(data);
-
-  // perform check on the input
-  if (data->type_num != NPY_FLOAT64){
-    PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, e_step.name());
-    return 0;
-  }
-
-  if (data->ndim != 2){
-    PyErr_Format(PyExc_TypeError, "`%s' only processes 2D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, e_step.name());
-    return 0;
-  }
-
-  if (data->shape[1] != (Py_ssize_t)gmm_machine->cxx->getNInputs() ) {
-    PyErr_Format(PyExc_TypeError, "`%s' 2D `input` array should have the shape [N, %" PY_FORMAT_SIZE_T "d] not [N, %" PY_FORMAT_SIZE_T "d] for `%s`", Py_TYPE(self)->tp_name, gmm_machine->cxx->getNInputs(), data->shape[1], e_step.name());
-    return 0;
-  }
-
-  auto state = PyEval_SaveThread();
-  self->cxx->eStep(*gmm_machine->cxx, *PyBlitzArrayCxx_AsBlitz<double,2>(data));
-  PyEval_RestoreThread(state);
-
-  BOB_CATCH_MEMBER("cannot perform the e_step method", 0)
-
-  Py_RETURN_NONE;
-}
-
-
-/*** m_step ***/
-static auto m_step = bob::extension::FunctionDoc(
-  "m_step",
-  "Performs a maximum likelihood (ML) update of the GMM parameters "
-  "using the accumulated statistics in :py:attr:`gmm_statistics`",
-
-  "See Section 9.2.2 of Bishop, \"Pattern recognition and machine learning\", 2006",
-
-  true
-)
-.add_prototype("gmm_machine, [data]")
-.add_parameter("gmm_machine", ":py:class:`bob.learn.em.GMMMachine`", "GMMMachine Object")
-.add_parameter("data", "object", "Ignored.");
-static PyObject* PyBobLearnEMMLGMMTrainer_m_step(PyBobLearnEMMLGMMTrainerObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  /* Parses input arguments in a single shot */
-  char** kwlist = m_step.kwlist(0);
-
-  PyBobLearnEMGMMMachineObject* gmm_machine = 0;
-  PyObject* data;
-
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!|O", kwlist, &PyBobLearnEMGMMMachine_Type, &gmm_machine,
-                                                                 &data)) return 0;
-
-  self->cxx->mStep(*gmm_machine->cxx);
-
-  BOB_CATCH_MEMBER("cannot perform the m_step method", 0)
-
-  Py_RETURN_NONE;
-}
-
-
-/*** computeLikelihood ***/
-static auto compute_likelihood = bob::extension::FunctionDoc(
-  "compute_likelihood",
-  "This functions returns the average min (Square Euclidean) distance (average distance to the closest mean)",
-  0,
-  true
-)
-.add_prototype("gmm_machine")
-.add_parameter("gmm_machine", ":py:class:`bob.learn.em.GMMMachine`", "GMMMachine Object");
-static PyObject* PyBobLearnEMMLGMMTrainer_compute_likelihood(PyBobLearnEMMLGMMTrainerObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  /* Parses input arguments in a single shot */
-  char** kwlist = compute_likelihood.kwlist(0);
-
-  PyBobLearnEMGMMMachineObject* gmm_machine;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", kwlist, &PyBobLearnEMGMMMachine_Type, &gmm_machine)) return 0;
-
-  double value = self->cxx->computeLikelihood(*gmm_machine->cxx);
-  return Py_BuildValue("d", value);
-
-  BOB_CATCH_MEMBER("cannot perform the computeLikelihood method", 0)
-}
-
-
-
-static PyMethodDef PyBobLearnEMMLGMMTrainer_methods[] = {
-  {
-    initialize.name(),
-    (PyCFunction)PyBobLearnEMMLGMMTrainer_initialize,
-    METH_VARARGS|METH_KEYWORDS,
-    initialize.doc()
-  },
-  {
-    e_step.name(),
-    (PyCFunction)PyBobLearnEMMLGMMTrainer_e_step,
-    METH_VARARGS|METH_KEYWORDS,
-    e_step.doc()
-  },
-  {
-    m_step.name(),
-    (PyCFunction)PyBobLearnEMMLGMMTrainer_m_step,
-    METH_VARARGS|METH_KEYWORDS,
-    m_step.doc()
-  },
-  {
-    compute_likelihood.name(),
-    (PyCFunction)PyBobLearnEMMLGMMTrainer_compute_likelihood,
-    METH_VARARGS|METH_KEYWORDS,
-    compute_likelihood.doc()
-  },
-  {0} /* Sentinel */
-};
-
-
-/******************************************************************/
-/************ Module Section **************************************/
-/******************************************************************/
-
-// Define the Gaussian type struct; will be initialized later
-PyTypeObject PyBobLearnEMMLGMMTrainer_Type = {
-  PyVarObject_HEAD_INIT(0,0)
-  0
-};
-
-bool init_BobLearnEMMLGMMTrainer(PyObject* module)
-{
-  // initialize the type struct
-  PyBobLearnEMMLGMMTrainer_Type.tp_name      = ML_GMMTrainer_doc.name();
-  PyBobLearnEMMLGMMTrainer_Type.tp_basicsize = sizeof(PyBobLearnEMMLGMMTrainerObject);
-  PyBobLearnEMMLGMMTrainer_Type.tp_flags     = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE;//Enable the class inheritance
-  PyBobLearnEMMLGMMTrainer_Type.tp_doc       = ML_GMMTrainer_doc.doc();
-
-  // set the functions
-  PyBobLearnEMMLGMMTrainer_Type.tp_new          = PyType_GenericNew;
-  PyBobLearnEMMLGMMTrainer_Type.tp_init         = reinterpret_cast<initproc>(PyBobLearnEMMLGMMTrainer_init);
-  PyBobLearnEMMLGMMTrainer_Type.tp_dealloc      = reinterpret_cast<destructor>(PyBobLearnEMMLGMMTrainer_delete);
-  PyBobLearnEMMLGMMTrainer_Type.tp_richcompare  = reinterpret_cast<richcmpfunc>(PyBobLearnEMMLGMMTrainer_RichCompare);
-  PyBobLearnEMMLGMMTrainer_Type.tp_methods      = PyBobLearnEMMLGMMTrainer_methods;
-  PyBobLearnEMMLGMMTrainer_Type.tp_getset       = PyBobLearnEMMLGMMTrainer_getseters;
-  PyBobLearnEMMLGMMTrainer_Type.tp_call         = reinterpret_cast<ternaryfunc>(PyBobLearnEMMLGMMTrainer_compute_likelihood);
-
-
-  // check that everything is fine
-  if (PyType_Ready(&PyBobLearnEMMLGMMTrainer_Type) < 0) return false;
-
-  // add the type to the module
-  Py_INCREF(&PyBobLearnEMMLGMMTrainer_Type);
-  return PyModule_AddObject(module, "ML_GMMTrainer", (PyObject*)&PyBobLearnEMMLGMMTrainer_Type) >= 0;
-}
diff --git a/bob/learn/em/plda_base.cpp b/bob/learn/em/plda_base.cpp
deleted file mode 100644
index 839dc9c233ab87d0e187c2493ad8d89f8d490474..0000000000000000000000000000000000000000
--- a/bob/learn/em/plda_base.cpp
+++ /dev/null
@@ -1,1097 +0,0 @@
-/**
- * @date Thu Jan 29 15:44:15 2015 +0200
- * @author Tiago de Freitas Pereira <tiago.pereira@idiap.ch>
- *
- * @brief Python API for bob::learn::em
- *
- * Copyright (C) 2011-2014 Idiap Research Institute, Martigny, Switzerland
- */
-
-#include "main.h"
-
-/******************************************************************/
-/************ Constructor Section *********************************/
-/******************************************************************/
-
-static auto PLDABase_doc = bob::extension::ClassDoc(
-  BOB_EXT_MODULE_PREFIX ".PLDABase",
-
-  "This class is a container for the :math:`F` (between class variantion matrix), :math:`G` (within class variantion matrix) and :math:`\\Sigma` "
-  "matrices and the mean vector :math:`\\mu` of a PLDA model. This also"
-  "precomputes useful matrices to make the model scalable."
-  "References: [ElShafey2014]_ [PrinceElder2007]_ [LiFu2012]_ ",
-  ""
-).add_constructor(
-  bob::extension::FunctionDoc(
-    "__init__",
-
-     "Constructor, builds a new PLDABase. :math:`F`, :math:`G` "
-     "and :math:`\\Sigma` are initialized to the 'eye' matrix (matrix with 1's "
-     "on the diagonal and 0 outside), and :math:`\\mu` is initialized to 0.",
-
-    "",
-    true
-  )
-  .add_prototype("dim_d,dim_f,dim_g,variance_threshold","")
-  .add_prototype("other","")
-  .add_prototype("hdf5","")
-
-  .add_parameter("dim_d", "int", "Dimensionality of the feature vector.")
-  .add_parameter("dim_f", "int", "Size of :math:`F` (between class variantion matrix).")
-  .add_parameter("dim_g", "int", "Size of :math:`G` (within class variantion matrix).")
-  .add_parameter("variance_threshold", "float", "The smallest possible value of the variance (Ignored if set to 0.)")
-
-  .add_parameter("other", ":py:class:`bob.learn.em.PLDABase`", "A PLDABase object to be copied.")
-  .add_parameter("hdf5", ":py:class:`bob.io.base.HDF5File`", "An HDF5 file open for reading")
-
-);
-
-
-static int PyBobLearnEMPLDABase_init_copy(PyBobLearnEMPLDABaseObject* self, PyObject* args, PyObject* kwargs) {
-
-  char** kwlist = PLDABase_doc.kwlist(1);
-  PyBobLearnEMPLDABaseObject* o;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", kwlist, &PyBobLearnEMPLDABase_Type, &o)){
-    PLDABase_doc.print_usage();
-    return -1;
-  }
-
-  self->cxx.reset(new bob::learn::em::PLDABase(*o->cxx));
-  return 0;
-}
-
-
-static int PyBobLearnEMPLDABase_init_hdf5(PyBobLearnEMPLDABaseObject* self, PyObject* args, PyObject* kwargs) {
-
-  char** kwlist = PLDABase_doc.kwlist(2);
-
-  PyBobIoHDF5FileObject* config = 0;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, &PyBobIoHDF5File_Converter, &config)){
-    PLDABase_doc.print_usage();
-    return -1;
-  }
-  auto config_ = make_safe(config);
-  self->cxx.reset(new bob::learn::em::PLDABase(*(config->f)));
-
-  return 0;
-}
-
-
-static int PyBobLearnEMPLDABase_init_dim(PyBobLearnEMPLDABaseObject* self, PyObject* args, PyObject* kwargs) {
-
-  char** kwlist = PLDABase_doc.kwlist(0);
-
-  int dim_D, dim_F, dim_G = 1;
-  double variance_threshold = 0.0;
-
-  //Here we have to select which keyword argument to read
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "iii|d", kwlist, &dim_D, &dim_F, &dim_G, &variance_threshold)){
-    PLDABase_doc.print_usage();
-    return -1;
-  }
-
-  if(dim_D <= 0){
-    PyErr_Format(PyExc_TypeError, "dim_D argument must be greater than or equal to one");
-    return -1;
-  }
-
-  if(dim_F <= 0){
-    PyErr_Format(PyExc_TypeError, "dim_F argument must be greater than or equal to one");
-    return -1;
-  }
-
-  if(dim_G <= 0){
-    PyErr_Format(PyExc_TypeError, "dim_G argument must be greater than or equal to one");
-    return -1;
-  }
-
-  if(variance_threshold < 0){
-    PyErr_Format(PyExc_TypeError, "variance_threshold argument must be greater than or equal to zero");
-    return -1;
-  }
-
-
-  self->cxx.reset(new bob::learn::em::PLDABase(dim_D, dim_F, dim_G, variance_threshold));
-  return 0;
-}
-
-static int PyBobLearnEMPLDABase_init(PyBobLearnEMPLDABaseObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  // get the number of command line arguments
-  int nargs = (args?PyTuple_Size(args):0) + (kwargs?PyDict_Size(kwargs):0);
-
-  if(nargs==1){
-    //Reading the input argument
-    PyObject* arg = 0;
-    if (PyTuple_Size(args))
-      arg = PyTuple_GET_ITEM(args, 0);
-    else {
-      PyObject* tmp = PyDict_Values(kwargs);
-      auto tmp_ = make_safe(tmp);
-      arg = PyList_GET_ITEM(tmp, 0);
-    }
-
-    // If the constructor input is Gaussian object
-    if (PyBobLearnEMPLDABase_Check(arg))
-      return PyBobLearnEMPLDABase_init_copy(self, args, kwargs);
-    // If the constructor input is a HDF5
-    else if (PyBobIoHDF5File_Check(arg))
-      return PyBobLearnEMPLDABase_init_hdf5(self, args, kwargs);
-  }
-  else if((nargs==3)||(nargs==4))
-    return PyBobLearnEMPLDABase_init_dim(self, args, kwargs);
-  else{
-    PyErr_Format(PyExc_RuntimeError, "number of arguments mismatch - %s requires 1, 3 or 4 arguments, but you provided %d (see help)", Py_TYPE(self)->tp_name, nargs);
-    PLDABase_doc.print_usage();
-    return -1;
-  }
-  BOB_CATCH_MEMBER("cannot create PLDABase", -1)
-  return 0;
-}
-
-
-
-static void PyBobLearnEMPLDABase_delete(PyBobLearnEMPLDABaseObject* self) {
-  self->cxx.reset();
-  Py_TYPE(self)->tp_free((PyObject*)self);
-}
-
-static PyObject* PyBobLearnEMPLDABase_RichCompare(PyBobLearnEMPLDABaseObject* self, PyObject* other, int op) {
-  BOB_TRY
-
-  if (!PyBobLearnEMPLDABase_Check(other)) {
-    PyErr_Format(PyExc_TypeError, "cannot compare `%s' with `%s'", Py_TYPE(self)->tp_name, Py_TYPE(other)->tp_name);
-    return 0;
-  }
-  auto other_ = reinterpret_cast<PyBobLearnEMPLDABaseObject*>(other);
-  switch (op) {
-    case Py_EQ:
-      if (*self->cxx==*other_->cxx) Py_RETURN_TRUE; else Py_RETURN_FALSE;
-    case Py_NE:
-      if (*self->cxx==*other_->cxx) Py_RETURN_FALSE; else Py_RETURN_TRUE;
-    default:
-      Py_INCREF(Py_NotImplemented);
-      return Py_NotImplemented;
-  }
-  BOB_CATCH_MEMBER("cannot compare PLDABase objects", 0)
-}
-
-int PyBobLearnEMPLDABase_Check(PyObject* o) {
-  return PyObject_IsInstance(o, reinterpret_cast<PyObject*>(&PyBobLearnEMPLDABase_Type));
-}
-
-
-/******************************************************************/
-/************ Variables Section ***********************************/
-/******************************************************************/
-
-/***** shape *****/
-static auto shape = bob::extension::VariableDoc(
-  "shape",
-  "(int,int, int)",
-  "A tuple that represents the dimensionality of the feature vector dim_d, the :math:`F` matrix and the :math:`G` matrix.",
-  ""
-);
-PyObject* PyBobLearnEMPLDABase_getShape(PyBobLearnEMPLDABaseObject* self, void*) {
-  BOB_TRY
-  return Py_BuildValue("(i,i,i)", self->cxx->getDimD(), self->cxx->getDimF(), self->cxx->getDimG());
-  BOB_CATCH_MEMBER("shape could not be read", 0)
-}
-
-
-/***** F *****/
-static auto F = bob::extension::VariableDoc(
-  "f",
-  "array_like <float, 2D>",
-  "Returns the :math:`F` matrix (between class variantion matrix)",
-  ""
-);
-PyObject* PyBobLearnEMPLDABase_getF(PyBobLearnEMPLDABaseObject* self, void*){
-  BOB_TRY
-  return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getF());
-  BOB_CATCH_MEMBER("`f` could not be read", 0)
-}
-int PyBobLearnEMPLDABase_setF(PyBobLearnEMPLDABaseObject* self, PyObject* value, void*){
-  BOB_TRY
-  PyBlitzArrayObject* o;
-  if (!PyBlitzArray_Converter(value, &o)){
-    PyErr_Format(PyExc_RuntimeError, "%s %s expects a 2D array of floats", Py_TYPE(self)->tp_name, F.name());
-    return -1;
-  }
-  auto o_ = make_safe(o);
-  auto b = PyBlitzArrayCxx_AsBlitz<double,2>(o, "f");
-  if (!b) return -1;
-  self->cxx->setF(*b);
-  return 0;
-  BOB_CATCH_MEMBER("`f` vector could not be set", -1)
-}
-
-/***** G *****/
-static auto G = bob::extension::VariableDoc(
-  "g",
-  "array_like <float, 2D>",
-  "Returns the :math:`G` matrix (between class variantion matrix)",
-  ""
-);
-PyObject* PyBobLearnEMPLDABase_getG(PyBobLearnEMPLDABaseObject* self, void*){
-  BOB_TRY
-  return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getG());
-  BOB_CATCH_MEMBER("`g` could not be read", 0)
-}
-int PyBobLearnEMPLDABase_setG(PyBobLearnEMPLDABaseObject* self, PyObject* value, void*){
-  BOB_TRY
-  PyBlitzArrayObject* o;
-  if (!PyBlitzArray_Converter(value, &o)){
-    PyErr_Format(PyExc_RuntimeError, "%s %s expects a 2D array of floats", Py_TYPE(self)->tp_name, G.name());
-    return -1;
-  }
-  auto o_ = make_safe(o);
-  auto b = PyBlitzArrayCxx_AsBlitz<double,2>(o, "g");
-  if (!b) return -1;
-  self->cxx->setG(*b);
-  return 0;
-  BOB_CATCH_MEMBER("`g` vector could not be set", -1)
-}
-
-
-/***** mu *****/
-static auto mu = bob::extension::VariableDoc(
-  "mu",
-  "array_like <float, 1D>",
-  "Gets the :math:`\\mu` mean vector of the PLDA model",
-  ""
-);
-PyObject* PyBobLearnEMPLDABase_getMu(PyBobLearnEMPLDABaseObject* self, void*){
-  BOB_TRY
-  return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getMu());
-  BOB_CATCH_MEMBER("`mu` could not be read", 0)
-}
-int PyBobLearnEMPLDABase_setMu(PyBobLearnEMPLDABaseObject* self, PyObject* value, void*){
-  BOB_TRY
-  PyBlitzArrayObject* o;
-  if (!PyBlitzArray_Converter(value, &o)){
-    PyErr_Format(PyExc_RuntimeError, "%s %s expects a 2D array of floats", Py_TYPE(self)->tp_name, mu.name());
-    return -1;
-  }
-  auto o_ = make_safe(o);
-  auto b = PyBlitzArrayCxx_AsBlitz<double,1>(o, "mu");
-  if (!b) return -1;
-  self->cxx->setMu(*b);
-  return 0;
-  BOB_CATCH_MEMBER("`mu` vector could not be set", -1)
-}
-
-
-/***** __isigma__ *****/
-static auto __isigma__ = bob::extension::VariableDoc(
-  "__isigma__",
-  "array_like <float, 1D>",
-  "Gets the inverse vector/diagonal matrix of :math:`\\Sigma^{-1}`",
-  ""
-);
-static PyObject* PyBobLearnEMPLDABase_getISigma(PyBobLearnEMPLDABaseObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-  return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getISigma());
-  BOB_CATCH_MEMBER("__isigma__ could not be read", 0)
-}
-
-
-/***** __alpha__ *****/
-static auto __alpha__ = bob::extension::VariableDoc(
-  "__alpha__",
-  "array_like <float, 2D>",
-  "Gets the \f$\alpha\f$ matrix."
-  ":math:`\\alpha = (Id + G^T \\Sigma^{-1} G)^{-1} = \\mathcal{G}`",
-  ""
-);
-static PyObject* PyBobLearnEMPLDABase_getAlpha(PyBobLearnEMPLDABaseObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-  return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getAlpha());
-  BOB_CATCH_MEMBER("__alpha__ could not be read", 0)
-}
-
-
-/***** __beta__ *****/
-static auto __beta__ = bob::extension::VariableDoc(
-  "__beta__",
-  "array_like <float, 2D>",
-  "Gets the :math:`\\beta` matrix "
-  ":math:`\\beta = (\\Sigma + G G^T)^{-1} = \\mathcal{S} = \\Sigma^{-1} - \\Sigma^{-1} G \\mathcal{G} G^{T} \\Sigma^{-1}`",
-  ""
-);
-static PyObject* PyBobLearnEMPLDABase_getBeta(PyBobLearnEMPLDABaseObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-  return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getBeta());
-  BOB_CATCH_MEMBER("__beta__ could not be read", 0)
-}
-
-
-/***** __ft_beta__ *****/
-static auto __ft_beta__ = bob::extension::VariableDoc(
-  "__ft_beta__",
-  "array_like <float, 2D>",
-  "Gets the :math:`F^T \\beta' matrix",
-  ""
-);
-static PyObject* PyBobLearnEMPLDABase_getFtBeta(PyBobLearnEMPLDABaseObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-  return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getFtBeta());
-  BOB_CATCH_MEMBER("__ft_beta__ could not be read", 0)
-}
-
-
-/***** __gt_i_sigma__ *****/
-static auto __gt_i_sigma__ = bob::extension::VariableDoc(
-  "__gt_i_sigma__",
-  "array_like <float, 2D>",
-  "Gets the :math:`G^T \\Sigma^{-1}` matrix",
-  ""
-);
-static PyObject* PyBobLearnEMPLDABase_getGtISigma(PyBobLearnEMPLDABaseObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-  return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getGtISigma());
-  BOB_CATCH_MEMBER("__gt_i_sigma__ could not be read", 0)
-}
-
-
-/***** __logdet_alpha__ *****/
-static auto __logdet_alpha__ = bob::extension::VariableDoc(
-  "__logdet_alpha__",
-  "float",
-  "Gets :math:`\\log(\\det(\\alpha))`",
-  ""
-);
-static PyObject* PyBobLearnEMPLDABase_getLogDetAlpha(PyBobLearnEMPLDABaseObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-  return Py_BuildValue("d",self->cxx->getLogDetAlpha());
-  BOB_CATCH_MEMBER("__logdet_alpha__ could not be read", 0)
-}
-
-/***** __logdet_sigma__ *****/
-static auto __logdet_sigma__ = bob::extension::VariableDoc(
-  "__logdet_sigma__",
-  "float",
-  "Gets :math:`\\log(\\det(\\Sigma))`",
-  ""
-);
-static PyObject* PyBobLearnEMPLDABase_getLogDetSigma(PyBobLearnEMPLDABaseObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-  return Py_BuildValue("d",self->cxx->getLogDetSigma());
-  BOB_CATCH_MEMBER("__logdet_sigma__ could not be read", 0)
-}
-
-
-/***** variance_threshold *****/
-static auto variance_threshold = bob::extension::VariableDoc(
-  "variance_threshold",
-  "float",
-  "",
-  ""
-);
-static PyObject* PyBobLearnEMPLDABase_getVarianceThreshold(PyBobLearnEMPLDABaseObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-  return Py_BuildValue("d",self->cxx->getVarianceThreshold());
-  BOB_CATCH_MEMBER("variance_threshold could not be read", 0)
-}
-int PyBobLearnEMPLDABase_setVarianceThreshold(PyBobLearnEMPLDABaseObject* self, PyObject* value, void*){
-  BOB_TRY
-
-  if (!PyBob_NumberCheck(value)){
-    PyErr_Format(PyExc_RuntimeError, "%s %s expects an float", Py_TYPE(self)->tp_name, variance_threshold.name());
-    return -1;
-  }
-
-  self->cxx->setVarianceThreshold(PyFloat_AS_DOUBLE(value));
-  BOB_CATCH_MEMBER("variance_threshold could not be set", -1)
-  return 0;
-}
-
-
-
-
-/***** sigma *****/
-static auto sigma = bob::extension::VariableDoc(
-  "sigma",
-  "array_like <float, 1D>",
-  "Gets the :math:`\\sigma` (diagonal) covariance matrix of the PLDA model",
-  ""
-);
-static PyObject* PyBobLearnEMPLDABase_getSigma(PyBobLearnEMPLDABaseObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-  return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getSigma());
-  BOB_CATCH_MEMBER("sigma could not be read", 0)
-}
-int PyBobLearnEMPLDABase_setSigma(PyBobLearnEMPLDABaseObject* self, PyObject* value, void*){
-  BOB_TRY
-  PyBlitzArrayObject* o;
-  if (!PyBlitzArray_Converter(value, &o)){
-    PyErr_Format(PyExc_RuntimeError, "%s %s expects a 2D array of floats", Py_TYPE(self)->tp_name, sigma.name());
-    return -1;
-  }
-  auto o_ = make_safe(o);
-  auto b = PyBlitzArrayCxx_AsBlitz<double,1>(o, "sigma");
-  if (!b) return -1;
-  self->cxx->setSigma(*b);
-  return 0;
-  BOB_CATCH_MEMBER("`sigma` vector could not be set", -1)
-}
-
-
-static PyGetSetDef PyBobLearnEMPLDABase_getseters[] = {
-  {
-   shape.name(),
-   (getter)PyBobLearnEMPLDABase_getShape,
-   0,
-   shape.doc(),
-   0
-  },
-  {
-   F.name(),
-   (getter)PyBobLearnEMPLDABase_getF,
-   (setter)PyBobLearnEMPLDABase_setF,
-   F.doc(),
-   0
-  },
-  {
-   G.name(),
-   (getter)PyBobLearnEMPLDABase_getG,
-   (setter)PyBobLearnEMPLDABase_setG,
-   G.doc(),
-   0
-  },
-  {
-   mu.name(),
-   (getter)PyBobLearnEMPLDABase_getMu,
-   (setter)PyBobLearnEMPLDABase_setMu,
-   mu.doc(),
-   0
-  },
-  {
-   __isigma__.name(),
-   (getter)PyBobLearnEMPLDABase_getISigma,
-   0,
-   __isigma__.doc(),
-   0
-  },
-  {
-   __alpha__.name(),
-   (getter)PyBobLearnEMPLDABase_getAlpha,
-   0,
-   __alpha__.doc(),
-   0
-  },
-  {
-   __beta__.name(),
-   (getter)PyBobLearnEMPLDABase_getBeta,
-   0,
-   __beta__.doc(),
-   0
-  },
-  {
-  __ft_beta__.name(),
-   (getter)PyBobLearnEMPLDABase_getFtBeta,
-   0,
-   __ft_beta__.doc(),
-   0
-  },
-  {
-  __gt_i_sigma__.name(),
-   (getter)PyBobLearnEMPLDABase_getGtISigma,
-   0,
-   __gt_i_sigma__.doc(),
-   0
-  },
-  {
-  __logdet_alpha__.name(),
-   (getter)PyBobLearnEMPLDABase_getLogDetAlpha,
-   0,
-   __logdet_alpha__.doc(),
-   0
-  },
-  {
-  __logdet_sigma__.name(),
-   (getter)PyBobLearnEMPLDABase_getLogDetSigma,
-   0,
-   __logdet_sigma__.doc(),
-   0
-  },
-  {
-   sigma.name(),
-   (getter)PyBobLearnEMPLDABase_getSigma,
-   (setter)PyBobLearnEMPLDABase_setSigma,
-   sigma.doc(),
-   0
-  },
-  {
-   variance_threshold.name(),
-   (getter)PyBobLearnEMPLDABase_getVarianceThreshold,
-   (setter)PyBobLearnEMPLDABase_setVarianceThreshold,
-   variance_threshold.doc(),
-   0
-  },
-  {0}  // Sentinel
-};
-
-
-/******************************************************************/
-/************ Functions Section ***********************************/
-/******************************************************************/
-
-
-/*** save ***/
-static auto save = bob::extension::FunctionDoc(
-  "save",
-  "Save the configuration of the PLDABase to a given HDF5 file"
-)
-.add_prototype("hdf5")
-.add_parameter("hdf5", ":py:class:`bob.io.base.HDF5File`", "An HDF5 file open for writing");
-static PyObject* PyBobLearnEMPLDABase_Save(PyBobLearnEMPLDABaseObject* self,  PyObject* args, PyObject* kwargs) {
-
-  BOB_TRY
-
-  // get list of arguments
-  char** kwlist = save.kwlist(0);
-  PyBobIoHDF5FileObject* hdf5;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, PyBobIoHDF5File_Converter, &hdf5)) return 0;
-
-  auto hdf5_ = make_safe(hdf5);
-  self->cxx->save(*hdf5->f);
-
-  BOB_CATCH_MEMBER("cannot save the data", 0)
-  Py_RETURN_NONE;
-}
-
-/*** load ***/
-static auto load = bob::extension::FunctionDoc(
-  "load",
-  "Load the configuration of the PLDABase to a given HDF5 file"
-)
-.add_prototype("hdf5")
-.add_parameter("hdf5", ":py:class:`bob.io.base.HDF5File`", "An HDF5 file open for reading");
-static PyObject* PyBobLearnEMPLDABase_Load(PyBobLearnEMPLDABaseObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  char** kwlist = load.kwlist(0);
-  PyBobIoHDF5FileObject* hdf5;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, PyBobIoHDF5File_Converter, &hdf5)) return 0;
-
-  auto hdf5_ = make_safe(hdf5);
-  self->cxx->load(*hdf5->f);
-
-  BOB_CATCH_MEMBER("cannot load the data", 0)
-  Py_RETURN_NONE;
-}
-
-
-/*** is_similar_to ***/
-static auto is_similar_to = bob::extension::FunctionDoc(
-  "is_similar_to",
-
-  "Compares this PLDABase with the ``other`` one to be approximately the same.",
-  "The optional values ``r_epsilon`` and ``a_epsilon`` refer to the "
-  "relative and absolute precision for the ``weights``, ``biases`` "
-  "and any other values internal to this machine."
-)
-.add_prototype("other, [r_epsilon], [a_epsilon]","output")
-.add_parameter("other", ":py:class:`bob.learn.em.PLDABase`", "A PLDABase object to be compared.")
-.add_parameter("r_epsilon", "float", "Relative precision.")
-.add_parameter("a_epsilon", "float", "Absolute precision.")
-.add_return("output","bool","True if it is similar, otherwise false.");
-static PyObject* PyBobLearnEMPLDABase_IsSimilarTo(PyBobLearnEMPLDABaseObject* self, PyObject* args, PyObject* kwds) {
-
-  /* Parses input arguments in a single shot */
-  char** kwlist = is_similar_to.kwlist(0);
-
-  //PyObject* other = 0;
-  PyBobLearnEMPLDABaseObject* other = 0;
-  double r_epsilon = 1.e-5;
-  double a_epsilon = 1.e-8;
-
-  if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|dd", kwlist,
-        &PyBobLearnEMPLDABase_Type, &other,
-        &r_epsilon, &a_epsilon)){
-
-        is_similar_to.print_usage();
-        return 0;
-  }
-
-  if (self->cxx->is_similar_to(*other->cxx, r_epsilon, a_epsilon))
-    Py_RETURN_TRUE;
-  else
-    Py_RETURN_FALSE;
-}
-
-
-/*** resize ***/
-static auto resize = bob::extension::FunctionDoc(
-  "resize",
-  "Resizes the dimensionality of the PLDA model. Paramaters :math:`\\mu`, :math:`F`, :math:`G` and :math:`\\Sigma` are reinitialized.",
-  0,
-  true
-)
-.add_prototype("dim_d,dim_f,dim_g")
-.add_parameter("dim_d", "int", "Dimensionality of the feature vector.")
-.add_parameter("dim_f", "int", "Size of :math:`F` (between class variantion matrix).")
-.add_parameter("dim_g", "int", "Size of :math:`G` (within class variantion matrix).");
-static PyObject* PyBobLearnEMPLDABase_resize(PyBobLearnEMPLDABaseObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  /* Parses input arguments in a single shot */
-  char** kwlist = resize.kwlist(0);
-
-  int dim_D, dim_F, dim_G = 0;
-
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "iii", kwlist, &dim_D, &dim_F, &dim_G)) return 0;
-
-  if(dim_D <= 0){
-    PyErr_Format(PyExc_TypeError, "dim_d argument must be greater than or equal to one");
-    Py_RETURN_NONE;
-  }
-
-  if(dim_F <= 0){
-    PyErr_Format(PyExc_TypeError, "dim_f argument must be greater than or equal to one");
-    Py_RETURN_NONE;
-  }
-
-  if(dim_G <= 0){
-    PyErr_Format(PyExc_TypeError, "dim_g argument must be greater than or equal to one");
-    Py_RETURN_NONE;
-  }
-
-  self->cxx->resize(dim_D, dim_F, dim_G);
-
-  BOB_CATCH_MEMBER("cannot perform the resize method", 0)
-
-  Py_RETURN_NONE;
-}
-
-
-/***** get_gamma *****/
-static auto get_gamma = bob::extension::FunctionDoc(
-  "get_gamma",
-  "Gets the :math:`\\gamma_a` matrix for a given :math:`a` (number of samples). "
-  ":math:`\\gamma_{a}=(Id + a F^T \\beta F)^{-1} = \\mathcal{F}_{a}`",
-  0,
-  true
-)
-.add_prototype("a","output")
-.add_parameter("a", "int", "Index")
-.add_return("output","array_like <float, 2D>","Get the :math:`\\gamma` matrix");
-static PyObject* PyBobLearnEMPLDABase_getGamma(PyBobLearnEMPLDABaseObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  char** kwlist = get_gamma.kwlist(0);
-
-  int i = 0;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, &i)) return 0;
-
-  return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getGamma(i));
-  BOB_CATCH_MEMBER("`get_gamma` could not be read", 0)
-}
-
-
-/***** has_gamma *****/
-static auto has_gamma = bob::extension::FunctionDoc(
-  "has_gamma",
-  "Tells if the :math:`\\gamma_a` matrix for a given a (number of samples) exists. "
-  ":math:`\\gamma_a=(Id + aF^T \\beta F)^{-1}`",
-  0,
-  true
-)
-.add_prototype("a","output")
-.add_parameter("a", "int", "Index")
-.add_return("output","bool","");
-static PyObject* PyBobLearnEMPLDABase_hasGamma(PyBobLearnEMPLDABaseObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  char** kwlist = has_gamma.kwlist(0);
-  int i = 0;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, &i)) return 0;
-
-  if(self->cxx->hasGamma(i))
-    Py_RETURN_TRUE;
-  else
-    Py_RETURN_FALSE;
- BOB_CATCH_MEMBER("`has_gamma` could not be read", 0)
-}
-
-
-/***** compute_gamma *****/
-static auto compute_gamma = bob::extension::FunctionDoc(
-  "compute_gamma",
-  "Tells if the :math:`\\gamma_a` matrix for a given a (number of samples) exists."
-  " :math:`\\gamma_a=(Id + a F^T \\beta F)^{-1}`",
-  0,
-  true
-)
-.add_prototype("a,res")
-.add_parameter("a", "int", "Index")
-.add_parameter("res", "array_like <float, 2D>", "Input data");
-static PyObject* PyBobLearnEMPLDABase_computeGamma(PyBobLearnEMPLDABaseObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  char** kwlist = compute_gamma.kwlist(0);
-  int i = 0;
-  PyBlitzArrayObject* res = 0;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "iO&", kwlist, &i, &PyBlitzArray_Converter, &res)) return 0;
-
-  auto res_ = make_safe(res);
-
-  self->cxx->computeGamma(i,*PyBlitzArrayCxx_AsBlitz<double,2>(res));
-  Py_RETURN_NONE;
-  BOB_CATCH_MEMBER("`compute_gamma` could not be read", 0)
-}
-
-/***** get_add_gamma *****/
-static auto get_add_gamma = bob::extension::FunctionDoc(
-  "get_add_gamma",
-   "Gets the :math:`gamma_a` matrix for a given :math:`f_a` (number of samples)."
-   " :math:`\\gamma_a=(Id + a F^T \\beta F)^{-1} =\\mathcal{F}_{a}`."
-   "Tries to find it from the base machine and then from this machine.",
-  0,
-  true
-)
-.add_prototype("a","output")
-.add_parameter("a", "int", "Index")
-.add_return("output","array_like <float, 2D>","");
-static PyObject* PyBobLearnEMPLDABase_getAddGamma(PyBobLearnEMPLDABaseObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  char** kwlist = get_add_gamma.kwlist(0);
-
-  int i = 0;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, &i)) return 0;
-
-  return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getAddGamma(i));
-  BOB_CATCH_MEMBER("`get_add_gamma` could not be read", 0)
-}
-
-
-/***** has_log_like_const_term *****/
-static auto has_log_like_const_term = bob::extension::FunctionDoc(
-  "has_log_like_const_term",
-   "Tells if the log likelihood constant term for a given :math:`a` (number of samples) exists in this machine (does not check the base machine). "
-   ":math:`l_{a}=\\frac{a}{2} ( -D log(2\\pi) -log|\\Sigma| +log|\\alpha| +log|\\gamma_a|)`",
-  0,
-  true
-)
-.add_prototype("a","output")
-.add_parameter("a", "int", "Index")
-.add_return("output","bool","");
-static PyObject* PyBobLearnEMPLDABase_hasLogLikeConstTerm(PyBobLearnEMPLDABaseObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  char** kwlist = has_log_like_const_term.kwlist(0);
-  int i = 0;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, &i)) return 0;
-
-  if(self->cxx->hasLogLikeConstTerm(i))
-    Py_RETURN_TRUE;
-  else
-    Py_RETURN_FALSE;
- BOB_CATCH_MEMBER("`has_log_like_const_term` could not be read", 0)
-}
-
-
-/***** compute_log_like_const_term" *****/
-static auto compute_log_like_const_term = bob::extension::FunctionDoc(
-  "compute_log_like_const_term",
-  "Computes the log likelihood constant term for a given :math:`a` (number of samples), given the provided :math:`\\gamma_a` matrix. "
-  ":math:`l_{a} = \\frac{a}{2} ( -D log(2\\pi) -log|\\Sigma| +log|\\alpha| +log|\\gamma_a|)`",
-
-  0,
-  true
-)
-.add_prototype("a,res")
-.add_parameter("a", "int", "Index")
-.add_parameter("res", "array_like <float, 2D>", "Input data");
-static PyObject* PyBobLearnEMPLDABase_computeLogLikeConstTerm(PyBobLearnEMPLDABaseObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  char** kwlist = compute_log_like_const_term.kwlist(0);
-  int i = 0;
-  PyBlitzArrayObject* res = 0;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "iO&", kwlist, &i, &PyBlitzArray_Converter, &res)) return 0;
-
-  auto res_ = make_safe(res);
-
-  self->cxx->computeLogLikeConstTerm(i,*PyBlitzArrayCxx_AsBlitz<double,2>(res));
-  Py_RETURN_NONE;
-  BOB_CATCH_MEMBER("`compute_gamma` could not be read", 0)
-}
-
-
-/***** get_add_log_like_const_term *****/
-static auto get_add_log_like_const_term = bob::extension::FunctionDoc(
-  "get_add_log_like_const_term",
-
-   "Gets the log likelihood constant term for a given :math:`a` (number of samples). "
-   ":math:`l_{a} = \\frac{a}{2} ( -D log(2\\pi) -log|\\Sigma| +log|\\alpha| +log|\\gamma_a|)`",
-  0,
-  true
-)
-.add_prototype("a","output")
-.add_parameter("a", "int", "Index")
-.add_return("output","float","");
-static PyObject* PyBobLearnEMPLDABase_getAddLogLikeConstTerm(PyBobLearnEMPLDABaseObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  char** kwlist = get_add_log_like_const_term.kwlist(0);
-  int i = 0;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, &i)) return 0;
-
-  return Py_BuildValue("d",self->cxx->getAddLogLikeConstTerm(i));
-
-  BOB_CATCH_MEMBER("`get_add_log_like_const_term` could not be read", 0)
-}
-
-
-/***** get_log_like_const_term *****/
-static auto get_log_like_const_term = bob::extension::FunctionDoc(
-  "get_log_like_const_term",
-   "Gets the log likelihood constant term for a given :math:`a` (number of samples). "
-    ":math:`l_{a}=\\frac{a}{2} ( -D log(2\\pi) -log|\\Sigma| +log|\\alpha| +log|\\gamma_a|)`",
-  0,
-  true
-)
-.add_prototype("a","output")
-.add_parameter("a", "int", "Index")
-.add_return("output","float","");
-static PyObject* PyBobLearnEMPLDABase_getLogLikeConstTerm(PyBobLearnEMPLDABaseObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  char** kwlist = get_log_like_const_term.kwlist(0);
-  int i = 0;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, &i)) return 0;
-
-  return Py_BuildValue("d",self->cxx->getLogLikeConstTerm(i));
-
-  BOB_CATCH_MEMBER("`get_log_like_const_term` could not be read", 0)
-}
-
-/***** clear_maps *****/
-static auto clear_maps = bob::extension::FunctionDoc(
-  "clear_maps",
-  "Clears the maps (:math:`\\gamma_a` and loglike_constterm_a).",
-  0,
-  true
-)
-.add_prototype("");
-static PyObject* PyBobLearnEMPLDABase_clearMaps(PyBobLearnEMPLDABaseObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  self->cxx->clearMaps();
-  Py_RETURN_NONE;
-
-  BOB_CATCH_MEMBER("`clear_maps` could not be read", 0)
-}
-
-
-/***** compute_log_likelihood_point_estimate *****/
-static auto compute_log_likelihood_point_estimate = bob::extension::FunctionDoc(
-  "compute_log_likelihood_point_estimate",
-   "Gets the log-likelihood of an observation, given the current model and the latent variables (point estimate)."
-   "This will basically compute :math:`p(x_{ij} | h_{i}, w_{ij}, \\Theta)`, given by "
-   ":math:`\\mathcal{N}(x_{ij}|[\\mu + F h_{i} + G w_{ij} + \\epsilon_{ij}, \\Sigma])`, which is in logarithm, "
-   ":math:`\\frac{D}{2} log(2\\pi) -\\frac{1}{2} log(det(\\Sigma)) -\\frac{1}{2} {(x_{ij}-(\\mu+F h_{i}+G w_{ij}))^{T}\\Sigma^{-1}(x_{ij}-(\\mu+F h_{i}+G w_{ij}))}`",
-  0,
-  true
-)
-.add_prototype("xij,hi,wij","output")
-.add_parameter("xij", "array_like <float, 1D>", "")
-.add_parameter("hi", "array_like <float, 1D>", "")
-.add_parameter("wij", "array_like <float, 1D>", "")
-.add_return("output", "float", "");
-static PyObject* PyBobLearnEMPLDABase_computeLogLikelihoodPointEstimate(PyBobLearnEMPLDABaseObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  char** kwlist = compute_log_likelihood_point_estimate.kwlist(0);
-  PyBlitzArrayObject* xij, *hi, *wij;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&O&O&", kwlist, &PyBlitzArray_Converter, &xij,
-                                                               &PyBlitzArray_Converter, &hi,
-                                                               &PyBlitzArray_Converter, &wij)) return 0;
-
-  auto xij_ = make_safe(xij);
-  auto hi_ = make_safe(hi);
-  auto wij_ = make_safe(wij);
-
-  return Py_BuildValue("d", self->cxx->computeLogLikelihoodPointEstimate(*PyBlitzArrayCxx_AsBlitz<double,1>(xij), *PyBlitzArrayCxx_AsBlitz<double,1>(hi), *PyBlitzArrayCxx_AsBlitz<double,1>(wij)));
-
-  BOB_CATCH_MEMBER("`compute_log_likelihood_point_estimate` could not be read", 0)
-}
-
-/***** __precompute__ *****/
-static auto __precompute__ = bob::extension::FunctionDoc(
-  "__precompute__",
-  "Precomputes useful values for the log likelihood "
-  ":math:`\\log(\\det(\\alpha))` and :math:`\\log(\\det(\\Sigma))`.",
-  0,
-  true
-);
-static PyObject* PyBobLearnEMPLDABase_precompute(PyBobLearnEMPLDABaseObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  self->cxx->precompute();
-  Py_RETURN_NONE;
-
-  BOB_CATCH_MEMBER("`precompute` could not be read", 0)
-}
-
-
-/***** __precompute_log_like__ *****/
-static auto __precompute_log_like__ = bob::extension::FunctionDoc(
-  "__precompute_log_like__",
-
-  "Precomputes useful values for the log likelihood "
-  ":math:`\\log(\\det(\\alpha))` and :math:`\\log(\\det(\\Sigma))`.",
-
-  0,
-  true
-);
-static PyObject* PyBobLearnEMPLDABase_precomputeLogLike(PyBobLearnEMPLDABaseObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  self->cxx->precomputeLogLike();
-  Py_RETURN_NONE;
-
-  BOB_CATCH_MEMBER("`__precompute_log_like__` could not be read", 0)
-}
-
-
-static PyMethodDef PyBobLearnEMPLDABase_methods[] = {
-  {
-    save.name(),
-    (PyCFunction)PyBobLearnEMPLDABase_Save,
-    METH_VARARGS|METH_KEYWORDS,
-    save.doc()
-  },
-  {
-    load.name(),
-    (PyCFunction)PyBobLearnEMPLDABase_Load,
-    METH_VARARGS|METH_KEYWORDS,
-    load.doc()
-  },
-  {
-    is_similar_to.name(),
-    (PyCFunction)PyBobLearnEMPLDABase_IsSimilarTo,
-    METH_VARARGS|METH_KEYWORDS,
-    is_similar_to.doc()
-  },
-  {
-    resize.name(),
-    (PyCFunction)PyBobLearnEMPLDABase_resize,
-    METH_VARARGS|METH_KEYWORDS,
-    resize.doc()
-  },
-  {
-    get_gamma.name(),
-    (PyCFunction)PyBobLearnEMPLDABase_getGamma,
-    METH_VARARGS|METH_KEYWORDS,
-    get_gamma.doc()
-  },
-  {
-    has_gamma.name(),
-    (PyCFunction)PyBobLearnEMPLDABase_hasGamma,
-    METH_VARARGS|METH_KEYWORDS,
-    has_gamma.doc()
-  },
-  {
-    compute_gamma.name(),
-    (PyCFunction)PyBobLearnEMPLDABase_computeGamma,
-    METH_VARARGS|METH_KEYWORDS,
-    compute_gamma.doc()
-  },
-  {
-    get_add_gamma.name(),
-    (PyCFunction)PyBobLearnEMPLDABase_getAddGamma,
-    METH_VARARGS|METH_KEYWORDS,
-    get_add_gamma.doc()
-  },
-  {
-    has_log_like_const_term.name(),
-    (PyCFunction)PyBobLearnEMPLDABase_hasLogLikeConstTerm,
-    METH_VARARGS|METH_KEYWORDS,
-    has_log_like_const_term.doc()
-  },
-  {
-    compute_log_like_const_term.name(),
-    (PyCFunction)PyBobLearnEMPLDABase_computeLogLikeConstTerm,
-    METH_VARARGS|METH_KEYWORDS,
-    compute_log_like_const_term.doc()
-  },
-  {
-    get_add_log_like_const_term.name(),
-    (PyCFunction)PyBobLearnEMPLDABase_getAddLogLikeConstTerm,
-    METH_VARARGS|METH_KEYWORDS,
-    get_add_log_like_const_term.doc()
-  },
-  {
-    get_log_like_const_term.name(),
-    (PyCFunction)PyBobLearnEMPLDABase_getLogLikeConstTerm,
-    METH_VARARGS|METH_KEYWORDS,
-    get_log_like_const_term.doc()
-  },
-  {
-    clear_maps.name(),
-    (PyCFunction)PyBobLearnEMPLDABase_clearMaps,
-    METH_NOARGS,
-    clear_maps.doc()
-  },
-  {
-    compute_log_likelihood_point_estimate.name(),
-    (PyCFunction)PyBobLearnEMPLDABase_computeLogLikelihoodPointEstimate,
-    METH_VARARGS|METH_KEYWORDS,
-    compute_log_likelihood_point_estimate.doc()
-  },
-  {
-    __precompute__.name(),
-    (PyCFunction)PyBobLearnEMPLDABase_precompute,
-    METH_NOARGS,
-    __precompute__.doc()
-  },
-  {
-    __precompute_log_like__.name(),
-    (PyCFunction)PyBobLearnEMPLDABase_precomputeLogLike,
-    METH_NOARGS,
-    __precompute_log_like__.doc()
-  },
-  {0} /* Sentinel */
-};
-
-
-/******************************************************************/
-/************ Module Section **************************************/
-/******************************************************************/
-
-// Define the JFA type struct; will be initialized later
-PyTypeObject PyBobLearnEMPLDABase_Type = {
-  PyVarObject_HEAD_INIT(0,0)
-  0
-};
-
-bool init_BobLearnEMPLDABase(PyObject* module)
-{
-  // initialize the type struct
-  PyBobLearnEMPLDABase_Type.tp_name      = PLDABase_doc.name();
-  PyBobLearnEMPLDABase_Type.tp_basicsize = sizeof(PyBobLearnEMPLDABaseObject);
-  PyBobLearnEMPLDABase_Type.tp_flags     = Py_TPFLAGS_DEFAULT;
-  PyBobLearnEMPLDABase_Type.tp_doc       = PLDABase_doc.doc();
-
-  // set the functions
-  PyBobLearnEMPLDABase_Type.tp_new         = PyType_GenericNew;
-  PyBobLearnEMPLDABase_Type.tp_init        = reinterpret_cast<initproc>(PyBobLearnEMPLDABase_init);
-  PyBobLearnEMPLDABase_Type.tp_dealloc     = reinterpret_cast<destructor>(PyBobLearnEMPLDABase_delete);
-  PyBobLearnEMPLDABase_Type.tp_richcompare = reinterpret_cast<richcmpfunc>(PyBobLearnEMPLDABase_RichCompare);
-  PyBobLearnEMPLDABase_Type.tp_methods     = PyBobLearnEMPLDABase_methods;
-  PyBobLearnEMPLDABase_Type.tp_getset      = PyBobLearnEMPLDABase_getseters;
-  //PyBobLearnEMPLDABase_Type.tp_call = reinterpret_cast<ternaryfunc>(PyBobLearnEMPLDABase_forward);
-
-
-  // check that everything is fine
-  if (PyType_Ready(&PyBobLearnEMPLDABase_Type) < 0) return false;
-
-  // add the type to the module
-  Py_INCREF(&PyBobLearnEMPLDABase_Type);
-  return PyModule_AddObject(module, "PLDABase", (PyObject*)&PyBobLearnEMPLDABase_Type) >= 0;
-}
diff --git a/bob/learn/em/plda_machine.cpp b/bob/learn/em/plda_machine.cpp
deleted file mode 100644
index c85e9d542c3536343385f8c87e01d54098e20001..0000000000000000000000000000000000000000
--- a/bob/learn/em/plda_machine.cpp
+++ /dev/null
@@ -1,806 +0,0 @@
-/**
- * @date Thu Jan 30 11:10:15 2015 +0200
- * @author Tiago de Freitas Pereira <tiago.pereira@idiap.ch>
- *
- * @brief Python API for bob::learn::em
- *
- * Copyright (C) 2011-2014 Idiap Research Institute, Martigny, Switzerland
- */
-
-#include "main.h"
-
-/******************************************************************/
-/************ Constructor Section *********************************/
-/******************************************************************/
-
-static inline bool f(PyObject* o){return o != 0 && PyObject_IsTrue(o) > 0;}  /* converts PyObject to bool and returns false if object is NULL */
-
-static auto PLDAMachine_doc = bob::extension::ClassDoc(
-  BOB_EXT_MODULE_PREFIX ".PLDAMachine",
-
-  "This class is a container for an enrolled identity/class. It contains information extracted from the enrollment samples. "
-  "It should be used in combination with a PLDABase instance.\n\n"
-  "References: [ElShafey2014]_ [PrinceElder2007]_ [LiFu2012]_ ",
-  ""
-).add_constructor(
-  bob::extension::FunctionDoc(
-    "__init__",
-
-     "Constructor, builds a new PLDAMachine.",
-
-    "",
-    true
-  )
-  .add_prototype("plda_base","")
-  .add_prototype("other","")
-  .add_prototype("hdf5,plda_base","")
-
-  .add_parameter("plda_base", ":py:class:`bob.learn.em.PLDABase`", "")
-  .add_parameter("other", ":py:class:`bob.learn.em.PLDAMachine`", "A PLDAMachine object to be copied.")
-  .add_parameter("hdf5", ":py:class:`bob.io.base.HDF5File`", "An HDF5 file open for reading")
-
-);
-
-
-static int PyBobLearnEMPLDAMachine_init_copy(PyBobLearnEMPLDAMachineObject* self, PyObject* args, PyObject* kwargs) {
-
-  char** kwlist = PLDAMachine_doc.kwlist(1);
-  PyBobLearnEMPLDAMachineObject* o;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", kwlist, &PyBobLearnEMPLDAMachine_Type, &o)){
-    PLDAMachine_doc.print_usage();
-    return -1;
-  }
-
-  self->cxx.reset(new bob::learn::em::PLDAMachine(*o->cxx));
-  return 0;
-}
-
-
-static int PyBobLearnEMPLDAMachine_init_hdf5(PyBobLearnEMPLDAMachineObject* self, PyObject* args, PyObject* kwargs) {
-
-  char** kwlist = PLDAMachine_doc.kwlist(2);
-
-  PyBobIoHDF5FileObject* config = 0;
-  PyBobLearnEMPLDABaseObject* plda_base;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&O!", kwlist, &PyBobIoHDF5File_Converter, &config,
-                                                                 &PyBobLearnEMPLDABase_Type, &plda_base)){
-    PLDAMachine_doc.print_usage();
-    return -1;
-  }
-  auto config_ = make_safe(config);
-  self->cxx.reset(new bob::learn::em::PLDAMachine(*(config->f),plda_base->cxx));
-
-  return 0;
-}
-
-
-static int PyBobLearnEMPLDAMachine_init_pldabase(PyBobLearnEMPLDAMachineObject* self, PyObject* args, PyObject* kwargs) {
-
-  char** kwlist = PLDAMachine_doc.kwlist(0);
-  PyBobLearnEMPLDABaseObject* plda_base;
-
-  //Here we have to select which keyword argument to read
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", kwlist, &PyBobLearnEMPLDABase_Type, &plda_base)){
-    PLDAMachine_doc.print_usage();
-    return -1;
-  }
-
-  self->cxx.reset(new bob::learn::em::PLDAMachine(plda_base->cxx));
-  return 0;
-}
-
-static int PyBobLearnEMPLDAMachine_init(PyBobLearnEMPLDAMachineObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  // get the number of command line arguments
-  int nargs = (args?PyTuple_Size(args):0) + (kwargs?PyDict_Size(kwargs):0);
-
-  if(nargs==1){
-    //Reading the input argument
-    PyObject* arg = 0;
-    if (PyTuple_Size(args))
-      arg = PyTuple_GET_ITEM(args, 0);
-    else {
-      PyObject* tmp = PyDict_Values(kwargs);
-      auto tmp_ = make_safe(tmp);
-      arg = PyList_GET_ITEM(tmp, 0);
-    }
-
-    // If the constructor input is Gaussian object
-    if (PyBobLearnEMPLDAMachine_Check(arg))
-      return PyBobLearnEMPLDAMachine_init_copy(self, args, kwargs);
-    // If the constructor input is a HDF5
-    else if (PyBobLearnEMPLDABase_Check(arg))
-      return PyBobLearnEMPLDAMachine_init_pldabase(self, args, kwargs);
-  }
-  else if(nargs==2)
-    return PyBobLearnEMPLDAMachine_init_hdf5(self, args, kwargs);
-  else{
-    PyErr_Format(PyExc_RuntimeError, "number of arguments mismatch - %s requires 1 or 2 arguments, but you provided %d (see help)", Py_TYPE(self)->tp_name, nargs);
-    PLDAMachine_doc.print_usage();
-    return -1;
-  }
-  BOB_CATCH_MEMBER("cannot create PLDAMachine", -1)
-  return 0;
-}
-
-
-
-static void PyBobLearnEMPLDAMachine_delete(PyBobLearnEMPLDAMachineObject* self) {
-  self->cxx.reset();
-  Py_TYPE(self)->tp_free((PyObject*)self);
-}
-
-static PyObject* PyBobLearnEMPLDAMachine_RichCompare(PyBobLearnEMPLDAMachineObject* self, PyObject* other, int op) {
-  BOB_TRY
-
-  if (!PyBobLearnEMPLDAMachine_Check(other)) {
-    PyErr_Format(PyExc_TypeError, "cannot compare `%s' with `%s'", Py_TYPE(self)->tp_name, Py_TYPE(other)->tp_name);
-    return 0;
-  }
-  auto other_ = reinterpret_cast<PyBobLearnEMPLDAMachineObject*>(other);
-  switch (op) {
-    case Py_EQ:
-      if (*self->cxx==*other_->cxx) Py_RETURN_TRUE; else Py_RETURN_FALSE;
-    case Py_NE:
-      if (*self->cxx==*other_->cxx) Py_RETURN_FALSE; else Py_RETURN_TRUE;
-    default:
-      Py_INCREF(Py_NotImplemented);
-      return Py_NotImplemented;
-  }
-  BOB_CATCH_MEMBER("cannot compare PLDAMachine objects", 0)
-}
-
-int PyBobLearnEMPLDAMachine_Check(PyObject* o) {
-  return PyObject_IsInstance(o, reinterpret_cast<PyObject*>(&PyBobLearnEMPLDAMachine_Type));
-}
-
-
-/******************************************************************/
-/************ Variables Section ***********************************/
-/******************************************************************/
-
-/***** shape *****/
-static auto shape = bob::extension::VariableDoc(
-  "shape",
-  "(int,int, int)",
-  "A tuple that represents the dimensionality of the feature vector dim_d, the :math:`F` matrix and the :math:`G` matrix.",
-  ""
-);
-PyObject* PyBobLearnEMPLDAMachine_getShape(PyBobLearnEMPLDAMachineObject* self, void*) {
-  BOB_TRY
-  return Py_BuildValue("(i,i,i)", self->cxx->getDimD(), self->cxx->getDimF(), self->cxx->getDimG());
-  BOB_CATCH_MEMBER("shape could not be read", 0)
-}
-
-
-/***** n_samples *****/
-static auto n_samples = bob::extension::VariableDoc(
-  "n_samples",
-  "int",
-  "Number of enrolled samples",
-  ""
-);
-static PyObject* PyBobLearnEMPLDAMachine_getNSamples(PyBobLearnEMPLDAMachineObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-  return Py_BuildValue("i",self->cxx->getNSamples());
-  BOB_CATCH_MEMBER("n_samples could not be read", 0)
-}
-int PyBobLearnEMPLDAMachine_setNSamples(PyBobLearnEMPLDAMachineObject* self, PyObject* value, void*){
-  BOB_TRY
-
-  if (!PyInt_Check(value)){
-    PyErr_Format(PyExc_RuntimeError, "%s %s expects an int", Py_TYPE(self)->tp_name, n_samples.name());
-    return -1;
-  }
-
-  if (PyInt_AS_LONG(value) < 0){
-    PyErr_Format(PyExc_TypeError, "n_samples must be greater than or equal to zero");
-    return -1;
-  }
-
-  self->cxx->setNSamples(PyInt_AS_LONG(value));
-  BOB_CATCH_MEMBER("n_samples could not be set", -1)
-  return 0;
-}
-
-
-/***** w_sum_xit_beta_xi *****/
-static auto w_sum_xit_beta_xi = bob::extension::VariableDoc(
-  "w_sum_xit_beta_xi",
-  "float",
-  "Gets the :math:`A = -0.5 \\sum_{i} x_{i}^T \\beta x_{i}` value",
-  ""
-);
-static PyObject* PyBobLearnEMPLDAMachine_getWSumXitBetaXi(PyBobLearnEMPLDAMachineObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-  return Py_BuildValue("d",self->cxx->getWSumXitBetaXi());
-  BOB_CATCH_MEMBER("w_sum_xit_beta_xi could not be read", 0)
-}
-int PyBobLearnEMPLDAMachine_setWSumXitBetaXi(PyBobLearnEMPLDAMachineObject* self, PyObject* value, void*){
-  BOB_TRY
-
-  if (!PyBob_NumberCheck(value)){
-    PyErr_Format(PyExc_RuntimeError, "%s %s expects an float", Py_TYPE(self)->tp_name, w_sum_xit_beta_xi.name());
-    return -1;
-  }
-
-  self->cxx->setWSumXitBetaXi(PyFloat_AS_DOUBLE(value));
-  BOB_CATCH_MEMBER("w_sum_xit_beta_xi could not be set", -1)
-  return 0;
-}
-
-
-/***** plda_base *****/
-static auto plda_base = bob::extension::VariableDoc(
-  "plda_base",
-  ":py:class:`bob.learn.em.PLDABase`",
-  "The PLDABase attached to this machine",
-  ""
-);
-PyObject* PyBobLearnEMPLDAMachine_getPLDABase(PyBobLearnEMPLDAMachineObject* self, void*){
-  BOB_TRY
-
-  boost::shared_ptr<bob::learn::em::PLDABase> plda_base_o = self->cxx->getPLDABase();
-
-  //Allocating the correspondent python object
-  PyBobLearnEMPLDABaseObject* retval =
-    (PyBobLearnEMPLDABaseObject*)PyBobLearnEMPLDABase_Type.tp_alloc(&PyBobLearnEMPLDABase_Type, 0);
-  retval->cxx = plda_base_o;
-
-  return Py_BuildValue("N",retval);
-  BOB_CATCH_MEMBER("plda_base could not be read", 0)
-}
-int PyBobLearnEMPLDAMachine_setPLDABase(PyBobLearnEMPLDAMachineObject* self, PyObject* value, void*){
-  BOB_TRY
-
-  if (!PyBobLearnEMPLDABase_Check(value)){
-    PyErr_Format(PyExc_RuntimeError, "%s %s expects a :py:class:`bob.learn.em.PLDABase`", Py_TYPE(self)->tp_name, plda_base.name());
-    return -1;
-  }
-
-  PyBobLearnEMPLDABaseObject* plda_base_o = 0;
-  PyArg_Parse(value, "O!", &PyBobLearnEMPLDABase_Type,&plda_base_o);
-
-  self->cxx->setPLDABase(plda_base_o->cxx);
-
-  return 0;
-  BOB_CATCH_MEMBER("plda_base could not be set", -1)
-}
-
-
-/***** weighted_sum *****/
-static auto weighted_sum = bob::extension::VariableDoc(
-  "weighted_sum",
-  "array_like <float, 1D>",
-  "Get/Set :math:`\\sum_{i} F^T \\beta x_{i}` value",
-  ""
-);
-static PyObject* PyBobLearnEMPLDAMachine_getWeightedSum(PyBobLearnEMPLDAMachineObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-  return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getWeightedSum());
-  BOB_CATCH_MEMBER("weighted_sum could not be read", 0)
-}
-int PyBobLearnEMPLDAMachine_setWeightedSum(PyBobLearnEMPLDAMachineObject* self, PyObject* value, void*){
-  BOB_TRY
-  PyBlitzArrayObject* o;
-  if (!PyBlitzArray_Converter(value, &o)){
-    PyErr_Format(PyExc_RuntimeError, "%s %s expects a 2D array of floats", Py_TYPE(self)->tp_name, weighted_sum.name());
-    return -1;
-  }
-  auto o_ = make_safe(o);
-  auto b = PyBlitzArrayCxx_AsBlitz<double,1>(o, "weighted_sum");
-  if (!b) return -1;
-  self->cxx->setWeightedSum(*b);
-  return 0;
-  BOB_CATCH_MEMBER("`weighted_sum` vector could not be set", -1)
-}
-
-
-/***** log_likelihood *****/
-static auto log_likelihood = bob::extension::VariableDoc(
-  "log_likelihood",
-  "float",
-  "Get the current log likelihood",
-  ""
-);
-static PyObject* PyBobLearnEMPLDAMachine_getLogLikelihood(PyBobLearnEMPLDAMachineObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-  return Py_BuildValue("d",self->cxx->getLogLikelihood());
-  BOB_CATCH_MEMBER("log_likelihood could not be read", 0)
-}
-int PyBobLearnEMPLDAMachine_setLogLikelihood(PyBobLearnEMPLDAMachineObject* self, PyObject* value, void*){
-  BOB_TRY
-
-  if (!PyBob_NumberCheck(value)){
-    PyErr_Format(PyExc_RuntimeError, "%s %s expects a float", Py_TYPE(self)->tp_name, log_likelihood.name());
-    return -1;
-  }
-
-  self->cxx->setLogLikelihood(PyFloat_AS_DOUBLE(value));
-  BOB_CATCH_MEMBER("log_likelihood could not be set", -1)
-  return 0;
-}
-
-
-static PyGetSetDef PyBobLearnEMPLDAMachine_getseters[] = {
-  {
-   shape.name(),
-   (getter)PyBobLearnEMPLDAMachine_getShape,
-   0,
-   shape.doc(),
-   0
-  },
-  {
-   n_samples.name(),
-   (getter)PyBobLearnEMPLDAMachine_getNSamples,
-   (setter)PyBobLearnEMPLDAMachine_setNSamples,
-   n_samples.doc(),
-   0
-  },
-  {
-   w_sum_xit_beta_xi.name(),
-   (getter)PyBobLearnEMPLDAMachine_getWSumXitBetaXi,
-   (setter)PyBobLearnEMPLDAMachine_setWSumXitBetaXi,
-   w_sum_xit_beta_xi.doc(),
-   0
-  },
-  {
-   plda_base.name(),
-   (getter)PyBobLearnEMPLDAMachine_getPLDABase,
-   (setter)PyBobLearnEMPLDAMachine_setPLDABase,
-   plda_base.doc(),
-   0
-  },
-  {
-   weighted_sum.name(),
-   (getter)PyBobLearnEMPLDAMachine_getWeightedSum,
-   (setter)PyBobLearnEMPLDAMachine_setWeightedSum,
-   weighted_sum.doc(),
-   0
-  },
-  {
-   log_likelihood.name(),
-   (getter)PyBobLearnEMPLDAMachine_getLogLikelihood,
-   (setter)PyBobLearnEMPLDAMachine_setLogLikelihood,
-   log_likelihood.doc(),
-   0
-  },
-  {0}  // Sentinel
-};
-
-
-
-/******************************************************************/
-/************ Functions Section ***********************************/
-/******************************************************************/
-
-
-/*** save ***/
-static auto save = bob::extension::FunctionDoc(
-  "save",
-  "Save the configuration of the PLDAMachine to a given HDF5 file"
-)
-.add_prototype("hdf5")
-.add_parameter("hdf5", ":py:class:`bob.io.base.HDF5File`", "An HDF5 file open for writing");
-static PyObject* PyBobLearnEMPLDAMachine_Save(PyBobLearnEMPLDAMachineObject* self,  PyObject* args, PyObject* kwargs) {
-
-  BOB_TRY
-
-  // get list of arguments
-  char** kwlist = save.kwlist(0);
-  PyBobIoHDF5FileObject* hdf5;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, PyBobIoHDF5File_Converter, &hdf5)) return 0;
-
-  auto hdf5_ = make_safe(hdf5);
-  self->cxx->save(*hdf5->f);
-
-  BOB_CATCH_MEMBER("cannot save the data", 0)
-  Py_RETURN_NONE;
-}
-
-/*** load ***/
-static auto load = bob::extension::FunctionDoc(
-  "load",
-  "Load the configuration of the PLDAMachine to a given HDF5 file"
-)
-.add_prototype("hdf5")
-.add_parameter("hdf5", ":py:class:`bob.io.base.HDF5File`", "An HDF5 file open for reading");
-static PyObject* PyBobLearnEMPLDAMachine_Load(PyBobLearnEMPLDAMachineObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  char** kwlist = load.kwlist(0);
-  PyBobIoHDF5FileObject* hdf5;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, PyBobIoHDF5File_Converter, &hdf5)) return 0;
-
-  auto hdf5_ = make_safe(hdf5);
-  self->cxx->load(*hdf5->f);
-
-  BOB_CATCH_MEMBER("cannot load the data", 0)
-  Py_RETURN_NONE;
-}
-
-
-/*** is_similar_to ***/
-static auto is_similar_to = bob::extension::FunctionDoc(
-  "is_similar_to",
-
-  "Compares this PLDAMachine with the ``other`` one to be approximately the same.",
-  "The optional values ``r_epsilon`` and ``a_epsilon`` refer to the "
-  "relative and absolute precision for the ``weights``, ``biases`` "
-  "and any other values internal to this machine."
-)
-.add_prototype("other, [r_epsilon], [a_epsilon]","output")
-.add_parameter("other", ":py:class:`bob.learn.em.PLDAMachine`", "A PLDAMachine object to be compared.")
-.add_parameter("r_epsilon", "float", "Relative precision.")
-.add_parameter("a_epsilon", "float", "Absolute precision.")
-.add_return("output","bool","True if it is similar, otherwise false.");
-static PyObject* PyBobLearnEMPLDAMachine_IsSimilarTo(PyBobLearnEMPLDAMachineObject* self, PyObject* args, PyObject* kwds) {
-
-  /* Parses input arguments in a single shot */
-  char** kwlist = is_similar_to.kwlist(0);
-
-  //PyObject* other = 0;
-  PyBobLearnEMPLDAMachineObject* other = 0;
-  double r_epsilon = 1.e-5;
-  double a_epsilon = 1.e-8;
-
-  if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|dd", kwlist,
-        &PyBobLearnEMPLDAMachine_Type, &other,
-        &r_epsilon, &a_epsilon)){
-
-        is_similar_to.print_usage();
-        return 0;
-  }
-
-  if (self->cxx->is_similar_to(*other->cxx, r_epsilon, a_epsilon))
-    Py_RETURN_TRUE;
-  else
-    Py_RETURN_FALSE;
-}
-
-
-/***** get_gamma *****/
-static auto get_gamma = bob::extension::FunctionDoc(
-  "get_gamma",
-  "Gets the :math:`\\gamma_a` matrix for a given :math:`a` (number of samples). "
-  ":math:`\\gamma_{a}=(Id + a F^T \\beta F)^{-1}= \\mathcal{F}_{a}`",
-  0,
-  true
-)
-.add_prototype("a","output")
-.add_parameter("a", "int", "Index")
-.add_return("output","array_like <float, 2D>","Get the :math:`\\gamma` matrix");
-static PyObject* PyBobLearnEMPLDAMachine_getGamma(PyBobLearnEMPLDAMachineObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  char** kwlist = get_gamma.kwlist(0);
-
-  int i = 0;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, &i)) return 0;
-
-  return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getGamma(i));
-  BOB_CATCH_MEMBER("`get_gamma` could not be read", 0)
-}
-
-
-/***** has_gamma *****/
-static auto has_gamma = bob::extension::FunctionDoc(
-  "has_gamma",
-  "Tells if the :math:`\\gamma_a` matrix for a given a (number of samples) exists. "
-  ":math:`\\gamma_a=(Id + a F^T \\beta F)^{-1}`",
-  0,
-  true
-)
-.add_prototype("a","output")
-.add_parameter("a", "int", "Index")
-.add_return("output","bool","");
-static PyObject* PyBobLearnEMPLDAMachine_hasGamma(PyBobLearnEMPLDAMachineObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  char** kwlist = has_gamma.kwlist(0);
-  int i = 0;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, &i)) return 0;
-
-  if(self->cxx->hasGamma(i))
-    Py_RETURN_TRUE;
-  else
-    Py_RETURN_FALSE;
- BOB_CATCH_MEMBER("`has_gamma` could not be read", 0)
-}
-
-
-/***** get_add_gamma *****/
-static auto get_add_gamma = bob::extension::FunctionDoc(
-  "get_add_gamma",
-   "Gets the :math:`gamma_a` matrix for a given :math:`f_a` (number of samples)."
-   " :math:`\\gamma_a=(Id + a F^T \\beta F)^{-1} =\\mathcal{F}_{a}`."
-   "Tries to find it from the base machine and then from this machine.",
-  0,
-  true
-)
-.add_prototype("a","output")
-.add_parameter("a", "int", "Index")
-.add_return("output","array_like <float, 2D>","");
-static PyObject* PyBobLearnEMPLDAMachine_getAddGamma(PyBobLearnEMPLDAMachineObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  char** kwlist = get_add_gamma.kwlist(0);
-
-  int i = 0;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, &i)) return 0;
-
-  return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getAddGamma(i));
-  BOB_CATCH_MEMBER("`get_add_gamma` could not be read", 0)
-}
-
-
-/***** has_log_like_const_term *****/
-static auto has_log_like_const_term = bob::extension::FunctionDoc(
-  "has_log_like_const_term",
-   "Tells if the log likelihood constant term for a given :math:`a` (number of samples) exists in this machine (does not check the base machine). "
-   ":math:`l_{a}=\\frac{a}{2} ( -D log(2\\pi) -log|\\Sigma| +log|\\alpha| +log|\\gamma_a|)`",
-  0,
-  true
-)
-.add_prototype("a","output")
-.add_parameter("a", "int", "Index")
-.add_return("output","bool","");
-static PyObject* PyBobLearnEMPLDAMachine_hasLogLikeConstTerm(PyBobLearnEMPLDAMachineObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  char** kwlist = has_log_like_const_term.kwlist(0);
-  int i = 0;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, &i)) return 0;
-
-  if(self->cxx->hasLogLikeConstTerm(i))
-    Py_RETURN_TRUE;
-  else
-    Py_RETURN_FALSE;
- BOB_CATCH_MEMBER("`has_log_like_const_term` could not be read", 0)
-}
-
-
-/***** get_add_log_like_const_term *****/
-static auto get_add_log_like_const_term = bob::extension::FunctionDoc(
-  "get_add_log_like_const_term",
-
-   "Gets the log likelihood constant term for a given :math:`a` (number of samples). "
-   ":math:`l_{a} = \\frac{a}{2} ( -D log(2\\pi) -log|\\Sigma| +log|\\alpha| +log|gamma_a|)`",
-  0,
-  true
-)
-.add_prototype("a","output")
-.add_parameter("a", "int", "Index")
-.add_return("output","float","");
-static PyObject* PyBobLearnEMPLDAMachine_getAddLogLikeConstTerm(PyBobLearnEMPLDAMachineObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  char** kwlist = get_add_log_like_const_term.kwlist(0);
-  int i = 0;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, &i)) return 0;
-
-  return Py_BuildValue("d",self->cxx->getAddLogLikeConstTerm(i));
-
-  BOB_CATCH_MEMBER("`get_add_log_like_const_term` could not be read", 0)
-}
-
-
-/***** get_log_like_const_term *****/
-static auto get_log_like_const_term = bob::extension::FunctionDoc(
-  "get_log_like_const_term",
-   "Gets the log likelihood constant term for a given :math:`a` (number of samples). "
-    ":math:`l_{a}=\\frac{a}{2}( -D log(2\\pi) -log|\\Sigma| +log|\\alpha| + log|\\gamma_a|)`",
-  0,
-  true
-)
-.add_prototype("a","output")
-.add_parameter("a", "int", "Index")
-.add_return("output","float","");
-static PyObject* PyBobLearnEMPLDAMachine_getLogLikeConstTerm(PyBobLearnEMPLDAMachineObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  char** kwlist = get_log_like_const_term.kwlist(0);
-  int i = 0;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, &i)) return 0;
-
-  return Py_BuildValue("d",self->cxx->getLogLikeConstTerm(i));
-
-  BOB_CATCH_MEMBER("`get_log_like_const_term` could not be read", 0)
-}
-
-/***** clear_maps *****/
-static auto clear_maps = bob::extension::FunctionDoc(
-  "clear_maps",
-  "Clears the maps (:math:`\\gamma_a` and loglike_constterm_a).",
-  0,
-  true
-)
-.add_prototype("");
-static PyObject* PyBobLearnEMPLDAMachine_clearMaps(PyBobLearnEMPLDAMachineObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  self->cxx->clearMaps();
-  Py_RETURN_NONE;
-
-  BOB_CATCH_MEMBER("`clear_maps` could not be read", 0)
-}
-
-
-/***** compute_log_likelihood *****/
-static auto compute_log_likelihood = bob::extension::FunctionDoc(
-  "compute_log_likelihood",
-  "Compute the log-likelihood of the given sample and (optionally) the enrolled samples",
-  0,
-  true
-)
-.add_prototype("sample,with_enrolled_samples","output")
-.add_parameter("sample", "array_like <float, 1D>,array_like <float, 2D>", "Sample")
-.add_parameter("with_enrolled_samples", "bool", "")
-.add_return("output","float","The log-likelihood");
-static PyObject* PyBobLearnEMPLDAMachine_computeLogLikelihood(PyBobLearnEMPLDAMachineObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  char** kwlist = compute_log_likelihood.kwlist(0);
-
-  PyBlitzArrayObject* samples;
-  PyObject* with_enrolled_samples = Py_True;
-
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&|O!", kwlist, &PyBlitzArray_Converter, &samples,
-                                                                  &PyBool_Type, &with_enrolled_samples)) return 0;
-  auto samples_ = make_safe(samples);
-
-  /*Using the proper method according to the dimension*/
-  if (samples->ndim==1)
-    return Py_BuildValue("d",self->cxx->computeLogLikelihood(*PyBlitzArrayCxx_AsBlitz<double,1>(samples), f(with_enrolled_samples)));
-  else
-    return Py_BuildValue("d",self->cxx->computeLogLikelihood(*PyBlitzArrayCxx_AsBlitz<double,2>(samples), f(with_enrolled_samples)));
-
-
-  BOB_CATCH_MEMBER("`compute_log_likelihood` could not be read", 0)
-}
-
-
-/***** log_likelihood_ratio *****/
-static auto log_likelihood_ratio = bob::extension::FunctionDoc(
-  "log_likelihood_ratio",
-  "Computes a log likelihood ratio from a 1D or 2D blitz::Array",
-  0,
-  true
-)
-.add_prototype("samples","output")
-.add_parameter("samples", "array_like <float, 1D>,array_like <float, 2D>", "Sample")
-.add_return("output","float","The log-likelihood ratio");
-static PyObject* PyBobLearnEMPLDAMachine_log_likelihood_ratio(PyBobLearnEMPLDAMachineObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  char** kwlist = log_likelihood_ratio.kwlist(0);
-
-  PyBlitzArrayObject* samples;
-
-  /*Convert to PyObject first to access the number of dimensions*/
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, &PyBlitzArray_Converter, &samples)) return 0;
-  auto samples_ = make_safe(samples);
-
-   //There are 2 methods in C++, one <double,1> and the another <double,2>
-  if(samples->ndim==1)
-    return Py_BuildValue("d",self->cxx->forward(*PyBlitzArrayCxx_AsBlitz<double,1>(samples)));
-  else
-    return Py_BuildValue("d",self->cxx->forward(*PyBlitzArrayCxx_AsBlitz<double,2>(samples)));
-
-  BOB_CATCH_MEMBER("log_likelihood_ratio could not be executed", 0)
-}
-
-
-static PyMethodDef PyBobLearnEMPLDAMachine_methods[] = {
-  {
-    save.name(),
-    (PyCFunction)PyBobLearnEMPLDAMachine_Save,
-    METH_VARARGS|METH_KEYWORDS,
-    save.doc()
-  },
-  {
-    load.name(),
-    (PyCFunction)PyBobLearnEMPLDAMachine_Load,
-    METH_VARARGS|METH_KEYWORDS,
-    load.doc()
-  },
-  {
-    is_similar_to.name(),
-    (PyCFunction)PyBobLearnEMPLDAMachine_IsSimilarTo,
-    METH_VARARGS|METH_KEYWORDS,
-    is_similar_to.doc()
-  },
-  {
-    get_gamma.name(),
-    (PyCFunction)PyBobLearnEMPLDAMachine_getGamma,
-    METH_VARARGS|METH_KEYWORDS,
-    get_gamma.doc()
-  },
-  {
-    has_gamma.name(),
-    (PyCFunction)PyBobLearnEMPLDAMachine_hasGamma,
-    METH_VARARGS|METH_KEYWORDS,
-    has_gamma.doc()
-  },
-  {
-    get_add_gamma.name(),
-    (PyCFunction)PyBobLearnEMPLDAMachine_getAddGamma,
-    METH_VARARGS|METH_KEYWORDS,
-    get_add_gamma.doc()
-  },
-  {
-    has_log_like_const_term.name(),
-    (PyCFunction)PyBobLearnEMPLDAMachine_hasLogLikeConstTerm,
-    METH_VARARGS|METH_KEYWORDS,
-    has_log_like_const_term.doc()
-  },
-  {
-    get_add_log_like_const_term.name(),
-    (PyCFunction)PyBobLearnEMPLDAMachine_getAddLogLikeConstTerm,
-    METH_VARARGS|METH_KEYWORDS,
-    get_add_log_like_const_term.doc()
-  },
-  {
-    get_log_like_const_term.name(),
-    (PyCFunction)PyBobLearnEMPLDAMachine_getLogLikeConstTerm,
-    METH_VARARGS|METH_KEYWORDS,
-    get_log_like_const_term.doc()
-  },
-  {
-    clear_maps.name(),
-    (PyCFunction)PyBobLearnEMPLDAMachine_clearMaps,
-    METH_NOARGS,
-    clear_maps.doc()
-  },
-  {
-    compute_log_likelihood.name(),
-    (PyCFunction)PyBobLearnEMPLDAMachine_computeLogLikelihood,
-    METH_VARARGS|METH_KEYWORDS,
-    compute_log_likelihood.doc()
-  },
-  {
-    log_likelihood_ratio.name(),
-    (PyCFunction)PyBobLearnEMPLDAMachine_log_likelihood_ratio,
-    METH_VARARGS|METH_KEYWORDS,
-    log_likelihood_ratio.doc()
-  },
-  {0} /* Sentinel */
-};
-
-
-/******************************************************************/
-/************ Module Section **************************************/
-/******************************************************************/
-
-// Define the JFA type struct; will be initialized later
-PyTypeObject PyBobLearnEMPLDAMachine_Type = {
-  PyVarObject_HEAD_INIT(0,0)
-  0
-};
-
-bool init_BobLearnEMPLDAMachine(PyObject* module)
-{
-  // initialize the type struct
-  PyBobLearnEMPLDAMachine_Type.tp_name      = PLDAMachine_doc.name();
-  PyBobLearnEMPLDAMachine_Type.tp_basicsize = sizeof(PyBobLearnEMPLDAMachineObject);
-  PyBobLearnEMPLDAMachine_Type.tp_flags     = Py_TPFLAGS_DEFAULT;
-  PyBobLearnEMPLDAMachine_Type.tp_doc       = PLDAMachine_doc.doc();
-
-  // set the functions
-  PyBobLearnEMPLDAMachine_Type.tp_new         = PyType_GenericNew;
-  PyBobLearnEMPLDAMachine_Type.tp_init        = reinterpret_cast<initproc>(PyBobLearnEMPLDAMachine_init);
-  PyBobLearnEMPLDAMachine_Type.tp_dealloc     = reinterpret_cast<destructor>(PyBobLearnEMPLDAMachine_delete);
-  PyBobLearnEMPLDAMachine_Type.tp_richcompare = reinterpret_cast<richcmpfunc>(PyBobLearnEMPLDAMachine_RichCompare);
-  PyBobLearnEMPLDAMachine_Type.tp_methods     = PyBobLearnEMPLDAMachine_methods;
-  PyBobLearnEMPLDAMachine_Type.tp_getset      = PyBobLearnEMPLDAMachine_getseters;
-  PyBobLearnEMPLDAMachine_Type.tp_call = reinterpret_cast<ternaryfunc>(PyBobLearnEMPLDAMachine_log_likelihood_ratio);
-
-
-  // check that everything is fine
-  if (PyType_Ready(&PyBobLearnEMPLDAMachine_Type) < 0) return false;
-
-  // add the type to the module
-  Py_INCREF(&PyBobLearnEMPLDAMachine_Type);
-  return PyModule_AddObject(module, "PLDAMachine", (PyObject*)&PyBobLearnEMPLDAMachine_Type) >= 0;
-}
diff --git a/bob/learn/em/plda_trainer.cpp b/bob/learn/em/plda_trainer.cpp
deleted file mode 100644
index 49842a3b086f4c3e865812a69a71eaedf553924d..0000000000000000000000000000000000000000
--- a/bob/learn/em/plda_trainer.cpp
+++ /dev/null
@@ -1,721 +0,0 @@
-/**
- * @author Tiago de Freitas Pereira <tiago.pereira@idiap.ch>
- * @date Wed 04 Feb 14:15:00 2015
- *
- * @brief Python API for bob::learn::em
- *
- * Copyright (C) 2011-2014 Idiap Research Institute, Martigny, Switzerland
- */
-
-#include "main.h"
-#include <boost/make_shared.hpp>
-#include <boost/assign.hpp>
-
-//Defining maps for each initializatio method
-static const std::map<std::string, bob::learn::em::PLDATrainer::InitFMethod> FMethod = boost::assign::map_list_of
-  ("RANDOM_F",  bob::learn::em::PLDATrainer::RANDOM_F)
-  ("BETWEEN_SCATTER", bob::learn::em::PLDATrainer::BETWEEN_SCATTER)
-  ;
-
-static const std::map<std::string, bob::learn::em::PLDATrainer::InitGMethod> GMethod = boost::assign::map_list_of
-  ("RANDOM_G",  bob::learn::em::PLDATrainer::RANDOM_G)
-  ("WITHIN_SCATTER", bob::learn::em::PLDATrainer::WITHIN_SCATTER)
-  ;
-
-static const std::map<std::string, bob::learn::em::PLDATrainer::InitSigmaMethod> SigmaMethod = boost::assign::map_list_of
-  ("RANDOM_SIGMA",  bob::learn::em::PLDATrainer::RANDOM_SIGMA)
-  ("VARIANCE_G", bob::learn::em::PLDATrainer::VARIANCE_G)
-  ("CONSTANT", bob::learn::em::PLDATrainer::CONSTANT)
-  ("VARIANCE_DATA", bob::learn::em::PLDATrainer::VARIANCE_DATA)
-  ;
-
-//String to type
-static inline bob::learn::em::PLDATrainer::InitFMethod string2FMethod(const std::string& o){
-  auto it = FMethod.find(o);
-  if (it == FMethod.end()) throw std::runtime_error("The given FMethod '" + o + "' is not known; choose one of ('RANDOM_F','BETWEEN_SCATTER')");
-  else return it->second;
-}
-
-static inline bob::learn::em::PLDATrainer::InitGMethod string2GMethod(const std::string& o){
-  auto it = GMethod.find(o);
-  if (it == GMethod.end()) throw std::runtime_error("The given GMethod '" + o + "' is not known; choose one of ('RANDOM_G','WITHIN_SCATTER')");
-  else return it->second;
-}
-
-static inline bob::learn::em::PLDATrainer::InitSigmaMethod string2SigmaMethod(const std::string& o){
-  auto it = SigmaMethod.find(o);
-  if (it == SigmaMethod.end()) throw std::runtime_error("The given SigmaMethod '" + o + "' is not known; choose one of ('RANDOM_SIGMA','VARIANCE_G', 'CONSTANT', 'VARIANCE_DATA')");
-  else return it->second;
-}
-
-//Type to string
-static inline const std::string& FMethod2string(bob::learn::em::PLDATrainer::InitFMethod o){
-  for (auto it = FMethod.begin(); it != FMethod.end(); ++it) if (it->second == o) return it->first;
-  throw std::runtime_error("The given FMethod type is not known");
-}
-
-static inline const std::string& GMethod2string(bob::learn::em::PLDATrainer::InitGMethod o){
-  for (auto it = GMethod.begin(); it != GMethod.end(); ++it) if (it->second == o) return it->first;
-  throw std::runtime_error("The given GMethod type is not known");
-}
-
-static inline const std::string& SigmaMethod2string(bob::learn::em::PLDATrainer::InitSigmaMethod o){
-  for (auto it = SigmaMethod.begin(); it != SigmaMethod.end(); ++it) if (it->second == o) return it->first;
-  throw std::runtime_error("The given SigmaMethod type is not known");
-}
-
-
-static inline bool f(PyObject* o){return o != 0 && PyObject_IsTrue(o) > 0;}  /* converts PyObject to bool and returns false if object is NULL */
-
-template <int N>
-int list_as_vector(PyObject* list, std::vector<blitz::Array<double,N> >& vec)
-{
-  for (int i=0; i<PyList_GET_SIZE(list); i++)
-  {
-    PyBlitzArrayObject* blitz_object;
-    if (!PyArg_Parse(PyList_GetItem(list, i), "O&", &PyBlitzArray_Converter, &blitz_object)){
-      PyErr_Format(PyExc_RuntimeError, "Expected numpy array object");
-      return -1;
-    }
-    auto blitz_object_ = make_safe(blitz_object);
-    vec.push_back(*PyBlitzArrayCxx_AsBlitz<double,N>(blitz_object));
-  }
-  return 0;
-}
-
-
-template <int N>
-static PyObject* vector_as_list(const std::vector<blitz::Array<double,N> >& vec)
-{
-  PyObject* list = PyList_New(vec.size());
-  for(size_t i=0; i<vec.size(); i++){
-    blitz::Array<double,N> numpy_array = vec[i];
-    PyObject* numpy_py_object = PyBlitzArrayCxx_AsNumpy(numpy_array);
-    PyList_SET_ITEM(list, i, numpy_py_object);
-  }
-  return list;
-}
-
-
-/******************************************************************/
-/************ Constructor Section *********************************/
-/******************************************************************/
-
-
-static auto PLDATrainer_doc = bob::extension::ClassDoc(
-  BOB_EXT_MODULE_PREFIX ".PLDATrainer",
-  "This class can be used to train the :math:`F`, :math:`G` and "
-  " :math:`\\Sigma` matrices and the mean vector :math:`\\mu` of a PLDA model."
-  "References: [ElShafey2014]_ [PrinceElder2007]_ [LiFu2012]_ ",
-  ""
-).add_constructor(
-  bob::extension::FunctionDoc(
-    "__init__",
-    "Default constructor.\n Initializes a new PLDA trainer. The "
-    "training stage will place the resulting components in the "
-    "PLDABase.",
-    "",
-    true
-  )
-  .add_prototype("use_sum_second_order","")
-  .add_prototype("other","")
-  .add_prototype("","")
-
-  .add_parameter("other", ":py:class:`bob.learn.em.PLDATrainer`", "A PLDATrainer object to be copied.")
-  .add_parameter("use_sum_second_order", "bool", "")
-);
-
-static int PyBobLearnEMPLDATrainer_init_copy(PyBobLearnEMPLDATrainerObject* self, PyObject* args, PyObject* kwargs) {
-
-  char** kwlist = PLDATrainer_doc.kwlist(1);
-  PyBobLearnEMPLDATrainerObject* o;
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", kwlist, &PyBobLearnEMPLDATrainer_Type, &o)){
-    PLDATrainer_doc.print_usage();
-    return -1;
-  }
-
-  self->cxx.reset(new bob::learn::em::PLDATrainer(*o->cxx));
-  return 0;
-}
-
-
-static int PyBobLearnEMPLDATrainer_init_bool(PyBobLearnEMPLDATrainerObject* self, PyObject* args, PyObject* kwargs) {
-
-  char** kwlist = PLDATrainer_doc.kwlist(0);
-  PyObject* use_sum_second_order = Py_False;
-
-  //Parsing the input argments
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|O!", kwlist, &PyBool_Type, &use_sum_second_order))
-    return -1;
-
-  self->cxx.reset(new bob::learn::em::PLDATrainer(f(use_sum_second_order)));
-  return 0;
-}
-
-
-static int PyBobLearnEMPLDATrainer_init(PyBobLearnEMPLDATrainerObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  // get the number of command line arguments
-  int nargs = (args?PyTuple_Size(args):0) + (kwargs?PyDict_Size(kwargs):0);
-
-  if(nargs==0)
-    return PyBobLearnEMPLDATrainer_init_bool(self, args, kwargs);
-  else if(nargs==1){
-    //Reading the input argument
-    PyObject* arg = 0;
-    if (PyTuple_Size(args))
-      arg = PyTuple_GET_ITEM(args, 0);
-    else {
-      PyObject* tmp = PyDict_Values(kwargs);
-      auto tmp_ = make_safe(tmp);
-      arg = PyList_GET_ITEM(tmp, 0);
-    }
-
-    if(PyBobLearnEMPLDATrainer_Check(arg))
-      // If the constructor input is PLDATrainer object
-      return PyBobLearnEMPLDATrainer_init_copy(self, args, kwargs);
-    else
-      return PyBobLearnEMPLDATrainer_init_bool(self, args, kwargs);
-  }
-  else{
-    PyErr_Format(PyExc_RuntimeError, "number of arguments mismatch - %s requires only 0 or 1 argument, but you provided %d (see help)", Py_TYPE(self)->tp_name, nargs);
-    PLDATrainer_doc.print_usage();
-    return -1;
-  }
-
-  BOB_CATCH_MEMBER("cannot create PLDATrainer", -1)
-  return 0;
-}
-
-
-static void PyBobLearnEMPLDATrainer_delete(PyBobLearnEMPLDATrainerObject* self) {
-  self->cxx.reset();
-  Py_TYPE(self)->tp_free((PyObject*)self);
-}
-
-
-int PyBobLearnEMPLDATrainer_Check(PyObject* o) {
-  return PyObject_IsInstance(o, reinterpret_cast<PyObject*>(&PyBobLearnEMPLDATrainer_Type));
-}
-
-
-static PyObject* PyBobLearnEMPLDATrainer_RichCompare(PyBobLearnEMPLDATrainerObject* self, PyObject* other, int op) {
-  BOB_TRY
-
-  if (!PyBobLearnEMPLDATrainer_Check(other)) {
-    PyErr_Format(PyExc_TypeError, "cannot compare `%s' with `%s'", Py_TYPE(self)->tp_name, Py_TYPE(other)->tp_name);
-    return 0;
-  }
-  auto other_ = reinterpret_cast<PyBobLearnEMPLDATrainerObject*>(other);
-  switch (op) {
-    case Py_EQ:
-      if (*self->cxx==*other_->cxx) Py_RETURN_TRUE; else Py_RETURN_FALSE;
-    case Py_NE:
-      if (*self->cxx==*other_->cxx) Py_RETURN_FALSE; else Py_RETURN_TRUE;
-    default:
-      Py_INCREF(Py_NotImplemented);
-      return Py_NotImplemented;
-  }
-  BOB_CATCH_MEMBER("cannot compare PLDATrainer objects", 0)
-}
-
-
-/******************************************************************/
-/************ Variables Section ***********************************/
-/******************************************************************/
-
-static auto z_second_order = bob::extension::VariableDoc(
-  "z_second_order",
-  "array_like <float, 3D>",
-  "",
-  ""
-);
-PyObject* PyBobLearnEMPLDATrainer_get_z_second_order(PyBobLearnEMPLDATrainerObject* self, void*){
-  BOB_TRY
-  //return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getZSecondOrder());
-  return vector_as_list(self->cxx->getZSecondOrder());
-  BOB_CATCH_MEMBER("z_second_order could not be read", 0)
-}
-
-
-static auto z_second_order_sum = bob::extension::VariableDoc(
-  "z_second_order_sum",
-  "array_like <float, 2D>",
-  "",
-  ""
-);
-PyObject* PyBobLearnEMPLDATrainer_get_z_second_order_sum(PyBobLearnEMPLDATrainerObject* self, void*){
-  BOB_TRY
-  return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getZSecondOrderSum());
-  BOB_CATCH_MEMBER("z_second_order_sum could not be read", 0)
-}
-
-
-static auto z_first_order = bob::extension::VariableDoc(
-  "z_first_order",
-  "array_like <float, 2D>",
-  "",
-  ""
-);
-PyObject* PyBobLearnEMPLDATrainer_get_z_first_order(PyBobLearnEMPLDATrainerObject* self, void*){
-  BOB_TRY
-  //return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getZFirstOrder());
-  return vector_as_list(self->cxx->getZFirstOrder());
-  BOB_CATCH_MEMBER("z_first_order could not be read", 0)
-}
-
-
-/***** init_f_method *****/
-static auto init_f_method = bob::extension::VariableDoc(
-  "init_f_method",
-  "str",
-  "The method used for the initialization of :math:`$F$`.",
-  "Possible values are: ('RANDOM_F', 'BETWEEN_SCATTER')"
-);
-PyObject* PyBobLearnEMPLDATrainer_getFMethod(PyBobLearnEMPLDATrainerObject* self, void*) {
-  BOB_TRY
-  return Py_BuildValue("s", FMethod2string(self->cxx->getInitFMethod()).c_str());
-  BOB_CATCH_MEMBER("init_f_method method could not be read", 0)
-}
-int PyBobLearnEMPLDATrainer_setFMethod(PyBobLearnEMPLDATrainerObject* self, PyObject* value, void*) {
-  BOB_TRY
-
-  if (!PyString_Check(value)){
-    PyErr_Format(PyExc_RuntimeError, "%s %s expects an str", Py_TYPE(self)->tp_name, init_f_method.name());
-    return -1;
-  }
-  self->cxx->setInitFMethod(string2FMethod(PyString_AS_STRING(value)));
-
-  return 0;
-  BOB_CATCH_MEMBER("init_f_method method could not be set", -1)
-}
-
-
-/***** init_g_method *****/
-static auto init_g_method = bob::extension::VariableDoc(
-  "init_g_method",
-  "str",
-  "The method used for the initialization of :math:`$G$`.",
-  "Possible values are: ('RANDOM_G', 'WITHIN_SCATTER')"
-);
-PyObject* PyBobLearnEMPLDATrainer_getGMethod(PyBobLearnEMPLDATrainerObject* self, void*) {
-  BOB_TRY
-  return Py_BuildValue("s", GMethod2string(self->cxx->getInitGMethod()).c_str());
-  BOB_CATCH_MEMBER("init_g_method method could not be read", 0)
-}
-int PyBobLearnEMPLDATrainer_setGMethod(PyBobLearnEMPLDATrainerObject* self, PyObject* value, void*) {
-  BOB_TRY
-
-  if (!PyString_Check(value)){
-    PyErr_Format(PyExc_RuntimeError, "%s %s expects an str", Py_TYPE(self)->tp_name, init_g_method.name());
-    return -1;
-  }
-  self->cxx->setInitGMethod(string2GMethod(PyString_AS_STRING(value)));
-
-  return 0;
-  BOB_CATCH_MEMBER("init_g_method method could not be set", -1)
-}
-
-/***** init_sigma_method *****/
-static auto init_sigma_method = bob::extension::VariableDoc(
-  "init_sigma_method",
-  "str",
-  "The method used for the initialization of :math:`$\\Sigma$`.",
-  "Possible values are: ('RANDOM_SIGMA', 'VARIANCE_G', 'CONSTANT', 'VARIANCE_DATA')"
-);
-PyObject* PyBobLearnEMPLDATrainer_getSigmaMethod(PyBobLearnEMPLDATrainerObject* self, void*) {
-  BOB_TRY
-  return Py_BuildValue("s", SigmaMethod2string(self->cxx->getInitSigmaMethod()).c_str());
-  BOB_CATCH_MEMBER("init_sigma_method method could not be read", 0)
-}
-int PyBobLearnEMPLDATrainer_setSigmaMethod(PyBobLearnEMPLDATrainerObject* self, PyObject* value, void*) {
-  BOB_TRY
-
-  if (!PyString_Check(value)){
-    PyErr_Format(PyExc_RuntimeError, "%s %s expects an str", Py_TYPE(self)->tp_name, init_sigma_method.name());
-    return -1;
-  }
-  self->cxx->setInitSigmaMethod(string2SigmaMethod(PyString_AS_STRING(value)));
-
-  return 0;
-  BOB_CATCH_MEMBER("init_sigma_method method could not be set", -1)
-}
-
-
-static auto use_sum_second_order = bob::extension::VariableDoc(
-  "use_sum_second_order",
-  "bool",
-  "Tells whether the second order statistics are stored during the training procedure, or only their sum.",
-  ""
-);
-PyObject* PyBobLearnEMPLDATrainer_getUseSumSecondOrder(PyBobLearnEMPLDATrainerObject* self, void*){
-  BOB_TRY
-  return Py_BuildValue("O",self->cxx->getUseSumSecondOrder()?Py_True:Py_False);
-  BOB_CATCH_MEMBER("use_sum_second_order could not be read", 0)
-}
-int PyBobLearnEMPLDATrainer_setUseSumSecondOrder(PyBobLearnEMPLDATrainerObject* self, PyObject* value, void*) {
-  BOB_TRY
-
-  if (!PyBool_Check(value)){
-    PyErr_Format(PyExc_RuntimeError, "%s %s expects an str", Py_TYPE(self)->tp_name, use_sum_second_order.name());
-    return -1;
-  }
-  self->cxx->setUseSumSecondOrder(f(value));
-
-  return 0;
-  BOB_CATCH_MEMBER("use_sum_second_order method could not be set", -1)
-}
-
-
-
-static PyGetSetDef PyBobLearnEMPLDATrainer_getseters[] = {
-  {
-   z_first_order.name(),
-   (getter)PyBobLearnEMPLDATrainer_get_z_first_order,
-   0,
-   z_first_order.doc(),
-   0
-  },
-  {
-   z_second_order_sum.name(),
-   (getter)PyBobLearnEMPLDATrainer_get_z_second_order_sum,
-   0,
-   z_second_order_sum.doc(),
-   0
-  },
-  {
-   z_second_order.name(),
-   (getter)PyBobLearnEMPLDATrainer_get_z_second_order,
-   0,
-   z_second_order.doc(),
-   0
-  },
-  {
-   init_f_method.name(),
-   (getter)PyBobLearnEMPLDATrainer_getFMethod,
-   (setter)PyBobLearnEMPLDATrainer_setFMethod,
-   init_f_method.doc(),
-   0
-  },
-  {
-   init_g_method.name(),
-   (getter)PyBobLearnEMPLDATrainer_getGMethod,
-   (setter)PyBobLearnEMPLDATrainer_setGMethod,
-   init_g_method.doc(),
-   0
-  },
-  {
-   init_sigma_method.name(),
-   (getter)PyBobLearnEMPLDATrainer_getSigmaMethod,
-   (setter)PyBobLearnEMPLDATrainer_setSigmaMethod,
-   init_sigma_method.doc(),
-   0
-  },
-  {
-   use_sum_second_order.name(),
-   (getter)PyBobLearnEMPLDATrainer_getUseSumSecondOrder,
-   (setter)PyBobLearnEMPLDATrainer_setUseSumSecondOrder,
-   use_sum_second_order.doc(),
-   0
-  },
-  {0}  // Sentinel
-};
-
-
-/******************************************************************/
-/************ Functions Section ***********************************/
-/******************************************************************/
-
-/*** initialize ***/
-static auto initialize = bob::extension::FunctionDoc(
-  "initialize",
-  "Initialization before the EM steps",
-  "",
-  true
-)
-.add_prototype("plda_base, data, [rng]")
-.add_parameter("plda_base", ":py:class:`bob.learn.em.PLDABase`", "PLDAMachine Object")
-.add_parameter("data", "list", "")
-.add_parameter("rng", ":py:class:`bob.core.random.mt19937`", "The Mersenne Twister mt19937 random generator used for the initialization of subspaces/arrays before the EM loop.");
-static PyObject* PyBobLearnEMPLDATrainer_initialize(PyBobLearnEMPLDATrainerObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  /* Parses input arguments in a single shot */
-  char** kwlist = initialize.kwlist(0);
-
-  PyBobLearnEMPLDABaseObject* plda_base = 0;
-  PyObject* data = 0;
-  PyBoostMt19937Object* rng = 0;
-
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O!|O!", kwlist, &PyBobLearnEMPLDABase_Type, &plda_base,
-                                                                 &PyList_Type, &data,
-                                                                 &PyBoostMt19937_Type, &rng)) return 0;
-
-  std::vector<blitz::Array<double,2> > data_vector;
-  if(list_as_vector(data ,data_vector)==0){
-    if(rng){
-      self->cxx->setRng(rng->rng);
-    }
-
-    self->cxx->initialize(*plda_base->cxx, data_vector);
-  }
-  else
-    return 0;
-
-  BOB_CATCH_MEMBER("cannot perform the initialize method", 0)
-
-  Py_RETURN_NONE;
-}
-
-
-/*** e_step ***/
-static auto e_step = bob::extension::FunctionDoc(
-  "e_step",
-  "Expectation step before the EM steps",
-  "",
-  true
-)
-.add_prototype("plda_base,data")
-.add_parameter("plda_base", ":py:class:`bob.learn.em.PLDABase`", "PLDAMachine Object")
-.add_parameter("data", "list", "");
-static PyObject* PyBobLearnEMPLDATrainer_e_step(PyBobLearnEMPLDATrainerObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  /* Parses input arguments in a single shot */
-  char** kwlist = e_step.kwlist(0);
-
-  PyBobLearnEMPLDABaseObject* plda_base = 0;
-  PyObject* data = 0;
-
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O!", kwlist, &PyBobLearnEMPLDABase_Type, &plda_base,
-                                                                 &PyList_Type, &data)) return 0;
-
-  std::vector<blitz::Array<double,2> > data_vector;
-  if(list_as_vector(data ,data_vector)==0)
-    self->cxx->eStep(*plda_base->cxx, data_vector);
-  else
-    return 0;
-
-  BOB_CATCH_MEMBER("cannot perform the e_step method", 0)
-
-  Py_RETURN_NONE;
-}
-
-
-/*** m_step ***/
-static auto m_step = bob::extension::FunctionDoc(
-  "m_step",
-  "Maximization step ",
-  "",
-  true
-)
-.add_prototype("plda_base,data")
-.add_parameter("plda_base", ":py:class:`bob.learn.em.PLDABase`", "PLDAMachine Object")
-.add_parameter("data", "list", "");
-static PyObject* PyBobLearnEMPLDATrainer_m_step(PyBobLearnEMPLDATrainerObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  /* Parses input arguments in a single shot */
-  char** kwlist = m_step.kwlist(0);
-
-  PyBobLearnEMPLDABaseObject* plda_base = 0;
-  PyObject* data = 0;
-
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O!", kwlist, &PyBobLearnEMPLDABase_Type, &plda_base,
-                                                                 &PyList_Type, &data)) return 0;
-
-  std::vector<blitz::Array<double,2> > data_vector;
-  if(list_as_vector(data ,data_vector)==0)
-    self->cxx->mStep(*plda_base->cxx, data_vector);
-  else
-    return 0;
-
-  BOB_CATCH_MEMBER("cannot perform the m_step method", 0)
-
-  Py_RETURN_NONE;
-}
-
-
-/*** finalize ***/
-static auto finalize = bob::extension::FunctionDoc(
-  "finalize",
-  "finalize before the EM steps",
-  "",
-  true
-)
-.add_prototype("plda_base,data")
-.add_parameter("plda_base", ":py:class:`bob.learn.em.PLDABase`", "PLDAMachine Object")
-.add_parameter("data", "list", "");
-static PyObject* PyBobLearnEMPLDATrainer_finalize(PyBobLearnEMPLDATrainerObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  /* Parses input arguments in a single shot */
-  char** kwlist = finalize.kwlist(0);
-
-  PyBobLearnEMPLDABaseObject* plda_base = 0;
-  PyObject* data = 0;
-
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O!", kwlist, &PyBobLearnEMPLDABase_Type, &plda_base,
-                                                                 &PyList_Type, &data)) return 0;
-
-  std::vector<blitz::Array<double,2> > data_vector;
-  if(list_as_vector(data ,data_vector)==0)
-    self->cxx->finalize(*plda_base->cxx, data_vector);
-  else
-    return 0;
-
-  BOB_CATCH_MEMBER("cannot perform the finalize method", 0)
-
-  Py_RETURN_NONE;
-}
-
-
-
-/*** enroll ***/
-static auto enroll = bob::extension::FunctionDoc(
-  "enroll",
-  "Main procedure for enrolling a PLDAMachine",
-  "",
-  true
-)
-.add_prototype("plda_machine,data")
-.add_parameter("plda_machine", ":py:class:`bob.learn.em.PLDAMachine`", "PLDAMachine Object")
-.add_parameter("data", "list", "");
-static PyObject* PyBobLearnEMPLDATrainer_enroll(PyBobLearnEMPLDATrainerObject* self, PyObject* args, PyObject* kwargs) {
-  BOB_TRY
-
-  /* Parses input arguments in a single shot */
-  char** kwlist = enroll.kwlist(0);
-
-  PyBobLearnEMPLDAMachineObject* plda_machine = 0;
-  PyBlitzArrayObject* data = 0;
-
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O&", kwlist, &PyBobLearnEMPLDAMachine_Type, &plda_machine,
-                                                                 &PyBlitzArray_Converter, &data)) return 0;
-
-  auto data_ = make_safe(data);
-  self->cxx->enroll(*plda_machine->cxx, *PyBlitzArrayCxx_AsBlitz<double,2>(data));
-
-  BOB_CATCH_MEMBER("cannot perform the enroll method", 0)
-
-  Py_RETURN_NONE;
-}
-
-
-/*** is_similar_to ***/
-static auto is_similar_to = bob::extension::FunctionDoc(
-  "is_similar_to",
-
-  "Compares this PLDATrainer with the ``other`` one to be approximately the same.",
-  "The optional values ``r_epsilon`` and ``a_epsilon`` refer to the "
-  "relative and absolute precision for the ``weights``, ``biases`` "
-  "and any other values internal to this machine."
-)
-.add_prototype("other, [r_epsilon], [a_epsilon]","output")
-.add_parameter("other", ":py:class:`bob.learn.em.PLDAMachine`", "A PLDAMachine object to be compared.")
-.add_parameter("r_epsilon", "float", "Relative precision.")
-.add_parameter("a_epsilon", "float", "Absolute precision.")
-.add_return("output","bool","True if it is similar, otherwise false.");
-static PyObject* PyBobLearnEMPLDATrainer_IsSimilarTo(PyBobLearnEMPLDATrainerObject* self, PyObject* args, PyObject* kwds) {
-
-  /* Parses input arguments in a single shot */
-  char** kwlist = is_similar_to.kwlist(0);
-
-  //PyObject* other = 0;
-  PyBobLearnEMPLDATrainerObject* other = 0;
-  double r_epsilon = 1.e-5;
-  double a_epsilon = 1.e-8;
-
-  if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|dd", kwlist,
-        &PyBobLearnEMPLDATrainer_Type, &other,
-        &r_epsilon, &a_epsilon)){
-
-        is_similar_to.print_usage();
-        return 0;
-  }
-
-  if (self->cxx->is_similar_to(*other->cxx, r_epsilon, a_epsilon))
-    Py_RETURN_TRUE;
-  else
-    Py_RETURN_FALSE;
-}
-
-
-
-static PyMethodDef PyBobLearnEMPLDATrainer_methods[] = {
-  {
-    initialize.name(),
-    (PyCFunction)PyBobLearnEMPLDATrainer_initialize,
-    METH_VARARGS|METH_KEYWORDS,
-    initialize.doc()
-  },
-  {
-    e_step.name(),
-    (PyCFunction)PyBobLearnEMPLDATrainer_e_step,
-    METH_VARARGS|METH_KEYWORDS,
-    e_step.doc()
-  },
-  {
-    m_step.name(),
-    (PyCFunction)PyBobLearnEMPLDATrainer_m_step,
-    METH_VARARGS|METH_KEYWORDS,
-    m_step.doc()
-  },
-  {
-    finalize.name(),
-    (PyCFunction)PyBobLearnEMPLDATrainer_finalize,
-    METH_VARARGS|METH_KEYWORDS,
-    finalize.doc()
-  },
-  {
-    enroll.name(),
-    (PyCFunction)PyBobLearnEMPLDATrainer_enroll,
-    METH_VARARGS|METH_KEYWORDS,
-    enroll.doc()
-  },
-  {
-    is_similar_to.name(),
-    (PyCFunction)PyBobLearnEMPLDATrainer_IsSimilarTo,
-    METH_VARARGS|METH_KEYWORDS,
-    is_similar_to.doc()
-  },
-  {0} /* Sentinel */
-};
-
-
-/******************************************************************/
-/************ Module Section **************************************/
-/******************************************************************/
-
-// Define the Gaussian type struct; will be initialized later
-PyTypeObject PyBobLearnEMPLDATrainer_Type = {
-  PyVarObject_HEAD_INIT(0,0)
-  0
-};
-
-bool init_BobLearnEMPLDATrainer(PyObject* module)
-{
-  // initialize the type struct
-  PyBobLearnEMPLDATrainer_Type.tp_name      = PLDATrainer_doc.name();
-  PyBobLearnEMPLDATrainer_Type.tp_basicsize = sizeof(PyBobLearnEMPLDATrainerObject);
-  PyBobLearnEMPLDATrainer_Type.tp_flags     = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE;//Enable the class inheritance;
-  PyBobLearnEMPLDATrainer_Type.tp_doc       = PLDATrainer_doc.doc();
-
-  // set the functions
-  PyBobLearnEMPLDATrainer_Type.tp_new          = PyType_GenericNew;
-  PyBobLearnEMPLDATrainer_Type.tp_init         = reinterpret_cast<initproc>(PyBobLearnEMPLDATrainer_init);
-  PyBobLearnEMPLDATrainer_Type.tp_dealloc      = reinterpret_cast<destructor>(PyBobLearnEMPLDATrainer_delete);
-  PyBobLearnEMPLDATrainer_Type.tp_richcompare = reinterpret_cast<richcmpfunc>(PyBobLearnEMPLDATrainer_RichCompare);
-  PyBobLearnEMPLDATrainer_Type.tp_methods      = PyBobLearnEMPLDATrainer_methods;
-  PyBobLearnEMPLDATrainer_Type.tp_getset       = PyBobLearnEMPLDATrainer_getseters;
-  //PyBobLearnEMPLDATrainer_Type.tp_call         = reinterpret_cast<ternaryfunc>(PyBobLearnEMPLDATrainer_compute_likelihood);
-
-
-  // check that everything is fine
-  if (PyType_Ready(&PyBobLearnEMPLDATrainer_Type) < 0) return false;
-
-  // add the type to the module
-  Py_INCREF(&PyBobLearnEMPLDATrainer_Type);
-  return PyModule_AddObject(module, "PLDATrainer", (PyObject*)&PyBobLearnEMPLDATrainer_Type) >= 0;
-}
diff --git a/bob/learn/em/train.py b/bob/learn/em/train.py
deleted file mode 100644
index e0e500ba8a2dacdc4f76ee0f5bcacbf9848cb9cd..0000000000000000000000000000000000000000
--- a/bob/learn/em/train.py
+++ /dev/null
@@ -1,247 +0,0 @@
-#!/usr/bin/env python
-# vim: set fileencoding=utf-8 :
-# Tiago de Freitas Pereira <tiago.pereira@idiap.ch>
-# Fri Feb 13 13:18:10 2015 +0200
-#
-# Copyright (C) 2011-2015 Idiap Research Institute, Martigny, Switzerland
-import numpy
-from ._library import *
-import logging
-from multiprocessing.pool import ThreadPool
-
-logger = logging.getLogger(__name__)
-
-
-def _set_average(trainer, trainers, machine, data, trainer_type):
-  """_set_average(trainer, data) -> None
-
-  This function computes the average of the given data and sets it to the given machine.
-
-  This function works for different types of trainers, and can be used to parallelize the training.
-  For some trainers, the data is returned instead of set in the trainer.
-
-  **Parameters:**
-
-  trainer : one of :py:class:`KMeansTrainer`, :py:class:`MAP_GMMTrainer`, :py:class:`ML_GMMTrainer`, :py:class:`ISVTrainer`, :py:class:`IVectorTrainer`, :py:class:`PLDATrainer`, :py:class:`EMPCATrainer`
-    The trainer to set the data to.
-
-  trainers : [ trainer ]
-    The list of trainer objects that were used in the parallel training process.
-    All trainers must be of the same class as the ``trainer``.
-
-  data : [ object ]
-    The list of data objects that should be set to the trainer.
-    Usually this list is generated by parallelizing the e-step of the ``trainer``.
-  """
-
-  if trainer_type == "KMeansTrainer":
-    # K-Means statistics
-    trainer.reset_accumulators(machine)
-    for t in trainers:
-      trainer.zeroeth_order_statistics = trainer.zeroeth_order_statistics + t.zeroeth_order_statistics
-      trainer.first_order_statistics = trainer.first_order_statistics + t.first_order_statistics
-      trainer.average_min_distance = trainer.average_min_distance + t.average_min_distance
-
-    trainer.average_min_distance /= data.shape[0]
-
-  elif trainer_type in ("ML_GMMTrainer", "MAP_GMMTrainer"):
-    # GMM statistics
-    trainer.gmm_statistics = trainers[0].gmm_statistics
-    for t in trainers[1:]:
-      trainer.gmm_statistics += t.gmm_statistics
-
-  elif trainer_type == "IVectorTrainer":
-    # GMM statistics
-    trainer.reset_accumulators(machine)
-    trainer.acc_fnormij_wij = trainers[0].acc_fnormij_wij
-    trainer.acc_nij_wij2 = trainers[0].acc_nij_wij2
-    trainer.acc_nij = trainers[0].acc_nij
-    trainer.acc_snormij = trainers[0].acc_snormij
-    
-    for t in trainers[1:]:
-      trainer.acc_fnormij_wij = trainer.acc_fnormij_wij + t.acc_fnormij_wij
-      trainer.acc_nij_wij2    = trainer.acc_nij_wij2 + t.acc_nij_wij2
-      trainer.acc_nij         = trainer.acc_nij + t.acc_nij
-      trainer.acc_snormij     = trainer.acc_snormij + t.acc_snormij
-
-
-  else:
-    raise NotImplementedError("Implement Me!")
-
-
-def _parallel_e_step(args):
-  """This function applies the e_step of the given trainer (first argument) on the given data (second argument).
-  It is called by each parallel process.
-  """
-  trainer, machine, data = args
-  trainer.e_step(machine, data)
-
-
-def train(trainer, machine, data, max_iterations=50, convergence_threshold=None, initialize=True, rng=None, check_inputs=True, pool=None, trainer_type=None):
-
-  """
-  Trains a machine given a trainer and the proper data
-
-  **Parameters**:
-    trainer : one of :py:class:`KMeansTrainer`, :py:class:`MAP_GMMTrainer`, :py:class:`ML_GMMTrainer`, :py:class:`ISVTrainer`, :py:class:`IVectorTrainer`, :py:class:`PLDATrainer`, :py:class:`EMPCATrainer`
-      A trainer mechanism
-    machine : one of :py:class:`KMeansMachine`, :py:class:`GMMMachine`, :py:class:`ISVBase`, :py:class:`IVectorMachine`, :py:class:`PLDAMachine`, :py:class:`bob.learn.linear.Machine`
-      A container machine
-    data : array_like <float, 2D>
-      The data to be trained
-    max_iterations : int
-      The maximum number of iterations to train a machine
-    convergence_threshold : float
-      The convergence threshold to train a machine. If None, the training procedure will stop with the iterations criteria
-    initialize : bool
-      If True, runs the initialization procedure
-    rng :  :py:class:`bob.core.random.mt19937`
-      The Mersenne Twister mt19937 random generator used for the initialization of subspaces/arrays before the EM loop
-    check_inputs:
-      Shallow checks in the inputs. Check for inf and NaN
-    pool : ``int`` or ``multiprocessing.ThreadPool`` or ``None``
-      If given, the provided process pool will be used to parallelize the M-step of the
-      EM algorithm. You should provide a ThreadPool not a multi process Pool. If pool is
-      an integer, it will be used to create a ThreadPool with that many processes.
-    trainer_type : ``str`` or ``None``
-      This is used for the parallel e_step method to see how several processes' data can
-      be merged into one trainer before the m_step. By default
-      ``trainer.__class__.__name__`` is used. This is useful if you have custom trainers
-      and want to use this function.
-  """
-  if check_inputs and isinstance(data, numpy.ndarray):
-    sum_data = numpy.sum(data)
-
-    if numpy.isinf(sum_data):
-        raise ValueError("Please, check your inputs; numpy.inf detected in `data` ")
-
-    if numpy.isnan(sum_data):
-        raise ValueError("Please, check your inputs; numpy.nan detected in `data` ")
-
-  if isinstance(pool, int):
-    pool = ThreadPool(pool)
-
-  if trainer_type is None:
-    trainer_type = trainer.__class__.__name__
-
-  def _e_step(trainer, machine, data):
-
-    # performs the e-step, possibly in parallel
-    if pool is None:
-      # use only one core
-      trainer.e_step(machine, data)
-    else:
-
-      # use the given process pool
-      n_processes = pool._processes
-
-      # Mapping references of the data
-      split_data = []
-      offset = 0
-      
-      step = int(len(data) // n_processes)
-   
-      for p in range(n_processes):
-        if p == n_processes - 1:
-          # take all the data in the last chunk
-          split_data.append(data[offset:])
-        else:
-          split_data.append(data[offset: offset + step])
-
-        offset += step
-
-      # create trainers for each process
-      trainers = [trainer.__class__(trainer) for p in range(n_processes)]
-      # no need to copy the machines
-      machines = [machine.__class__(machine) for p in range(n_processes)]
-      # call the parallel processes
-      pool.map(_parallel_e_step, zip(trainers, machines, split_data))
-      # update the trainer with the data of the other trainers
-      _set_average(trainer, trainers, machine, data, trainer_type)
-
-  # Initialization
-  if initialize:
-    if rng is not None:
-      trainer.initialize(machine, data, rng)
-    else:
-      trainer.initialize(machine, data)
-
-  _e_step(trainer, machine, data)
-  average_output          = 0
-  average_output_previous = 0
-
-  if hasattr(trainer,"compute_likelihood"):
-    average_output          = trainer.compute_likelihood(machine)
-
-  for i in range(max_iterations):
-    logger.info("Iteration = %d/%d", i, max_iterations)
-    average_output_previous = average_output
-    trainer.m_step(machine, data)
-    _e_step(trainer, machine,data)
-
-    if hasattr(trainer,"compute_likelihood"):
-      average_output = trainer.compute_likelihood(machine)
-
-      if isinstance(machine, KMeansMachine):
-        logger.info("average euclidean distance = %f", average_output)
-      else:
-        logger.info("log likelihood = %f", average_output)
-
-      convergence_value = abs((average_output_previous - average_output)/average_output_previous)
-      logger.info("convergence value = %f",convergence_value)
-
-      #Terminates if converged (and likelihood computation is set)
-      if convergence_threshold is not None and convergence_value <= convergence_threshold:
-        break
-  if hasattr(trainer,"finalize"):
-    trainer.finalize(machine, data)
-
-
-def train_jfa(trainer, jfa_base, data, max_iterations=10, initialize=True, rng=None):
-  """
-  Trains a :py:class:`bob.learn.em.JFABase` given a :py:class:`bob.learn.em.JFATrainer` and the proper data
-
-  **Parameters**:
-    trainer : :py:class:`bob.learn.em.JFATrainer`
-      A JFA trainer mechanism
-    jfa_base : :py:class:`bob.learn.em.JFABase`
-      A container machine
-    data : [[:py:class:`bob.learn.em.GMMStats`]]
-      The data to be trained
-    max_iterations : int
-      The maximum number of iterations to train a machine
-    initialize : bool
-      If True, runs the initialization procedure
-    rng :  :py:class:`bob.core.random.mt19937`
-      The Mersenne Twister mt19937 random generator used for the initialization of subspaces/arrays before the EM loops
-  """
-
-  if initialize:
-    if rng is not None:
-      trainer.initialize(jfa_base, data, rng)
-    else:
-      trainer.initialize(jfa_base, data)
-
-  # V Subspace
-  logger.info("V subspace estimation...")
-  for i in range(max_iterations):
-    logger.info("Iteration = %d/%d", i, max_iterations)
-    trainer.e_step_v(jfa_base, data)
-    trainer.m_step_v(jfa_base, data)
-  trainer.finalize_v(jfa_base, data)
-
-  # U subspace
-  logger.info("U subspace estimation...")
-  for i in range(max_iterations):
-    logger.info("Iteration = %d/%d", i, max_iterations)
-    trainer.e_step_u(jfa_base, data)
-    trainer.m_step_u(jfa_base, data)
-  trainer.finalize_u(jfa_base, data)
-
-  # D subspace
-  logger.info("D subspace estimation...")
-  for i in range(max_iterations):
-    logger.info("Iteration = %d/%d", i, max_iterations)
-    trainer.e_step_d(jfa_base, data)
-    trainer.m_step_d(jfa_base, data)
-  trainer.finalize_d(jfa_base, data)
diff --git a/bob/learn/em/version.cpp b/bob/learn/em/version.cpp
deleted file mode 100644
index 866894fb97b991c1124d6f0766684056d719a1ba..0000000000000000000000000000000000000000
--- a/bob/learn/em/version.cpp
+++ /dev/null
@@ -1,91 +0,0 @@
-/**
- * @author Laurent El Shafey <Laurent.El-Shafey@idiap.ch>
- * @date Mon Apr 14 20:43:48 CEST 2014
- *
- * @brief Binds configuration information available from bob
- */
-
-#include <bob.learn.em/config.h>
-
-#define BOB_IMPORT_VERSION
-#include <bob.blitz/config.h>
-#include <bob.blitz/cleanup.h>
-#include <bob.core/config.h>
-#include <bob.io.base/config.h>
-#include <bob.sp/config.h>
-#include <bob.math/config.h>
-#include <bob.learn.activation/config.h>
-#include <bob.learn.linear/config.h>
-
-
-static PyObject* build_version_dictionary() {
-
-  PyObject* retval = PyDict_New();
-  if (!retval) return 0;
-  auto retval_ = make_safe(retval);
-
-  if (!dict_steal(retval, "Blitz++", blitz_version())) return 0;
-  if (!dict_steal(retval, "Boost", boost_version())) return 0;
-  if (!dict_steal(retval, "Compiler", compiler_version())) return 0;
-  if (!dict_steal(retval, "Python", python_version())) return 0;
-  if (!dict_steal(retval, "NumPy", numpy_version())) return 0;
-  if (!dict_steal(retval, "HDF5", hdf5_version())) return 0;
-  if (!dict_steal(retval, "bob.blitz", bob_blitz_version())) return 0;
-  if (!dict_steal(retval, "bob.core", bob_core_version())) return 0;
-  if (!dict_steal(retval, "bob.io.base", bob_io_base_version())) return 0;
-  if (!dict_steal(retval, "bob.sp", bob_sp_version())) return 0;
-  if (!dict_steal(retval, "bob.math", bob_math_version())) return 0;
-  if (!dict_steal(retval, "bob.learn.activation", bob_learn_activation_version())) return 0;
-  if (!dict_steal(retval, "bob.learn.linear", bob_learn_linear_version())) return 0;
-
-  return Py_BuildValue("O", retval);
-}
-
-static PyMethodDef module_methods[] = {
-    {0}  /* Sentinel */
-};
-
-PyDoc_STRVAR(module_docstr,
-"Information about software used to compile the C++ Bob API"
-);
-
-#if PY_VERSION_HEX >= 0x03000000
-static PyModuleDef module_definition = {
-  PyModuleDef_HEAD_INIT,
-  BOB_EXT_MODULE_NAME,
-  module_docstr,
-  -1,
-  module_methods,
-  0, 0, 0, 0
-};
-#endif
-
-static PyObject* create_module (void) {
-
-# if PY_VERSION_HEX >= 0x03000000
-  PyObject* module = PyModule_Create(&module_definition);
-  auto module_ = make_xsafe(module);
-  const char* ret = "O";
-# else
-  PyObject* module = Py_InitModule3(BOB_EXT_MODULE_NAME, module_methods, module_docstr);
-  const char* ret = "N";
-# endif
-  if (!module) return 0;
-
-  /* register version numbers and constants */
-  if (PyModule_AddIntConstant(module, "api", BOB_LEARN_EM_API_VERSION) < 0) return 0;
-  if (PyModule_AddStringConstant(module, "module", BOB_EXT_MODULE_VERSION) < 0) return 0;
-
-  PyObject* externals = build_version_dictionary();
-  if (!externals) return 0;
-  if (PyModule_AddObject(module, "externals", externals) < 0) return 0;
-
-  return Py_BuildValue(ret, module);
-}
-
-PyMODINIT_FUNC BOB_EXT_ENTRY_NAME (void) {
-# if PY_VERSION_HEX >= 0x03000000
-  return
-# endif
-    create_module();
-}
diff --git a/conda/meta.yaml b/conda/meta.yaml
index eb5cdbab872ca7f7ed1173c78b3e7fcfb4f67abe..63afbcb92f75faeb62613c9211eed90d07a6543a 100644
--- a/conda/meta.yaml
+++ b/conda/meta.yaml
@@ -17,32 +17,21 @@ build:
     - python setup.py install --single-version-externally-managed --record record.txt
 
 requirements:
-  build:
-    - {{ compiler('c') }}
-    - {{ compiler('cxx') }}
-    - pkg-config {{ pkg_config }}
-    - cmake {{ cmake }}
-    - make {{ make }}
   host:
     - python {{ python }}
     - setuptools {{ setuptools }}
     - bob.extension
     - bob.blitz
-    - bob.core >2.0.5
     - bob.io.base
     - bob.sp
-    - bob.math
     - bob.learn.activation
     - bob.learn.linear
-    - libblitz {{ libblitz }}
-    - boost {{ boost }}
     - numpy {{ numpy }}
     - dask {{ dask }}
     - dask-ml {{ dask_ml }}
   run:
     - python
     - setuptools
-    - boost
     - {{ pin_compatible('numpy') }}
     - {{ pin_compatible('dask') }}
     - {{ pin_compatible('dask-ml') }}
diff --git a/develop.cfg b/develop.cfg
index 40bd10aa0e2846152a12010f8c5ee123d60b95f9..8058224ef600e2139e69947bd2f99842e5fb1e75 100644
--- a/develop.cfg
+++ b/develop.cfg
@@ -10,11 +10,8 @@ extensions = bob.buildout
 
 auto-checkout = *
 develop = src/bob.extension
-          src/bob.blitz
-          src/bob.core
           src/bob.io.base
           src/bob.sp
-          src/bob.math
           src/bob.learn.activation
           src/bob.learn.linear
           .
@@ -26,11 +23,8 @@ newest = false
 
 [sources]
 bob.extension = git https://gitlab.idiap.ch/bob/bob.extension
-bob.blitz = git https://gitlab.idiap.ch/bob/bob.blitz
-bob.core = git https://gitlab.idiap.ch/bob/bob.core
 bob.io.base = git https://gitlab.idiap.ch/bob/bob.io.base
 bob.sp = git https://gitlab.idiap.ch/bob/bob.sp
-bob.math = git https://gitlab.idiap.ch/bob/bob.math
 bob.learn.activation = git https://gitlab.idiap.ch/bob/bob.learn.activation
 bob.learn.linear = git https://gitlab.idiap.ch/bob/bob.learn.linear
 
diff --git a/requirements.txt b/requirements.txt
index ddb7e6e216a56cd8784e952a1b15fce46553bedd..699a028be46754a7d43b80cb3e64ce9bf46cbc8d 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,9 +1,6 @@
 setuptools
 bob.extension
-bob.blitz
-bob.core > 2.0.5
 bob.io.base
 bob.sp
-bob.math > 2
 bob.learn.activation
 bob.learn.linear
diff --git a/setup.py b/setup.py
index 3528a1dcc0624d904f58acbb179f938642fa315f..732b0d949db969ed12a8ff6676e3d5115385ff5f 100644
--- a/setup.py
+++ b/setup.py
@@ -3,14 +3,17 @@
 # Andre Anjos <andre.anjos@idiap.ch>
 # Mon 16 Apr 08:18:08 2012 CEST
 
+from setuptools import dist
+from setuptools import setup
+
+from bob.extension.utils import find_packages
+from bob.extension.utils import load_requirements
+
 bob_packages = ['bob.core', 'bob.io.base', 'bob.sp', 'bob.math', 'bob.learn.activation', 'bob.learn.linear']
+dist.Distribution(dict(setup_requires=['bob.extension>=2.0.7'] + bob_packages))
 
-from setuptools import setup, find_packages, dist
-dist.Distribution(dict(setup_requires=['bob.extension>=2.0.7', 'bob.blitz'] + bob_packages))
-from bob.blitz.extension import Extension, Library, build_ext
 
-from bob.extension.utils import load_requirements
-build_requires = load_requirements()
+install_requires = load_requirements()
 
 # Define package version
 version = open("version.txt").read().rstrip()
@@ -27,6 +30,7 @@ setup(
     license='BSD',
     author='Andre Anjos',
     author_email='andre.anjos@idiap.ch',
+    keywords="bob, em, expectation-maximization",
 
     long_description=open('README.rst').read(),
 
@@ -34,109 +38,17 @@ setup(
     include_package_data=True,
     zip_safe=False,
 
-    setup_requires = build_requires,
-    install_requires = build_requires,
-
-
-
-    ext_modules = [
-      Extension("bob.learn.em.version",
-        [
-          "bob/learn/em/version.cpp",
-        ],
-        bob_packages = bob_packages,
-        packages = packages,
-        boost_modules = boost_modules,
-        version = version,
-      ),
-
-      Library("bob.learn.em.bob_learn_em",
-        [
-          "bob/learn/em/cpp/Gaussian.cpp",
-          "bob/learn/em/cpp/GMMMachine.cpp",
-          "bob/learn/em/cpp/GMMStats.cpp",
-          "bob/learn/em/cpp/IVectorMachine.cpp",
-          "bob/learn/em/cpp/KMeansMachine.cpp",
-          "bob/learn/em/cpp/LinearScoring.cpp",
-          "bob/learn/em/cpp/PLDAMachine.cpp",
-
-          "bob/learn/em/cpp/FABase.cpp",
-          "bob/learn/em/cpp/JFABase.cpp",
-          "bob/learn/em/cpp/ISVBase.cpp",
-          "bob/learn/em/cpp/JFAMachine.cpp",
-          "bob/learn/em/cpp/ISVMachine.cpp",
-
-          "bob/learn/em/cpp/FABaseTrainer.cpp",
-          "bob/learn/em/cpp/JFATrainer.cpp",
-          "bob/learn/em/cpp/ISVTrainer.cpp",
-
-          "bob/learn/em/cpp/EMPCATrainer.cpp",
-          "bob/learn/em/cpp/GMMBaseTrainer.cpp",
-          "bob/learn/em/cpp/IVectorTrainer.cpp",
-          "bob/learn/em/cpp/KMeansTrainer.cpp",
-          "bob/learn/em/cpp/MAP_GMMTrainer.cpp",
-          "bob/learn/em/cpp/ML_GMMTrainer.cpp",
-          "bob/learn/em/cpp/PLDATrainer.cpp",
-        ],
-        bob_packages = bob_packages,
-        packages = packages,
-        boost_modules = boost_modules,
-        version = version,
-      ),
-
-      Extension("bob.learn.em._library",
-        [
-          "bob/learn/em/gaussian.cpp",
-          "bob/learn/em/gmm_stats.cpp",
-          "bob/learn/em/gmm_machine.cpp",
-          "bob/learn/em/kmeans_machine.cpp",
-          "bob/learn/em/kmeans_trainer.cpp",
-
-          "bob/learn/em/ml_gmm_trainer.cpp",
-          "bob/learn/em/map_gmm_trainer.cpp",
-
-          "bob/learn/em/jfa_base.cpp",
-          "bob/learn/em/jfa_machine.cpp",
-          "bob/learn/em/jfa_trainer.cpp",
-
-          "bob/learn/em/isv_base.cpp",
-          "bob/learn/em/isv_machine.cpp",
-          "bob/learn/em/isv_trainer.cpp",
-
-          "bob/learn/em/ivector_machine.cpp",
-          "bob/learn/em/ivector_trainer.cpp",
-
-          "bob/learn/em/plda_base.cpp",
-          "bob/learn/em/plda_machine.cpp",
-
-          "bob/learn/em/empca_trainer.cpp",
-
-          "bob/learn/em/plda_trainer.cpp",
-
-          "bob/learn/em/linear_scoring.cpp",
-
-          "bob/learn/em/main.cpp",
-        ],
-        bob_packages = bob_packages,
-        packages = packages,
-        boost_modules = boost_modules,
-        version = version,
-      ),
-    ],
-
-    cmdclass = {
-      'build_ext': build_ext
-    },
-
-    classifiers = [
-      'Framework :: Bob',
-      'Development Status :: 3 - Alpha',
-      'Intended Audience :: Developers',
-      'License :: OSI Approved :: BSD License',
-      'Natural Language :: English',
-      'Programming Language :: Python',
-      'Programming Language :: Python :: 3',
-      'Topic :: Software Development :: Libraries :: Python Modules',
+    install_requires = install_requires,
+
+    classifiers=[
+        'Framework :: Bob',
+        'Development Status :: 3 - Alpha',
+        'Intended Audience :: Developers',
+        'License :: OSI Approved :: BSD License',
+        'Natural Language :: English',
+        'Programming Language :: Python',
+        'Programming Language :: Python :: 3',
+        'Topic :: Software Development :: Libraries :: Python Modules',
     ],
 
-  )
+)