diff --git a/bob/learn/misc/cpp/KMeansTrainer.cpp b/bob/learn/misc/cpp/KMeansTrainer.cpp
index 583543f7bbc1b249a435cc3242fabbf14c251e84..cbc2e2a2a3741a5f5bf49a0f9e2e8dda2066c8c6 100644
--- a/bob/learn/misc/cpp/KMeansTrainer.cpp
+++ b/bob/learn/misc/cpp/KMeansTrainer.cpp
@@ -8,9 +8,10 @@
 
 #include <bob.learn.misc/KMeansTrainer.h>
 #include <bob.core/array_copy.h>
-#include <bob.core/random.h>
 
 #include <boost/random.hpp>
+#include <bob.core/random.h>
+
 
 /*
 bob::learn::misc::KMeansTrainer::KMeansTrainer(double convergence_threshold,
@@ -30,15 +31,13 @@ bob::learn::misc::KMeansTrainer::KMeansTrainer(double convergence_threshold,
 }
 */
 
-bob::learn::misc::KMeansTrainer::KMeansTrainer(InitializationMethod i_m)
+bob::learn::misc::KMeansTrainer::KMeansTrainer(InitializationMethod i_m):
+m_rng(new boost::mt19937()),
+m_zeroethOrderStats(0),
+m_firstOrderStats(0),
+m_average_min_distance(0)
 {
-
   m_initialization_method = i_m;
-  m_zeroethOrderStats     = 0;
-  m_firstOrderStats       = 0;
-  m_average_min_distance  = 0;
-  
-  //m_rng(new boost::mt19937());
 }
 
 
@@ -218,8 +217,7 @@ void bob::learn::misc::KMeansTrainer::eStep(bob::learn::misc::KMeansMachine& kme
   m_average_min_distance /= static_cast<double>(ar.extent(0));
 }
 
-void bob::learn::misc::KMeansTrainer::mStep(bob::learn::misc::KMeansMachine& kmeans,
-  const blitz::Array<double,2>&)
+void bob::learn::misc::KMeansTrainer::mStep(bob::learn::misc::KMeansMachine& kmeans)
 {
   blitz::Array<double,2>& means = kmeans.updateMeans();
   for(size_t i=0; i<kmeans.getNMeans(); ++i)
diff --git a/bob/learn/misc/include/bob.learn.misc/KMeansTrainer.h b/bob/learn/misc/include/bob.learn.misc/KMeansTrainer.h
index caba9f64a1c2df62f7f71d344cdaa280d9af152b..fcf394cd0f8eded29a9a5773646f59ca0bb22655 100644
--- a/bob/learn/misc/include/bob.learn.misc/KMeansTrainer.h
+++ b/bob/learn/misc/include/bob.learn.misc/KMeansTrainer.h
@@ -104,8 +104,7 @@ class KMeansTrainer
     /**
      * @brief Updates the mean based on the statistics from the E-step.
      */
-    void mStep(bob::learn::misc::KMeansMachine& kmeans,
-      const blitz::Array<double,2>&);
+    void mStep(bob::learn::misc::KMeansMachine& kmeans);
 
     /**
      * @brief This functions returns the average min (Square Euclidean)
diff --git a/bob/learn/misc/kmeans_machine.cpp b/bob/learn/misc/kmeans_machine.cpp
index 39e467d4f3ccacd990727491784be44278c6a57b..436bb9ad7a0bb26c6f9a76b76123e574a2e073a0 100644
--- a/bob/learn/misc/kmeans_machine.cpp
+++ b/bob/learn/misc/kmeans_machine.cpp
@@ -24,7 +24,7 @@ static auto KMeansMachine_doc = bob::extension::ClassDoc(
     "",
     true
   )
-  .add_prototype("n_gaussians,n_inputs","")
+  .add_prototype("n_means,n_inputs","")
   .add_prototype("other","")
   .add_prototype("hdf5","")
   .add_prototype("","")
diff --git a/bob/learn/misc/kmeans_trainer.cpp b/bob/learn/misc/kmeans_trainer.cpp
index 8ce8bc3deab8f87ecdcf842222f64c64c995c3e8..7e13cd19f648c338bd890bbaf5de9cc10237fd09 100644
--- a/bob/learn/misc/kmeans_trainer.cpp
+++ b/bob/learn/misc/kmeans_trainer.cpp
@@ -13,6 +13,18 @@
 /************ Constructor Section *********************************/
 /******************************************************************/
 
+// InitializationMethod type conversion
+static const std::map<std::string, bob::learn::misc::KMeansTrainer::InitializationMethod> IM = {{"random",  bob::learn::misc::KMeansTrainer::InitializationMethod::RANDOM}, {"random_no_duplicate", bob::learn::misc::KMeansTrainer::InitializationMethod::RANDOM_NO_DUPLICATE}, {"kmeans_plus_plus", bob::learn::misc::KMeansTrainer::InitializationMethod::KMEANS_PLUS_PLUS}};
+static inline bob::learn::misc::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::misc::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",
@@ -31,7 +43,7 @@ static auto KMeansTrainer_doc = bob::extension::ClassDoc(
   .add_prototype("other","")
   .add_prototype("","")
 
-  .add_parameter("initialization_method", ":py:class:`bob.learn.misc.KMeansTrainer.getInitializationMethod`", "The initialization method of the means")
+  .add_parameter("initialization_method", "str", "The initialization method of the means")
   .add_parameter("other", ":py:class:`bob.learn.misc.KMeansTrainer`", "A KMeansTrainer object to be copied.")
 
 );
@@ -50,13 +62,54 @@ static int PyBobLearnMiscKMeansTrainer_init_copy(PyBobLearnMiscKMeansTrainerObje
   return 0;
 }
 
+static int PyBobLearnMiscKMeansTrainer_init_str(PyBobLearnMiscKMeansTrainerObject* 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::misc::KMeansTrainer(string2IM(std::string(value))));
+  return 0;
+}
+
 
 static int PyBobLearnMiscKMeansTrainer_init(PyBobLearnMiscKMeansTrainerObject* 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);
