diff --git a/bob/learn/misc/include/bob.learn.misc/PLDAMachine.h b/bob/learn/misc/include/bob.learn.misc/PLDAMachine.h index 5c2b668bddb3a2f6a1c61beffc274df1ca96fdf7..ad935f0a386a4f136f0bff59af0ceeb2ddd9ce83 100644 --- a/bob/learn/misc/include/bob.learn.misc/PLDAMachine.h +++ b/bob/learn/misc/include/bob.learn.misc/PLDAMachine.h @@ -11,7 +11,6 @@ #ifndef BOB_LEARN_MISC_PLDAMACHINE_H #define BOB_LEARN_MISC_PLDAMACHINE_H -#include <bob.learn.misc/Machine.h> #include <blitz/array.h> #include <bob.io.base/HDF5File.h> #include <map> @@ -432,7 +431,7 @@ class PLDABase * 3. 'Probabilistic Models for Inference about Identity', Li, Fu, Mohammed, * Elder and Prince, TPAMI'2012 */ -class PLDAMachine: public Machine<blitz::Array<double,1>, double> +class PLDAMachine { public: /** diff --git a/bob/learn/misc/ivector_machine.cpp b/bob/learn/misc/ivector_machine.cpp index eaae8264c71b2373c041e147340d29470c8fe210..1f058a3ec9c3aceede4bd82793c5a6571802e062 100644 --- a/bob/learn/misc/ivector_machine.cpp +++ b/bob/learn/misc/ivector_machine.cpp @@ -31,7 +31,7 @@ static auto IVectorMachine_doc = bob::extension::ClassDoc( .add_parameter("ubm", ":py:class:`bob.learn.misc.GMMMachine`", "The Universal Background Model.") .add_parameter("rt", "int", "Size of the Total Variability matrix (CD x rt).") - .add_parameter("variance_threshold", "double", "Variance flooring threshold for the Sigma (diagonal) matrix") + .add_parameter("variance_threshold", "double", "Variance flooring threshold for the :math:`\\Sigma` (diagonal) matrix") .add_parameter("other", ":py:class:`bob.learn.misc.IVectorMachine`", "A IVectorMachine object to be copied.") .add_parameter("hdf5", ":py:class:`bob.io.base.HDF5File`", "An HDF5 file open for reading") @@ -450,6 +450,39 @@ static PyObject* PyBobLearnMiscIVectorMachine_Forward(PyBobLearnMiscIVectorMachi } +/*** 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* PyBobLearnMiscIVectorMachine_resize(PyBobLearnMiscIVectorMachineObject* 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)) Py_RETURN_NONE; + + 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; +} + + static PyMethodDef PyBobLearnMiscIVectorMachine_methods[] = { { @@ -470,6 +503,12 @@ static PyMethodDef PyBobLearnMiscIVectorMachine_methods[] = { METH_VARARGS|METH_KEYWORDS, is_similar_to.doc() }, + { + resize.name(), + (PyCFunction)PyBobLearnMiscIVectorMachine_resize, + METH_VARARGS|METH_KEYWORDS, + resize.doc() + }, /* { diff --git a/bob/learn/misc/main.cpp b/bob/learn/misc/main.cpp index 316a9c17a743f2a780c21a6c85ad586710097f7e..0627cfb8cba5fcecd5f92ada95af4a8dad094cb2 100644 --- a/bob/learn/misc/main.cpp +++ b/bob/learn/misc/main.cpp @@ -53,7 +53,9 @@ static PyObject* create_module (void) { if (!init_BobLearnMiscJFAMachine(module)) return 0; if (!init_BobLearnMiscISVMachine(module)) return 0; - if (!init_BobLearnMiscIVectorMachine(module)) return 0; + if (!init_BobLearnMiscIVectorMachine(module)) return 0; + if (!init_BobLearnMiscPLDABase(module)) return 0; + if (!init_BobLearnMiscPLDAMachine(module)) return 0; static void* PyBobLearnMisc_API[PyBobLearnMisc_API_pointers]; diff --git a/bob/learn/misc/main.h b/bob/learn/misc/main.h index 50b5de618822780333b9013a2c41b40d86ddeb83..45759fa765565b044489625da3811b192d000c44 100644 --- a/bob/learn/misc/main.h +++ b/bob/learn/misc/main.h @@ -33,6 +33,7 @@ #include <bob.learn.misc/JFAMachine.h> #include <bob.learn.misc/ISVMachine.h> #include <bob.learn.misc/IVectorMachine.h> +#include <bob.learn.misc/PLDAMachine.h> #if PY_VERSION_HEX >= 0x03000000 @@ -216,5 +217,27 @@ bool init_BobLearnMiscIVectorMachine(PyObject* module); int PyBobLearnMiscIVectorMachine_Check(PyObject* o); +// PLDABase +typedef struct { + PyObject_HEAD + boost::shared_ptr<bob::learn::misc::PLDABase> cxx; +} PyBobLearnMiscPLDABaseObject; + +extern PyTypeObject PyBobLearnMiscPLDABase_Type; +bool init_BobLearnMiscPLDABase(PyObject* module); +int PyBobLearnMiscPLDABase_Check(PyObject* o); + + +// PLDAMachine +typedef struct { + PyObject_HEAD + boost::shared_ptr<bob::learn::misc::PLDAMachine> cxx; +} PyBobLearnMiscPLDAMachineObject; + +extern PyTypeObject PyBobLearnMiscPLDAMachine_Type; +bool init_BobLearnMiscPLDAMachine(PyObject* module); +int PyBobLearnMiscPLDAMachine_Check(PyObject* o); + + #endif // BOB_LEARN_EM_MAIN_H diff --git a/bob/learn/misc/plda_base.cpp b/bob/learn/misc/plda_base.cpp new file mode 100644 index 0000000000000000000000000000000000000000..32f0f6f8be6b2db30ac64b299157a82a823a5dee --- /dev/null +++ b/bob/learn/misc/plda_base.cpp @@ -0,0 +1,1028 @@ +/** + * @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", "double", "The smallest possible value of the variance (Ignored if set to 0.)") + + .add_parameter("other", ":py:class:`bob.learn.misc.PLDABase`", "A PLDABase object to be copied.") + .add_parameter("hdf5", ":py:class:`bob.io.base.HDF5File`", "An HDF5 file open for reading") + +); + + +static int PyBobLearnMiscPLDABase_init_copy(PyBobLearnMiscPLDABaseObject* self, PyObject* args, PyObject* kwargs) { + + char** kwlist = PLDABase_doc.kwlist(1); + PyBobLearnMiscPLDABaseObject* o; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", kwlist, &PyBobLearnMiscPLDABase_Type, &o)){ + PLDABase_doc.print_usage(); + return -1; + } + + self->cxx.reset(new bob::learn::misc::PLDABase(*o->cxx)); + return 0; +} + + +static int PyBobLearnMiscPLDABase_init_hdf5(PyBobLearnMiscPLDABaseObject* 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; + } + + self->cxx.reset(new bob::learn::misc::PLDABase(*(config->f))); + + return 0; +} + + +static int PyBobLearnMiscPLDABase_init_dim(PyBobLearnMiscPLDABaseObject* 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::misc::PLDABase(dim_D, dim_F, dim_G, variance_threshold)); + return 0; +} + +static int PyBobLearnMiscPLDABase_init(PyBobLearnMiscPLDABaseObject* 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 (PyBobLearnMiscPLDABase_Check(arg)) + return PyBobLearnMiscPLDABase_init_copy(self, args, kwargs); + // If the constructor input is a HDF5 + else if (PyBobIoHDF5File_Check(arg)) + return PyBobLearnMiscPLDABase_init_hdf5(self, args, kwargs); + } + else if((nargs==3)||(nargs==4)) + return PyBobLearnMiscPLDABase_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", 0) + return 0; +} + + + +static void PyBobLearnMiscPLDABase_delete(PyBobLearnMiscPLDABaseObject* self) { + self->cxx.reset(); + Py_TYPE(self)->tp_free((PyObject*)self); +} + +static PyObject* PyBobLearnMiscPLDABase_RichCompare(PyBobLearnMiscPLDABaseObject* self, PyObject* other, int op) { + BOB_TRY + + if (!PyBobLearnMiscPLDABase_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<PyBobLearnMiscPLDABaseObject*>(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 PyBobLearnMiscPLDABase_Check(PyObject* o) { + return PyObject_IsInstance(o, reinterpret_cast<PyObject*>(&PyBobLearnMiscPLDABase_Type)); +} + + +/******************************************************************/ +/************ Variables Section ***********************************/ +/******************************************************************/ + +/***** shape *****/ +static auto shape = bob::extension::VariableDoc( + "shape", + "(int,int, int)", + "A tuple that represents the dimensionality of the feature vector :math:`dim_d`, the :math:`F` matrix and the :math:`G` matrix.", + "" +); +PyObject* PyBobLearnMiscPLDABase_getShape(PyBobLearnMiscPLDABaseObject* 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* PyBobLearnMiscPLDABase_getF(PyBobLearnMiscPLDABaseObject* self, void*){ + BOB_TRY + return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getF()); + BOB_CATCH_MEMBER("`f` could not be read", 0) +} +int PyBobLearnMiscPLDABase_setF(PyBobLearnMiscPLDABaseObject* 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* PyBobLearnMiscPLDABase_getG(PyBobLearnMiscPLDABaseObject* self, void*){ + BOB_TRY + return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getG()); + BOB_CATCH_MEMBER("`g` could not be read", 0) +} +int PyBobLearnMiscPLDABase_setG(PyBobLearnMiscPLDABaseObject* 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* PyBobLearnMiscPLDABase_getMu(PyBobLearnMiscPLDABaseObject* self, void*){ + BOB_TRY + return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getMu()); + BOB_CATCH_MEMBER("`mu` could not be read", 0) +} +int PyBobLearnMiscPLDABase_setMu(PyBobLearnMiscPLDABaseObject* 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* PyBobLearnMiscPLDABase_getISigma(PyBobLearnMiscPLDABaseObject* 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* PyBobLearnMiscPLDABase_getAlpha(PyBobLearnMiscPLDABaseObject* 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* PyBobLearnMiscPLDABase_getBeta(PyBobLearnMiscPLDABaseObject* 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* PyBobLearnMiscPLDABase_getFtBeta(PyBobLearnMiscPLDABaseObject* 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* PyBobLearnMiscPLDABase_getGtISigma(PyBobLearnMiscPLDABaseObject* 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__", + "double", + "Gets :math:`\\log(\\det(\\alpha))`", + "" +); +static PyObject* PyBobLearnMiscPLDABase_getLogDetAlpha(PyBobLearnMiscPLDABaseObject* 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__", + "double", + "Gets :math:`\\log(\\det(\\Sigma))`", + "" +); +static PyObject* PyBobLearnMiscPLDABase_getLogDetSigma(PyBobLearnMiscPLDABaseObject* self, PyObject* args, PyObject* kwargs) { + BOB_TRY + return Py_BuildValue("d",self->cxx->getLogDetSigma()); + BOB_CATCH_MEMBER("__logdet_sigma__ could not be read", 0) +} + + +static PyGetSetDef PyBobLearnMiscPLDABase_getseters[] = { + { + shape.name(), + (getter)PyBobLearnMiscPLDABase_getShape, + 0, + shape.doc(), + 0 + }, + { + F.name(), + (getter)PyBobLearnMiscPLDABase_getF, + (setter)PyBobLearnMiscPLDABase_setF, + F.doc(), + 0 + }, + { + G.name(), + (getter)PyBobLearnMiscPLDABase_getG, + (setter)PyBobLearnMiscPLDABase_setG, + G.doc(), + 0 + }, + { + mu.name(), + (getter)PyBobLearnMiscPLDABase_getMu, + (setter)PyBobLearnMiscPLDABase_setMu, + mu.doc(), + 0 + }, + { + __isigma__.name(), + (getter)PyBobLearnMiscPLDABase_getISigma, + 0, + __isigma__.doc(), + 0 + }, + { + __alpha__.name(), + (getter)PyBobLearnMiscPLDABase_getAlpha, + 0, + __alpha__.doc(), + 0 + }, + { + __beta__.name(), + (getter)PyBobLearnMiscPLDABase_getBeta, + 0, + __beta__.doc(), + 0 + }, + { + __ft_beta__.name(), + (getter)PyBobLearnMiscPLDABase_getFtBeta, + 0, + __ft_beta__.doc(), + 0 + }, + { + __gt_i_sigma__.name(), + (getter)PyBobLearnMiscPLDABase_getGtISigma, + 0, + __gt_i_sigma__.doc(), + 0 + }, + { + __logdet_alpha__.name(), + (getter)PyBobLearnMiscPLDABase_getLogDetAlpha, + 0, + __logdet_alpha__.doc(), + 0 + }, + { + __logdet_sigma__.name(), + (getter)PyBobLearnMiscPLDABase_getLogDetSigma, + 0, + __logdet_sigma__.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* PyBobLearnMiscPLDABase_Save(PyBobLearnMiscPLDABaseObject* 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* PyBobLearnMiscPLDABase_Load(PyBobLearnMiscPLDABaseObject* 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.misc.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* PyBobLearnMiscPLDABase_IsSimilarTo(PyBobLearnMiscPLDABaseObject* self, PyObject* args, PyObject* kwds) { + + /* Parses input arguments in a single shot */ + char** kwlist = is_similar_to.kwlist(0); + + //PyObject* other = 0; + PyBobLearnMiscPLDABaseObject* other = 0; + double r_epsilon = 1.e-5; + double a_epsilon = 1.e-8; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|dd", kwlist, + &PyBobLearnMiscPLDABase_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:`F`(within class variantion matrix)."); +static PyObject* PyBobLearnMiscPLDABase_resize(PyBobLearnMiscPLDABaseObject* 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)) Py_RETURN_NONE; + + 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; +} + + +/***** gamma *****/ +static auto gamma_var = bob::extension::FunctionDoc( + "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* PyBobLearnMiscPLDABase_getGamma(PyBobLearnMiscPLDABaseObject* self, PyObject* args, PyObject* kwargs) { + BOB_TRY + + char** kwlist = gamma_var.kwlist(0); + + int i = 0; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, &i)) Py_RETURN_NONE; + + return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getGamma(i)); + BOB_CATCH_MEMBER("`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* PyBobLearnMiscPLDABase_hasGamma(PyBobLearnMiscPLDABaseObject* self, PyObject* args, PyObject* kwargs) { + BOB_TRY + + char** kwlist = has_gamma.kwlist(0); + int i = 0; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, &i)) Py_RETURN_NONE; + + 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* PyBobLearnMiscPLDABase_computeGamma(PyBobLearnMiscPLDABaseObject* 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)) Py_RETURN_NONE; + + 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* PyBobLearnMiscPLDABase_getAddGamma(PyBobLearnMiscPLDABaseObject* 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)) Py_RETURN_NONE; + + 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* PyBobLearnMiscPLDABase_hasLogLikeConstTerm(PyBobLearnMiscPLDABaseObject* 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)) Py_RETURN_NONE; + + 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* PyBobLearnMiscPLDABase_computeLogLikeConstTerm(PyBobLearnMiscPLDABaseObject* 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)) Py_RETURN_NONE; + + 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","double",""); +static PyObject* PyBobLearnMiscPLDABase_getAddLogLikeConstTerm(PyBobLearnMiscPLDABaseObject* 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)) Py_RETURN_NONE; + + 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","double",""); +static PyObject* PyBobLearnMiscPLDABase_getLogLikeConstTerm(PyBobLearnMiscPLDABaseObject* 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)) Py_RETURN_NONE; + + 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 +); +static PyObject* PyBobLearnMiscPLDABase_clearMaps(PyBobLearnMiscPLDABaseObject* 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", "double", ""); +static PyObject* PyBobLearnMiscPLDABase_computeLogLikelihoodPointEstimate(PyBobLearnMiscPLDABaseObject* self, PyObject* args, PyObject* kwargs) { + BOB_TRY + + char** kwlist = compute_log_likelihood_point_estimate.kwlist(0); + PyBlitzArrayObject* xij, *hi, *wij = 0; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, &PyBlitzArray_Converter, &xij, + &PyBlitzArray_Converter, &hi, + &PyBlitzArray_Converter, &wij)) Py_RETURN_NONE; + + 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* PyBobLearnMiscPLDABase_precompute(PyBobLearnMiscPLDABaseObject* 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* PyBobLearnMiscPLDABase_precomputeLogLike(PyBobLearnMiscPLDABaseObject* 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 PyBobLearnMiscPLDABase_methods[] = { + { + save.name(), + (PyCFunction)PyBobLearnMiscPLDABase_Save, + METH_VARARGS|METH_KEYWORDS, + save.doc() + }, + { + load.name(), + (PyCFunction)PyBobLearnMiscPLDABase_Load, + METH_VARARGS|METH_KEYWORDS, + load.doc() + }, + { + is_similar_to.name(), + (PyCFunction)PyBobLearnMiscPLDABase_IsSimilarTo, + METH_VARARGS|METH_KEYWORDS, + is_similar_to.doc() + }, + { + resize.name(), + (PyCFunction)PyBobLearnMiscPLDABase_resize, + METH_VARARGS|METH_KEYWORDS, + resize.doc() + }, + { + gamma_var.name(), + (PyCFunction)PyBobLearnMiscPLDABase_getGamma, + METH_VARARGS|METH_KEYWORDS, + gamma_var.doc() + }, + { + has_gamma.name(), + (PyCFunction)PyBobLearnMiscPLDABase_hasGamma, + METH_VARARGS|METH_KEYWORDS, + has_gamma.doc() + }, + { + compute_gamma.name(), + (PyCFunction)PyBobLearnMiscPLDABase_computeGamma, + METH_VARARGS|METH_KEYWORDS, + compute_gamma.doc() + }, + { + get_add_gamma.name(), + (PyCFunction)PyBobLearnMiscPLDABase_getAddGamma, + METH_VARARGS|METH_KEYWORDS, + get_add_gamma.doc() + }, + { + has_log_like_const_term.name(), + (PyCFunction)PyBobLearnMiscPLDABase_hasLogLikeConstTerm, + METH_VARARGS|METH_KEYWORDS, + has_log_like_const_term.doc() + }, + { + compute_log_like_const_term.name(), + (PyCFunction)PyBobLearnMiscPLDABase_computeLogLikeConstTerm, + METH_VARARGS|METH_KEYWORDS, + compute_log_like_const_term.doc() + }, + { + get_add_log_like_const_term.name(), + (PyCFunction)PyBobLearnMiscPLDABase_getAddLogLikeConstTerm, + METH_VARARGS|METH_KEYWORDS, + get_add_log_like_const_term.doc() + }, + { + get_log_like_const_term.name(), + (PyCFunction)PyBobLearnMiscPLDABase_getLogLikeConstTerm, + METH_VARARGS|METH_KEYWORDS, + get_log_like_const_term.doc() + }, + { + clear_maps.name(), + (PyCFunction)PyBobLearnMiscPLDABase_clearMaps, + METH_NOARGS, + clear_maps.doc() + }, + { + compute_log_likelihood_point_estimate.name(), + (PyCFunction)PyBobLearnMiscPLDABase_computeLogLikelihoodPointEstimate, + METH_VARARGS|METH_KEYWORDS, + compute_log_likelihood_point_estimate.doc() + }, + { + __precompute__.name(), + (PyCFunction)PyBobLearnMiscPLDABase_precompute, + METH_NOARGS, + __precompute__.doc() + }, + { + __precompute_log_like__.name(), + (PyCFunction)PyBobLearnMiscPLDABase_precomputeLogLike, + METH_NOARGS, + __precompute_log_like__.doc() + }, + {0} /* Sentinel */ +}; + + +/******************************************************************/ +/************ Module Section **************************************/ +/******************************************************************/ + +// Define the JFA type struct; will be initialized later +PyTypeObject PyBobLearnMiscPLDABase_Type = { + PyVarObject_HEAD_INIT(0,0) + 0 +}; + +bool init_BobLearnMiscPLDABase(PyObject* module) +{ + // initialize the type struct + PyBobLearnMiscPLDABase_Type.tp_name = PLDABase_doc.name(); + PyBobLearnMiscPLDABase_Type.tp_basicsize = sizeof(PyBobLearnMiscPLDABaseObject); + PyBobLearnMiscPLDABase_Type.tp_flags = Py_TPFLAGS_DEFAULT; + PyBobLearnMiscPLDABase_Type.tp_doc = PLDABase_doc.doc(); + + // set the functions + PyBobLearnMiscPLDABase_Type.tp_new = PyType_GenericNew; + PyBobLearnMiscPLDABase_Type.tp_init = reinterpret_cast<initproc>(PyBobLearnMiscPLDABase_init); + PyBobLearnMiscPLDABase_Type.tp_dealloc = reinterpret_cast<destructor>(PyBobLearnMiscPLDABase_delete); + PyBobLearnMiscPLDABase_Type.tp_richcompare = reinterpret_cast<richcmpfunc>(PyBobLearnMiscPLDABase_RichCompare); + PyBobLearnMiscPLDABase_Type.tp_methods = PyBobLearnMiscPLDABase_methods; + PyBobLearnMiscPLDABase_Type.tp_getset = PyBobLearnMiscPLDABase_getseters; + //PyBobLearnMiscPLDABase_Type.tp_call = reinterpret_cast<ternaryfunc>(PyBobLearnMiscPLDABase_forward); + + + // check that everything is fine + if (PyType_Ready(&PyBobLearnMiscPLDABase_Type) < 0) return false; + + // add the type to the module + Py_INCREF(&PyBobLearnMiscPLDABase_Type); + return PyModule_AddObject(module, "PLDABase", (PyObject*)&PyBobLearnMiscPLDABase_Type) >= 0; +} + diff --git a/bob/learn/misc/plda_machine.cpp b/bob/learn/misc/plda_machine.cpp new file mode 100644 index 0000000000000000000000000000000000000000..386b080a23c7ab4e7edc89104c2fd73f41fcaaed --- /dev/null +++ b/bob/learn/misc/plda_machine.cpp @@ -0,0 +1,638 @@ +/** + * @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 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." + "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", "`bob.learn.misc.PLDABase`", "") + .add_parameter("other", ":py:class:`bob.learn.misc.PLDAMachine`", "A PLDAMachine object to be copied.") + .add_parameter("hdf5", ":py:class:`bob.io.base.HDF5File`", "An HDF5 file open for reading") + +); + + +static int PyBobLearnMiscPLDAMachine_init_copy(PyBobLearnMiscPLDAMachineObject* self, PyObject* args, PyObject* kwargs) { + + char** kwlist = PLDAMachine_doc.kwlist(1); + PyBobLearnMiscPLDAMachineObject* o; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", kwlist, &PyBobLearnMiscPLDAMachine_Type, &o)){ + PLDAMachine_doc.print_usage(); + return -1; + } + + self->cxx.reset(new bob::learn::misc::PLDAMachine(*o->cxx)); + return 0; +} + + +static int PyBobLearnMiscPLDAMachine_init_hdf5(PyBobLearnMiscPLDAMachineObject* self, PyObject* args, PyObject* kwargs) { + + char** kwlist = PLDAMachine_doc.kwlist(2); + + PyBobIoHDF5FileObject* config = 0; + PyBobLearnMiscPLDABaseObject* plda_base; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&O!", kwlist, &PyBobIoHDF5File_Converter, &config, + &PyBobLearnMiscPLDABase_Type, &plda_base)){ + PLDAMachine_doc.print_usage(); + return -1; + } + + self->cxx.reset(new bob::learn::misc::PLDAMachine(*(config->f),plda_base->cxx)); + + return 0; +} + + +static int PyBobLearnMiscPLDAMachine_init_pldabase(PyBobLearnMiscPLDAMachineObject* self, PyObject* args, PyObject* kwargs) { + + char** kwlist = PLDAMachine_doc.kwlist(0); + PyBobLearnMiscPLDABaseObject* plda_base; + + //Here we have to select which keyword argument to read + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", kwlist, &PyBobLearnMiscPLDABase_Type, &plda_base)){ + PLDAMachine_doc.print_usage(); + return -1; + } + + self->cxx.reset(new bob::learn::misc::PLDAMachine(plda_base->cxx)); + return 0; +} + +static int PyBobLearnMiscPLDAMachine_init(PyBobLearnMiscPLDAMachineObject* 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 (PyBobLearnMiscPLDAMachine_Check(arg)) + return PyBobLearnMiscPLDAMachine_init_copy(self, args, kwargs); + // If the constructor input is a HDF5 + else if (PyBobLearnMiscPLDABase_Check(arg)) + return PyBobLearnMiscPLDAMachine_init_pldabase(self, args, kwargs); + } + else if(nargs==2) + return PyBobLearnMiscPLDAMachine_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", 0) + return 0; +} + + + +static void PyBobLearnMiscPLDAMachine_delete(PyBobLearnMiscPLDAMachineObject* self) { + self->cxx.reset(); + Py_TYPE(self)->tp_free((PyObject*)self); +} + +static PyObject* PyBobLearnMiscPLDAMachine_RichCompare(PyBobLearnMiscPLDAMachineObject* self, PyObject* other, int op) { + BOB_TRY + + if (!PyBobLearnMiscPLDAMachine_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<PyBobLearnMiscPLDAMachineObject*>(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 PyBobLearnMiscPLDAMachine_Check(PyObject* o) { + return PyObject_IsInstance(o, reinterpret_cast<PyObject*>(&PyBobLearnMiscPLDAMachine_Type)); +} + + +/******************************************************************/ +/************ Variables Section ***********************************/ +/******************************************************************/ + +/***** shape *****/ +static auto shape = bob::extension::VariableDoc( + "shape", + "(int,int, int)", + "A tuple that represents the dimensionality of the feature vector :math:`dim_d`, the :math:`F` matrix and the :math:`G` matrix.", + "" +); +PyObject* PyBobLearnMiscPLDAMachine_getShape(PyBobLearnMiscPLDAMachineObject* 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* PyBobLearnMiscPLDAMachine_getNSamples(PyBobLearnMiscPLDAMachineObject* self, PyObject* args, PyObject* kwargs) { + BOB_TRY + return Py_BuildValue("d",self->cxx->getNSamples()); + BOB_CATCH_MEMBER("n_samples could not be read", 0) +} +int PyBobLearnMiscPLDAMachine_setNSamples(PyBobLearnMiscPLDAMachineObject* 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", + "double", + "Gets the :math:`A = -0.5 \\sum_{i} x_{i}^T \\beta x_{i}` value", + "" +); +static PyObject* PyBobLearnMiscPLDAMachine_getWSumXitBetaXi(PyBobLearnMiscPLDAMachineObject* 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 PyBobLearnMiscPLDAMachine_setWSumXitBetaXi(PyBobLearnMiscPLDAMachineObject* self, PyObject* value, void*){ + BOB_TRY + + if (!PyNumber_Check(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; +} + +static PyGetSetDef PyBobLearnMiscPLDAMachine_getseters[] = { + { + shape.name(), + (getter)PyBobLearnMiscPLDAMachine_getShape, + 0, + shape.doc(), + 0 + }, + { + n_samples.name(), + (getter)PyBobLearnMiscPLDAMachine_getNSamples, + (setter)PyBobLearnMiscPLDAMachine_setNSamples, + n_samples.doc(), + 0 + }, + { + w_sum_xit_beta_xi.name(), + (getter)PyBobLearnMiscPLDAMachine_getWSumXitBetaXi, + (setter)PyBobLearnMiscPLDAMachine_setWSumXitBetaXi, + w_sum_xit_beta_xi.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* PyBobLearnMiscPLDAMachine_Save(PyBobLearnMiscPLDAMachineObject* 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* PyBobLearnMiscPLDAMachine_Load(PyBobLearnMiscPLDAMachineObject* 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.misc.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* PyBobLearnMiscPLDAMachine_IsSimilarTo(PyBobLearnMiscPLDAMachineObject* self, PyObject* args, PyObject* kwds) { + + /* Parses input arguments in a single shot */ + char** kwlist = is_similar_to.kwlist(0); + + //PyObject* other = 0; + PyBobLearnMiscPLDAMachineObject* other = 0; + double r_epsilon = 1.e-5; + double a_epsilon = 1.e-8; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|dd", kwlist, + &PyBobLearnMiscPLDAMachine_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; +} + + +/***** gamma *****/ +static auto gamma_var = bob::extension::FunctionDoc( + "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* PyBobLearnMiscPLDAMachine_getGamma(PyBobLearnMiscPLDAMachineObject* self, PyObject* args, PyObject* kwargs) { + BOB_TRY + + char** kwlist = gamma_var.kwlist(0); + + int i = 0; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, &i)) Py_RETURN_NONE; + + return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getGamma(i)); + BOB_CATCH_MEMBER("`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* PyBobLearnMiscPLDAMachine_hasGamma(PyBobLearnMiscPLDAMachineObject* self, PyObject* args, PyObject* kwargs) { + BOB_TRY + + char** kwlist = has_gamma.kwlist(0); + int i = 0; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, &i)) Py_RETURN_NONE; + + 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* PyBobLearnMiscPLDAMachine_getAddGamma(PyBobLearnMiscPLDAMachineObject* 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)) Py_RETURN_NONE; + + 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* PyBobLearnMiscPLDAMachine_hasLogLikeConstTerm(PyBobLearnMiscPLDAMachineObject* 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)) Py_RETURN_NONE; + + 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","double",""); +static PyObject* PyBobLearnMiscPLDAMachine_getAddLogLikeConstTerm(PyBobLearnMiscPLDAMachineObject* 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)) Py_RETURN_NONE; + + 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","double",""); +static PyObject* PyBobLearnMiscPLDAMachine_getLogLikeConstTerm(PyBobLearnMiscPLDAMachineObject* 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)) Py_RETURN_NONE; + + 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 +); +static PyObject* PyBobLearnMiscPLDAMachine_clearMaps(PyBobLearnMiscPLDAMachineObject* 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,use_enrolled_samples","output") +.add_parameter("sample", "array_like <float, 1D>", "Sample") +.add_parameter("use_enrolled_samples", "bool", "") +.add_return("output","double","The log-likelihood"); +static PyObject* PyBobLearnMiscPLDAMachine_computeLogLikelihood(PyBobLearnMiscPLDAMachineObject* self, PyObject* args, PyObject* kwargs) { + BOB_TRY + + char** kwlist = compute_log_likelihood.kwlist(0); + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&O!", kwlist, )) Py_RETURN_NONE; + + return Py_BuildValue("d",self->cxx->getLogLikeConstTerm(i)); + + BOB_CATCH_MEMBER("`get_log_like_const_term` could not be read", 0) +} +*/ + +static PyMethodDef PyBobLearnMiscPLDAMachine_methods[] = { + { + save.name(), + (PyCFunction)PyBobLearnMiscPLDAMachine_Save, + METH_VARARGS|METH_KEYWORDS, + save.doc() + }, + { + load.name(), + (PyCFunction)PyBobLearnMiscPLDAMachine_Load, + METH_VARARGS|METH_KEYWORDS, + load.doc() + }, + { + is_similar_to.name(), + (PyCFunction)PyBobLearnMiscPLDAMachine_IsSimilarTo, + METH_VARARGS|METH_KEYWORDS, + is_similar_to.doc() + }, + { + gamma_var.name(), + (PyCFunction)PyBobLearnMiscPLDAMachine_getGamma, + METH_VARARGS|METH_KEYWORDS, + gamma_var.doc() + }, + { + has_gamma.name(), + (PyCFunction)PyBobLearnMiscPLDAMachine_hasGamma, + METH_VARARGS|METH_KEYWORDS, + has_gamma.doc() + }, + { + get_add_gamma.name(), + (PyCFunction)PyBobLearnMiscPLDAMachine_getAddGamma, + METH_VARARGS|METH_KEYWORDS, + get_add_gamma.doc() + }, + { + has_log_like_const_term.name(), + (PyCFunction)PyBobLearnMiscPLDAMachine_hasLogLikeConstTerm, + METH_VARARGS|METH_KEYWORDS, + has_log_like_const_term.doc() + }, + { + get_add_log_like_const_term.name(), + (PyCFunction)PyBobLearnMiscPLDAMachine_getAddLogLikeConstTerm, + METH_VARARGS|METH_KEYWORDS, + get_add_log_like_const_term.doc() + }, + { + get_log_like_const_term.name(), + (PyCFunction)PyBobLearnMiscPLDAMachine_getLogLikeConstTerm, + METH_VARARGS|METH_KEYWORDS, + get_log_like_const_term.doc() + }, + { + clear_maps.name(), + (PyCFunction)PyBobLearnMiscPLDAMachine_clearMaps, + METH_NOARGS, + clear_maps.doc() + }, + {0} /* Sentinel */ +}; + + +/******************************************************************/ +/************ Module Section **************************************/ +/******************************************************************/ + +// Define the JFA type struct; will be initialized later +PyTypeObject PyBobLearnMiscPLDAMachine_Type = { + PyVarObject_HEAD_INIT(0,0) + 0 +}; + +bool init_BobLearnMiscPLDAMachine(PyObject* module) +{ + // initialize the type struct + PyBobLearnMiscPLDAMachine_Type.tp_name = PLDAMachine_doc.name(); + PyBobLearnMiscPLDAMachine_Type.tp_basicsize = sizeof(PyBobLearnMiscPLDAMachineObject); + PyBobLearnMiscPLDAMachine_Type.tp_flags = Py_TPFLAGS_DEFAULT; + PyBobLearnMiscPLDAMachine_Type.tp_doc = PLDAMachine_doc.doc(); + + // set the functions + PyBobLearnMiscPLDAMachine_Type.tp_new = PyType_GenericNew; + PyBobLearnMiscPLDAMachine_Type.tp_init = reinterpret_cast<initproc>(PyBobLearnMiscPLDAMachine_init); + PyBobLearnMiscPLDAMachine_Type.tp_dealloc = reinterpret_cast<destructor>(PyBobLearnMiscPLDAMachine_delete); + PyBobLearnMiscPLDAMachine_Type.tp_richcompare = reinterpret_cast<richcmpfunc>(PyBobLearnMiscPLDAMachine_RichCompare); + PyBobLearnMiscPLDAMachine_Type.tp_methods = PyBobLearnMiscPLDAMachine_methods; + PyBobLearnMiscPLDAMachine_Type.tp_getset = PyBobLearnMiscPLDAMachine_getseters; + //PyBobLearnMiscPLDAMachine_Type.tp_call = reinterpret_cast<ternaryfunc>(PyBobLearnMiscPLDAMachine_forward); + + + // check that everything is fine + if (PyType_Ready(&PyBobLearnMiscPLDAMachine_Type) < 0) return false; + + // add the type to the module + Py_INCREF(&PyBobLearnMiscPLDAMachine_Type); + return PyModule_AddObject(module, "PLDAMachine", (PyObject*)&PyBobLearnMiscPLDAMachine_Type) >= 0; +} + diff --git a/doc/index.rst b/doc/index.rst index 058e2263595cf5b3ba034fc2170c945100bec97c..cc40c8d2f3949e95a77f4b48ba74a727a670c74e 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -31,6 +31,10 @@ References .. [Vogt2008] *R. Vogt, S. Sridharan*. **'Explicit Modelling of Session Variability for Speaker Verification'**, Computer Speech & Language, 2008, vol. 22, no. 1, pp. 17-38 .. [McCool2013] *C. McCool, R. Wallace, M. McLaren, L. El Shafey, S. Marcel*. **'Session Variability Modelling for Face Authentication'**, IET Biometrics, 2013 .. [Dehak2010] *N. Dehak, P. Kenny, R. Dehak, P. Dumouchel, P. Ouellet*, **'Front End Factor Analysis for Speaker Verification'**, IEEE Transactions on Audio, Speech and Language Processing, 2010, vol. 19, issue 4, pp. 788-798 +.. [ElShafey2014] *Laurent El Shafey, Chris McCool, Roy Wallace, Sebastien Marcel*. **'A Scalable Formulation of Probabilistic Linear Discriminant Analysis: Applied to Face Recognition'**, TPAMI'2014 +.. [PrinceElder2007] *Prince and Elder*. **'Probabilistic Linear Discriminant Analysis for Inference About Identity'**, ICCV'2007 +.. [LiFu2012] *Li, Fu, Mohammed, Elder and Prince*. **'Probabilistic Models for Inference about Identity'**, TPAMI'2012 + Indices and tables diff --git a/setup.py b/setup.py index 47aa925003b41c770e1b492cd2e6d303cc7c7460..5fe11fe0703fb87dcaaf24eb2ffe8e31e766aad6 100644 --- a/setup.py +++ b/setup.py @@ -119,6 +119,8 @@ setup( "bob/learn/misc/isv_machine.cpp", "bob/learn/misc/ivector_machine.cpp", + "bob/learn/misc/plda_base.cpp", + "bob/learn/misc/plda_machine.cpp", "bob/learn/misc/main.cpp", ],