-  
+  int nargs = (args?PyTuple_Size(args):0) + (kwargs?PyDict_Size(kwargs):0);
+
+  switch (nargs) {
+
+    case 0:{ //default initializer ()
+      self->cxx.reset(new bob::learn::misc::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 (PyBobLearnMiscKMeansTrainer_Check(arg))
+        return PyBobLearnMiscKMeansTrainer_init_copy(self, args, kwargs);
+      else if(PyString_Check(arg))
+        return PyBobLearnMiscKMeansTrainer_init_str(self, args, kwargs);
+        //return PyBobLearnMiscKMeansTrainer_init_str(self, arg);
+    }
+    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", 0)
   return 0;
 }
@@ -72,12 +125,213 @@ int PyBobLearnMiscKMeansTrainer_Check(PyObject* o) {
   return PyObject_IsInstance(o, reinterpret_cast<PyObject*>(&PyBobLearnMiscKMeansTrainer_Type));
 }
 
+
+static PyObject* PyBobLearnMiscKMeansTrainer_RichCompare(PyBobLearnMiscKMeansTrainerObject* self, PyObject* other, int op) {
+  BOB_TRY
+
+  if (!PyBobLearnMiscKMeansTrainer_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<PyBobLearnMiscKMeansTrainerObject*>(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.",
+  ""
+);
+PyObject* PyBobLearnMiscKMeansTrainer_getInitializationMethod(PyBobLearnMiscKMeansTrainerObject* 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 PyBobLearnMiscKMeansTrainer_setInitializationMethod(PyBobLearnMiscKMeansTrainerObject* 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", 0)
+}
+
+
+/***** 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* PyBobLearnMiscKMeansTrainer_getZeroethOrderStatistics(PyBobLearnMiscKMeansTrainerObject* self, void*){
+  BOB_TRY
+  return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getZeroethOrderStats());
+  BOB_CATCH_MEMBER("zeroeth_order_statistics could not be read", 0)
+}
+int PyBobLearnMiscKMeansTrainer_setZeroethOrderStatistics(PyBobLearnMiscKMeansTrainerObject* 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* PyBobLearnMiscKMeansTrainer_getFirstOrderStatistics(PyBobLearnMiscKMeansTrainerObject* self, void*){
+  BOB_TRY
+  return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getFirstOrderStats());
+  BOB_CATCH_MEMBER("first_order_statistics could not be read", 0)
+}
+int PyBobLearnMiscKMeansTrainer_setFirstOrderStatistics(PyBobLearnMiscKMeansTrainerObject* 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* PyBobLearnMiscKMeansTrainer_getAverageMinDistance(PyBobLearnMiscKMeansTrainerObject* self, void*) {
+  BOB_TRY
+  return Py_BuildValue("d", self->cxx->getAverageMinDistance());
+  BOB_CATCH_MEMBER("Average Min Distance method could not be read", 0)
+}
+int PyBobLearnMiscKMeansTrainer_setAverageMinDistance(PyBobLearnMiscKMeansTrainerObject* self, PyObject* value, void*) {
+  BOB_TRY
+
+  if (!PyNumber_Check(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", 0)
+}
+
+
+
+/***** rng *****/
+static auto rng = bob::extension::VariableDoc(
+  "rng",
+  "str",
+  "The Mersenne Twister mt19937 random generator used for the initialization of subspaces/arrays before the EM loop.",
+  ""
+);
+PyObject* PyBobLearnMiscKMeansTrainer_getRng(PyBobLearnMiscKMeansTrainerObject* self, void*) {
+  BOB_TRY
+  //Allocating the correspondent python object
+  
+  PyBoostMt19937Object* retval =
+    (PyBoostMt19937Object*)PyBoostMt19937_Type.tp_alloc(&PyBoostMt19937_Type, 0);
+
+  retval->rng = self->cxx->getRng().get();
+  return Py_BuildValue("O", retval);
+  BOB_CATCH_MEMBER("Rng method could not be read", 0)
+}
+int PyBobLearnMiscKMeansTrainer_setRng(PyBobLearnMiscKMeansTrainerObject* self, PyObject* value, void*) {
+  BOB_TRY
+
+  if (!PyBoostMt19937_Check(value)){
+    PyErr_Format(PyExc_RuntimeError, "%s %s expects an PyBoostMt19937_Check", Py_TYPE(self)->tp_name, average_min_distance.name());
+    return -1;
+  }
+
+  PyBoostMt19937Object* boostObject = 0;
+  PyBoostMt19937_Converter(value, &boostObject);
+  self->cxx->setRng((boost::shared_ptr<boost::mt19937>)boostObject->rng);
+
+  return 0;
+  BOB_CATCH_MEMBER("Rng could not be set", 0)
+}
+
+
 
 static PyGetSetDef PyBobLearnMiscKMeansTrainer_getseters[] = { 
+  {
+   initialization_method.name(),
+   (getter)PyBobLearnMiscKMeansTrainer_getInitializationMethod,
+   (setter)PyBobLearnMiscKMeansTrainer_setInitializationMethod,
+   initialization_method.doc(),
+   0
+  },
+  {
+   zeroeth_order_statistics.name(),
+   (getter)PyBobLearnMiscKMeansTrainer_getZeroethOrderStatistics,
+   (setter)PyBobLearnMiscKMeansTrainer_setZeroethOrderStatistics,
+   zeroeth_order_statistics.doc(),
+   0
+  },
+  {
+   first_order_statistics.name(),
+   (getter)PyBobLearnMiscKMeansTrainer_getFirstOrderStatistics,
+   (setter)PyBobLearnMiscKMeansTrainer_setFirstOrderStatistics,
+   first_order_statistics.doc(),
+   0
+  },
+  {
+   average_min_distance.name(),
+   (getter)PyBobLearnMiscKMeansTrainer_getAverageMinDistance,
+   (setter)PyBobLearnMiscKMeansTrainer_setAverageMinDistance,
+   average_min_distance.doc(),
+   0
+  },
+  {
+   rng.name(),
+   (getter)PyBobLearnMiscKMeansTrainer_getRng,
+   0,
+   rng.doc(),
+   0
+  },
   {0}  // Sentinel
 };
 
@@ -86,10 +340,226 @@ static PyGetSetDef PyBobLearnMiscKMeansTrainer_getseters[] = {
 /************ 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")
+.add_parameter("kmeans_machine", ":py:class:`bob.learn.misc.KMeansMachine`", "KMeansMachine Object")
+.add_parameter("data", "array_like <float, 2D>", "Input data");
+static PyObject* PyBobLearnMiscKMeansTrainer_initialize(PyBobLearnMiscKMeansTrainerObject* self, PyObject* args, PyObject* kwargs) {
+  BOB_TRY
+
+  /* Parses input arguments in a single shot */
+  char** kwlist = initialize.kwlist(0);
+
+  PyBobLearnMiscKMeansMachineObject* kmeans_machine = 0;
+  PyBlitzArrayObject* data                          = 0;
+
+  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O&", kwlist, &PyBobLearnMiscKMeansMachine_Type, &kmeans_machine,
+                                                                 &PyBlitzArray_Converter, &data)) Py_RETURN_NONE;
+  auto data_ = make_safe(data);
+
+  self->cxx->initialize(*kmeans_machine->cxx, *PyBlitzArrayCxx_AsBlitz<double,2>(data));
+
+  BOB_CATCH_MEMBER("cannot perform the initialize method", 0)
+
+  Py_RETURN_NONE;
+}
+
+
+/*** eStep ***/
+static auto eStep = bob::extension::FunctionDoc(
+  "eStep",
+  "Compute the eStep, 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.misc.KMeansMachine`", "KMeansMachine Object")
+.add_parameter("data", "array_like <float, 2D>", "Input data");
+static PyObject* PyBobLearnMiscKMeansTrainer_eStep(PyBobLearnMiscKMeansTrainerObject* self, PyObject* args, PyObject* kwargs) {
+  BOB_TRY
+
+  /* Parses input arguments in a single shot */
+  char** kwlist = eStep.kwlist(0);
+
+  PyBobLearnMiscKMeansMachineObject* kmeans_machine;
+  PyBlitzArrayObject* data = 0;
+  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O&", kwlist, &PyBobLearnMiscKMeansMachine_Type, &kmeans_machine,
+                                                                 &PyBlitzArray_Converter, &data)) Py_RETURN_NONE;
+  auto data_ = make_safe(data);
+
+  self->cxx->eStep(*kmeans_machine->cxx, *PyBlitzArrayCxx_AsBlitz<double,2>(data));
+
+
+  BOB_CATCH_MEMBER("cannot perform the eStep method", 0)
+
+  Py_RETURN_NONE;
+}
+
+
+/*** mStep ***/
+static auto mStep = bob::extension::FunctionDoc(
+  "mStep",
+  "Updates the mean based on the statistics from the E-step",
+  0,
+  true
+)
+.add_prototype("kmeans_machine")
+.add_parameter("kmeans_machine", ":py:class:`bob.learn.misc.KMeansMachine`", "KMeansMachine Object");
+static PyObject* PyBobLearnMiscKMeansTrainer_mStep(PyBobLearnMiscKMeansTrainerObject* self, PyObject* args, PyObject* kwargs) {
+  BOB_TRY
+
+  /* Parses input arguments in a single shot */
+  char** kwlist = mStep.kwlist(0);
+
+  PyBobLearnMiscKMeansMachineObject* kmeans_machine;
+  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", kwlist, &PyBobLearnMiscKMeansMachine_Type, &kmeans_machine)) Py_RETURN_NONE;
+
+  self->cxx->mStep(*kmeans_machine->cxx);
+
+  BOB_CATCH_MEMBER("cannot perform the mStep 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.misc.KMeansMachine`", "KMeansMachine Object");
+static PyObject* PyBobLearnMiscKMeansTrainer_compute_likelihood(PyBobLearnMiscKMeansTrainerObject* self, PyObject* args, PyObject* kwargs) {
+  BOB_TRY
+
+  /* Parses input arguments in a single shot */
+  char** kwlist = compute_likelihood.kwlist(0);
+
+  PyBobLearnMiscKMeansMachineObject* kmeans_machine;
+  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", kwlist, &PyBobLearnMiscKMeansMachine_Type, &kmeans_machine)) Py_RETURN_NONE;
+
+  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.misc.KMeansMachine`", "KMeansMachine Object");
+static PyObject* PyBobLearnMiscKMeansTrainer_reset_accumulators(PyBobLearnMiscKMeansTrainerObject* self, PyObject* args, PyObject* kwargs) {
+  BOB_TRY
+
+  /* Parses input arguments in a single shot */
+  char** kwlist = reset_accumulators.kwlist(0);
+
+  PyBobLearnMiscKMeansMachineObject* kmeans_machine;
+  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", kwlist, &PyBobLearnMiscKMeansMachine_Type, &kmeans_machine)) Py_RETURN_NONE;
+
+  bool value = self->cxx->resetAccumulators(*kmeans_machine->cxx);
+  return Py_BuildValue("b", value);
+
+  BOB_CATCH_MEMBER("cannot perform the reset_accumulators method", 0)
+}
+
+
+/*** is_similar_to ***/
+/*
+static auto is_similar_to = bob::extension::FunctionDoc(
+  "is_similar_to",
+  
+  "Compares this KMeansTrainer 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 trainer."
+)
+.add_prototype("other, [r_epsilon], [a_epsilon]","output")
+.add_parameter("other", ":py:class:`bob.learn.misc.KMeansTrainer`", "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* PyBobLearnMiscKMeansTrainer_IsSimilarTo(PyBobLearnMiscKMeansTrainerObject* self, PyObject* args, PyObject* kwds) {
+
+  // Parses input arguments in a single shot 
+  char** kwlist = is_similar_to.kwlist(0);
+
+  //PyObject* other = 0;
+  PyBobLearnMiscKMeansTrainerObject* other = 0;
+  double r_epsilon = 1.e-5;
+  double a_epsilon = 1.e-8;
+
+  if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|dd", kwlist,
+        &PyBobLearnMiscKMeansTrainer_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 PyBobLearnMiscKMeansTrainer_methods[] = {
+  {
+    initialize.name(),
+    (PyCFunction)PyBobLearnMiscKMeansTrainer_initialize,
+    METH_VARARGS|METH_KEYWORDS,
+    initialize.doc()
+  },
+  {
+    eStep.name(),
+    (PyCFunction)PyBobLearnMiscKMeansTrainer_eStep,
+    METH_VARARGS|METH_KEYWORDS,
+    eStep.doc()
+  },
+  {
+    mStep.name(),
+    (PyCFunction)PyBobLearnMiscKMeansTrainer_mStep,
+    METH_VARARGS|METH_KEYWORDS,
+    mStep.doc()
+  },
+  {
+    compute_likelihood.name(),
+    (PyCFunction)PyBobLearnMiscKMeansTrainer_compute_likelihood,
+    METH_VARARGS|METH_KEYWORDS,
+    compute_likelihood.doc()
+  },
+  {
+    reset_accumulators.name(),
+    (PyCFunction)PyBobLearnMiscKMeansTrainer_reset_accumulators,
+    METH_VARARGS|METH_KEYWORDS,
+    reset_accumulators.doc()
+  },
+/*
+  {
+    is_similar_to.name(),
+    (PyCFunction)PyBobLearnMiscKMeansTrainer_IsSimilarTo,
+    METH_VARARGS|METH_KEYWORDS,
+    is_similar_to.doc()
+  },
+*/
   {0} /* Sentinel */
 };
 
@@ -116,10 +586,10 @@ bool init_BobLearnMiscKMeansTrainer(PyObject* module)
   PyBobLearnMiscKMeansTrainer_Type.tp_new = PyType_GenericNew;
   PyBobLearnMiscKMeansTrainer_Type.tp_init = reinterpret_cast<initproc>(PyBobLearnMiscKMeansTrainer_init);
   PyBobLearnMiscKMeansTrainer_Type.tp_dealloc = reinterpret_cast<destructor>(PyBobLearnMiscKMeansTrainer_delete);
-  //PyBobLearnMiscKMeansTrainer_Type.tp_richcompare = reinterpret_cast<richcmpfunc>(PyBobLearnMiscKMeansTrainer_RichCompare);
+  PyBobLearnMiscKMeansTrainer_Type.tp_richcompare = reinterpret_cast<richcmpfunc>(PyBobLearnMiscKMeansTrainer_RichCompare);
   PyBobLearnMiscKMeansTrainer_Type.tp_methods = PyBobLearnMiscKMeansTrainer_methods;
   PyBobLearnMiscKMeansTrainer_Type.tp_getset = PyBobLearnMiscKMeansTrainer_getseters;
-  //PyBobLearnMiscGMMMachine_Type.tp_call = reinterpret_cast<ternaryfunc>(PyBobLearnMiscGMMMachine_loglikelihood);
+  PyBobLearnMiscGMMMachine_Type.tp_call = reinterpret_cast<ternaryfunc>(PyBobLearnMiscKMeansTrainer_compute_likelihood);
 
 
   // check that everything is fine