/**
 * @author Andre Anjos <andre.anjos@idiap.ch>
 * @date Tue 29 Apr 09:26:02 2014 CEST
 *
 * @brief Bindings for Cost functions
 *
 * Copyright (C) 2011-2014 Idiap Research Institute, Martigny, Switzerland
 */

#define BOB_LEARN_MLP_MODULE
#include <bob.blitz/cppapi.h>
#include <bob.blitz/cleanup.h>
#include <bob.extension/defines.h>
#include <bob.learn.mlp/api.h>
#include <bob.learn.activation/api.h>
#include <structmember.h>
#include <boost/function.hpp>
#include <boost/bind.hpp>

/*************************************
 * Implementation of Cost base class *
 *************************************/

PyDoc_STRVAR(s_cost_str, BOB_EXT_MODULE_PREFIX ".Cost");

PyDoc_STRVAR(s_cost_doc,
"A base class for evaluating the performance cost.\n\
\n\
This is the base class for all concrete (C++ only) loss\n\
function implementations. You cannot instantiate objects\n\
of this type directly, use one of the derived classes.\n\
");

static int PyBobLearnCost_init
(PyBobLearnCostObject* self, PyObject* args, PyObject* kwds) {

  PyErr_Format(PyExc_NotImplementedError, "cannot instantiate objects of type `%s', use one of the derived classes", Py_TYPE(self)->tp_name);
  return -1;

}

int PyBobLearnCost_Check(PyObject* o) {
  return PyObject_IsInstance(o, reinterpret_cast<PyObject*>(&PyBobLearnCost_Type));
}

static PyObject* PyBobLearnCost_RichCompare
(PyBobLearnCostObject* self, PyObject* other, int op) {

  if (!PyBobLearnCost_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<PyBobLearnCostObject*>(other);

  switch (op) {
    case Py_EQ:
      if (self->cxx->str() == other_->cxx->str()) Py_RETURN_TRUE;
      Py_RETURN_FALSE;
      break;
    case Py_NE:
      if (self->cxx->str() != other_->cxx->str()) Py_RETURN_TRUE;
      Py_RETURN_FALSE;
      break;
    default:
      Py_INCREF(Py_NotImplemented);
      return Py_NotImplemented;
  }

}

#if PY_VERSION_HEX >= 0x03000000
#  define PYOBJECT_STR PyObject_Str
#else
#  define PYOBJECT_STR PyObject_Unicode
#endif

PyObject* PyBobLearnCost_Repr(PyBobLearnCostObject* self) {

  /**
   * Expected output:
   *
   * <bob.learn.linear.Cost [...]>
   */

  auto retval = PyUnicode_FromFormat("<%s [act: %s]>",
        Py_TYPE(self)->tp_name, self->cxx->str().c_str());

#if PYTHON_VERSION_HEX < 0x03000000
  if (!retval) return 0;
  PyObject* tmp = PyObject_Str(retval);
  Py_DECREF(retval);
  retval = tmp;
#endif

  return retval;

}

PyObject* PyBobLearnCost_Str(PyBobLearnCostObject* self) {
  return Py_BuildValue("s", self->cxx->str().c_str());
}

/**
 * Checks if a array `a1' and `a2' have a matching shape.
 */
static int have_same_shape (PyBlitzArrayObject* a1, PyBlitzArrayObject* a2) {

  if (a1->ndim != a2->ndim) return 0;

  for (Py_ssize_t k=0; k<a1->ndim; ++k) {
    if (a1->shape[k] != a2->shape[k]) return 0;
  }

  return 1;
}

static PyObject* apply_scalar(PyBobLearnCostObject* self, const char*,
    boost::function<double (double, double)> function,
    PyObject* args, PyObject* kwds) {

  static const char* const_kwlist[] = {"output", "target", 0};
  static char** kwlist = const_cast<char**>(const_kwlist);

  double output = 0.;
  double target = 0.;

  if (!PyArg_ParseTupleAndKeywords(args, kwds, "dd", kwlist,
        &output, &target)) return 0;

  return Py_BuildValue("d", function(output, target));

}

/**
 * Maps all elements of arr through function() into `result'
 */
static PyObject* apply_array(PyBobLearnCostObject* self, const char* fname,
    boost::function<double (double, double)> function,
    PyObject* args, PyObject* kwds) {

  static const char* const_kwlist[] = {"output", "target", "result", 0};
  static char** kwlist = const_cast<char**>(const_kwlist);

  PyBlitzArrayObject* output = 0;
  PyBlitzArrayObject* target = 0;
  PyBlitzArrayObject* result = 0;

  if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&O&|O&", kwlist,
        &PyBlitzArray_Converter, &output,
        &PyBlitzArray_Converter, &target,
        &PyBlitzArray_OutputConverter, &result
        )) return 0;

  //protects acquired resources through this scope
  auto output_ = make_safe(output);
  auto target_ = make_safe(target);
  auto result_ = make_xsafe(result);

  if (output->type_num != NPY_FLOAT64) {
    PyErr_Format(PyExc_TypeError, "`%s.%s' only supports 64-bit float arrays for input array `output'", Py_TYPE(self)->tp_name, fname);
    return 0;
  }

  if (target->type_num != NPY_FLOAT64) {
    PyErr_Format(PyExc_TypeError, "`%s.%s' only supports 64-bit float arrays for input array `target'", Py_TYPE(self)->tp_name, fname);
    return 0;
  }

  if (result && result->type_num != NPY_FLOAT64) {
    PyErr_Format(PyExc_TypeError, "`%s.%s' only supports 64-bit float arrays for output array `result'", Py_TYPE(self)->tp_name, fname);
    return 0;
  }

  if (!have_same_shape(output, target)) {
    PyErr_Format(PyExc_RuntimeError, "`%s.%s' requires input arrays `output' and `target' to have the same shape, but you provided arrays with different shapes", Py_TYPE(self)->tp_name, fname);
    return 0;
  }

  if (result && !have_same_shape(output, result)) {
    PyErr_Format(PyExc_RuntimeError, "`%s.%s' requires output array `result' to have the same shape as input arrays `output' and `target', but you provided arrays with different shapes", Py_TYPE(self)->tp_name, fname);
    return 0;
  }

  /** if ``result`` was not pre-allocated, do it now **/
  if (!result) {
    result = (PyBlitzArrayObject*)PyBlitzArray_SimpleNew(NPY_FLOAT64, output->ndim, output->shape);
    result_ = make_safe(result);
  }

  switch (output->ndim) {
    case 1:
      {
        blitz::Array<double,1>& output_ =
          *PyBlitzArrayCxx_AsBlitz<double,1>(output);
        blitz::Array<double,1>& target_ =
          *PyBlitzArrayCxx_AsBlitz<double,1>(target);
        blitz::Array<double,1>& result_ =
          *PyBlitzArrayCxx_AsBlitz<double,1>(result);
        for (int k=0; k<output_.extent(0); ++k)
          result_(k) = function(output_(k), target_(k));
      }
      break;

    case 2:
      {
        blitz::Array<double,2>& output_ =
          *PyBlitzArrayCxx_AsBlitz<double,2>(output);
        blitz::Array<double,2>& target_ =
          *PyBlitzArrayCxx_AsBlitz<double,2>(target);
        blitz::Array<double,2>& result_ =
          *PyBlitzArrayCxx_AsBlitz<double,2>(result);
        for (int k=0; k<output_.extent(0); ++k)
          for (int l=0; l<output_.extent(1); ++l)
            result_(k,l) = function(output_(k,l), target_(k,l));
      }
      break;

    case 3:
      {
        blitz::Array<double,3>& output_ =
          *PyBlitzArrayCxx_AsBlitz<double,3>(output);
        blitz::Array<double,3>& target_ =
          *PyBlitzArrayCxx_AsBlitz<double,3>(target);
        blitz::Array<double,3>& result_ =
          *PyBlitzArrayCxx_AsBlitz<double,3>(result);
        for (int k=0; k<output_.extent(0); ++k)
          for (int l=0; l<output_.extent(1); ++l)
            for (int m=0; m<output_.extent(2); ++m)
              result_(k,l,m) = function(output_(k,l,m), target_(k,l,m));
      }
      break;

    case 4:
      {
        blitz::Array<double,4>& output_ =
          *PyBlitzArrayCxx_AsBlitz<double,4>(output);
        blitz::Array<double,4>& target_ =
          *PyBlitzArrayCxx_AsBlitz<double,4>(target);
        blitz::Array<double,4>& result_ =
          *PyBlitzArrayCxx_AsBlitz<double,4>(result);
        for (int k=0; k<output_.extent(0); ++k)
          for (int l=0; l<output_.extent(1); ++l)
            for (int m=0; m<output_.extent(2); ++m)
              for (int n=0; n<output_.extent(3); ++n)
                result_(k,l,m,n) = function(output_(k,l,m,n), target_(k,l,m,n));
      }
      break;

    default:
      PyErr_Format(PyExc_RuntimeError, "`%s.%s' only accepts 1, 2, 3 or 4-dimensional double arrays (not %" PY_FORMAT_SIZE_T "dD arrays)", Py_TYPE(self)->tp_name, fname, output->ndim);
    return 0;

  }

  return PyBlitzArray_NUMPY_WRAP(Py_BuildValue("O", result));

}

PyDoc_STRVAR(s_f_str, "f");
PyDoc_STRVAR(s_f_doc,
"o.f(output, target, [result]) -> result\n\
\n\
Computes the cost, given the current and expected outputs.\n\
\n\
Keyword arguments:\n\
\n\
output, ND array, float64 | scalar\n\
  Real output from the machine. May be a N-dimensional array\n\
  or a plain scalar.\n\
\n\
target, ND array, float64 | scalar\n\
  Target output you are training to achieve. The data type\n\
  and extents for this object must match that of ``target``.\n\
\n\
result (optional), ND array, float64\n\
  Where to place the result from the calculation. You can\n\
  pass this argument if the input are N-dimensional arrays.\n\
  Otherwise, it is an error to pass such a container. If the\n\
  inputs are arrays and an object for ``result`` is passed,\n\
  then its dimensions and data-type must match that of both\n\
  ``output`` and ``result``.\n\
\n\
Returns the cost as a scalar, if the input were scalars or\n\
as an array with matching size of ``output`` and ``target``\n\
otherwise.\n\
");

static PyObject* PyBobLearnCost_f
(PyBobLearnCostObject* self, PyObject* args, PyObject* kwds) {

  PyObject* arg = 0; ///< borrowed (don't delete)
  if (PyTuple_Size(args)) arg = PyTuple_GET_ITEM(args, 0);
  else {
    PyObject* tmp = PyDict_Values(kwds);
    auto tmp_ = make_safe(tmp);
    arg = PyList_GET_ITEM(tmp, 0);
  }

  if (PyBob_NumberCheck(arg))
    return apply_scalar(self, s_f_str,
        boost::bind(&bob::learn::mlp::Cost::f, self->cxx, _1, _2), args, kwds);

  return apply_array(self, s_f_str,
      boost::bind(&bob::learn::mlp::Cost::f, self->cxx, _1, _2), args, kwds);

}

PyDoc_STRVAR(s_f_prime_str, "f_prime");
PyDoc_STRVAR(s_f_prime_doc,
"o.f_prime(output, target, [result]) -> result\n\
\n\
Computes the derivative of the cost w.r.t. output.\n\
\n\
Keyword arguments:\n\
\n\
output, ND array, float64 | scalar\n\
  Real output from the machine. May be a N-dimensional array\n\
  or a plain scalar.\n\
\n\
target, ND array, float64 | scalar\n\
  Target output you are training to achieve. The data type\n\
  and extents for this object must match that of ``target``.\n\
\n\
result (optional), ND array, float64\n\
  Where to place the result from the calculation. You can\n\
  pass this argument if the input are N-dimensional arrays.\n\
  Otherwise, it is an error to pass such a container. If the\n\
  inputs are arrays and an object for ``result`` is passed,\n\
  then its dimensions and data-type must match that of both\n\
  ``output`` and ``result``.\n\
\n\
Returns the cost as a scalar, if the input were scalars or\n\
as an array with matching size of ``output`` and ``target``\n\
otherwise.\n\
");

static PyObject* PyBobLearnCost_f_prime
(PyBobLearnCostObject* self, PyObject* args, PyObject* kwds) {

  PyObject* arg = 0; ///< borrowed (don't delete)
  if (PyTuple_Size(args)) arg = PyTuple_GET_ITEM(args, 0);
  else {
    PyObject* tmp = PyDict_Values(kwds);
    auto tmp_ = make_safe(tmp);
    arg = PyList_GET_ITEM(tmp, 0);
  }

  if (PyBob_NumberCheck(arg))
    return apply_scalar(self, s_f_prime_str,
        boost::bind(&bob::learn::mlp::Cost::f_prime,
          self->cxx, _1, _2), args, kwds);

  return apply_array(self, s_f_prime_str,
      boost::bind(&bob::learn::mlp::Cost::f_prime,
        self->cxx, _1, _2), args, kwds);

}

PyDoc_STRVAR(s_error_str, "error");
PyDoc_STRVAR(s_error_doc,
"o.error(output, target, [result]) -> result\n\
\n\
Computes the back-propagated error for a given MLP ``output``\n\
layer.\n\
\n\
Computes the back-propagated error for a given MLP ``output``\n\
layer, given its activation function and outputs - i.e., the\n\
error back-propagated through the last layer neuron up to the\n\
synapse connecting the last hidden layer to the output layer.\n\
\n\
This implementation allows for optimization in the\n\
calculation of the back-propagated errors in cases where there\n\
is a possibility of mathematical simplification when using a\n\
certain combination of cost-function and activation. For\n\
example, using a ML-cost and a logistic activation function.\n\
\n\
Keyword arguments:\n\
\n\
output, ND array, float64 | scalar\n\
  Real output from the machine. May be a N-dimensional array\n\
  or a plain scalar.\n\
\n\
target, ND array, float64 | scalar\n\
  Target output you are training to achieve. The data type and\n\
  extents for this object must match that of ``target``.\n\
\n\
result (optional), ND array, float64\n\
  Where to place the result from the calculation. You can pass\n\
  this argument if the input are N-dimensional arrays.\n\
  Otherwise, it is an error to pass such a container. If the\n\
  inputs are arrays and an object for ``result`` is passed,\n\
  then its dimensions and data-type must match that of both\n\
  ``output`` and ``result``.\n\
\n\
Returns the cost as a scalar, if the input were scalars or as\n\
        an array with matching size of ``output`` and\n\
        ``target`` otherwise.\n\
");

static PyObject* PyBobLearnCost_error
(PyBobLearnCostObject* self, PyObject* args, PyObject* kwds) {

  PyObject* arg = 0; ///< borrowed (don't delete)
  if (PyTuple_Size(args)) arg = PyTuple_GET_ITEM(args, 0);
  else {
    PyObject* tmp = PyDict_Values(kwds);
    auto tmp_ = make_safe(tmp);
    arg = PyList_GET_ITEM(tmp, 0);
  }

  if (PyBob_NumberCheck(arg))
    return apply_scalar(self, s_error_str,
        boost::bind(&bob::learn::mlp::Cost::error, self->cxx, _1, _2), args, kwds);

  return apply_array(self, s_error_str,
      boost::bind(&bob::learn::mlp::Cost::error, self->cxx, _1, _2), args, kwds);

}

static PyMethodDef PyBobLearnCost_methods[] = {
  {
    s_f_str,
    (PyCFunction)PyBobLearnCost_f,
    METH_VARARGS|METH_KEYWORDS,
    s_f_doc
  },
  {
    s_f_prime_str,
    (PyCFunction)PyBobLearnCost_f_prime,
    METH_VARARGS|METH_KEYWORDS,
    s_f_prime_doc
  },
  {
    s_error_str,
    (PyCFunction)PyBobLearnCost_error,
    METH_VARARGS|METH_KEYWORDS,
    s_error_doc
  },
  {0} /* Sentinel */
};

static PyObject* PyBobLearnCost_new (PyTypeObject* type, PyObject*, PyObject*) {

  /* Allocates the python object itself */
  PyBobLearnCostObject* self = (PyBobLearnCostObject*)type->tp_alloc(type, 0);

  self->cxx.reset();

  return reinterpret_cast<PyObject*>(self);

}

PyObject* PyBobLearnCost_NewFromCost (boost::shared_ptr<bob::learn::mlp::Cost> p) {

  PyBobLearnCostObject* retval = (PyBobLearnCostObject*)PyBobLearnCost_new(&PyBobLearnCost_Type, 0, 0);

  retval->cxx = p;

  return reinterpret_cast<PyObject*>(retval);

}

PyTypeObject PyBobLearnCost_Type = {
    PyVarObject_HEAD_INIT(0, 0)
    s_cost_str,                               /* tp_name */
    sizeof(PyBobLearnCostObject),             /* tp_basicsize */
    0,                                        /* tp_itemsize */
    0,                                        /* tp_dealloc */
    0,                                        /* tp_print */
    0,                                        /* tp_getattr */
    0,                                        /* tp_setattr */
    0,                                        /* tp_compare */
    (reprfunc)PyBobLearnCost_Repr,            /* tp_repr */
    0,                                        /* tp_as_number */
    0,                                        /* tp_as_sequence */
    0,                                        /* tp_as_mapping */
    0,                                        /* tp_hash */
    (ternaryfunc)PyBobLearnCost_f,            /* tp_call */
    (reprfunc)PyBobLearnCost_Str,             /* tp_str */
    0,                                        /* tp_getattro */
    0,                                        /* tp_setattro */
    0,                                        /* tp_as_buffer */
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
    s_cost_doc,                               /* tp_doc */
    0,                                        /* tp_traverse */
    0,                                        /* tp_clear */
    (richcmpfunc)PyBobLearnCost_RichCompare,  /* tp_richcompare */
    0,                                        /* tp_weaklistoffset */
    0,                                        /* tp_iter */
    0,                                        /* tp_iternext */
    PyBobLearnCost_methods,                   /* tp_methods */
    0,                                        /* tp_members */
    0,                                        /* tp_getset */
    0,                                        /* tp_base */
    0,                                        /* tp_dict */
    0,                                        /* tp_descr_get */
    0,                                        /* tp_descr_set */
    0,                                        /* tp_dictoffset */
    (initproc)PyBobLearnCost_init,            /* tp_init */
    0,                                        /* tp_alloc */
    PyBobLearnCost_new,                       /* tp_new */
};

PyDoc_STRVAR(s_squareerror_str, BOB_EXT_MODULE_PREFIX ".SquareError");

PyDoc_STRVAR(s_squareerror_doc,
"SquareError(actfun) -> new SquareError functor\n\
\n\
Calculates the Square-Error between output and target.\n\
\n\
The square error is defined as follows:\n\
\n\
.. math::\n\
   J = \\frac{(\\hat{y} - y)^2}{2}\n\
\n\
where :math:`\\hat{y}` is the output estimated by your machine and\n\
:math:`y` is the expected output.\n\
\n\
Keyword arguments:\n\
\n\
actfun\n\
  The activation function object used at the last layer\n\
\n\
");

static int PyBobLearnSquareError_init
(PyBobLearnSquareErrorObject* self, PyObject* args, PyObject* kwds) {

  /* Parses input arguments in a single shot */
  static const char* const_kwlist[] = {"actfun", 0};
  static char** kwlist = const_cast<char**>(const_kwlist);

  PyObject* actfun = 0;

  if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!", kwlist,
        &PyBobLearnActivation_Type, &actfun)) return -1;

  try {
    auto _actfun = reinterpret_cast<PyBobLearnActivationObject*>(actfun);
    self->cxx.reset(new bob::learn::mlp::SquareError(_actfun->cxx));
  }
  catch (std::exception& ex) {
    PyErr_SetString(PyExc_RuntimeError, ex.what());
  }
  catch (...) {
    PyErr_Format(PyExc_RuntimeError, "cannot create new object of type `%s' - unknown exception thrown", Py_TYPE(self)->tp_name);
  }

  self->parent.cxx = self->cxx;

  if (PyErr_Occurred()) return -1;

  return 0;

}

static void PyBobLearnSquareError_delete
(PyBobLearnSquareErrorObject* self) {

  self->parent.cxx.reset();
  self->cxx.reset();
  Py_TYPE(&self->parent)->tp_free((PyObject*)self);

}

PyTypeObject PyBobLearnSquareError_Type = {
    PyVarObject_HEAD_INIT(0, 0)
    s_squareerror_str,                        /*tp_name*/
    sizeof(PyBobLearnSquareErrorObject),      /*tp_basicsize*/
    0,                                        /*tp_itemsize*/
    (destructor)PyBobLearnSquareError_delete, /*tp_dealloc*/
    0,                                        /*tp_print*/
    0,                                        /*tp_getattr*/
    0,                                        /*tp_setattr*/
    0,                                        /*tp_compare*/
    0,                                        /*tp_repr*/
    0,                                        /*tp_as_number*/
    0,                                        /*tp_as_sequence*/
    0,                                        /*tp_as_mapping*/
    0,                                        /*tp_hash */
    0,                                        /*tp_call*/
    0,                                        /*tp_str*/
    0,                                        /*tp_getattro*/
    0,                                        /*tp_setattro*/
    0,                                        /*tp_as_buffer*/
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
    s_squareerror_doc,                        /* tp_doc */
    0,		                                    /* tp_traverse */
    0,		                                    /* tp_clear */
    0,                                        /* tp_richcompare */
    0,		                                    /* tp_weaklistoffset */
    0,		                                    /* tp_iter */
    0,		                                    /* tp_iternext */
    0,                                        /* tp_methods */
    0,                                        /* tp_members */
    0,                                        /* tp_getset */
    0,                                        /* tp_base */
    0,                                        /* tp_dict */
    0,                                        /* tp_descr_get */
    0,                                        /* tp_descr_set */
    0,                                        /* tp_dictoffset */
    (initproc)PyBobLearnSquareError_init,     /* tp_init */
};

PyDoc_STRVAR(s_crossentropyloss_str, BOB_EXT_MODULE_PREFIX ".CrossEntropyLoss");

PyDoc_STRVAR(s_crossentropyloss_doc,
"CrossEntropyLoss(actfun) -> new CrossEntropyLoss functor\n\
\n\
Calculates the Cross Entropy Loss between output and target.\n\
\n\
The cross entropy loss is defined as follows:\n\
\n\
.. math::\n\
   J = - y \\cdot \\log{(\\hat{y})} - (1-y) \\log{(1-\\hat{y})}\n\
\n\
where :math:`\\hat{y}` is the output estimated by your machine and\n\
:math:`y` is the expected output.\n\
\n\
Keyword arguments:\n\
\n\
actfun\n\
  The activation function object used at the last layer. If you\n\
  set this to :py:class:`bob.learn.activation.Logistic`, a\n\
  mathematical simplification is possible in which\n\
  ``backprop_error()`` can benefit increasing the numerical\n\
  stability of the training process. The simplification goes\n\
  as follows:\n\
  \n\
  .. math::\n\
     b = \\delta \\cdot \\varphi'(z)\n\
  \n\
  But, for the cross-entropy loss: \n\
  \n\
  .. math::\n\
     \\delta = \\frac{\\hat{y} - y}{\\hat{y}(1 - \\hat{y})}\n\
  \n\
  and :math:`\\varphi'(z) = \\hat{y} - (1 - \\hat{y})`, so:\n\
  \n\
  .. math::\n\
     b = \\hat{y} - y\n\
\n\
");

static int PyBobLearnCrossEntropyLoss_init
(PyBobLearnCrossEntropyLossObject* self, PyObject* args, PyObject* kwds) {

  /* Parses input arguments in a single shot */
  static const char* const_kwlist[] = {"actfun", 0};
  static char** kwlist = const_cast<char**>(const_kwlist);

  PyObject* actfun = 0;

  if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!", kwlist,
        &PyBobLearnActivation_Type, &actfun)) return -1;

  try {
    auto _actfun = reinterpret_cast<PyBobLearnActivationObject*>(actfun);
    self->cxx.reset(new bob::learn::mlp::CrossEntropyLoss(_actfun->cxx));
  }
  catch (std::exception& ex) {
    PyErr_SetString(PyExc_RuntimeError, ex.what());
  }
  catch (...) {
    PyErr_Format(PyExc_RuntimeError, "cannot create new object of type `%s' - unknown exception thrown", Py_TYPE(self)->tp_name);
  }

  self->parent.cxx = self->cxx;

  if (PyErr_Occurred()) return -1;

  return 0;

}

static void PyBobLearnCrossEntropyLoss_delete
(PyBobLearnCrossEntropyLossObject* self) {

  self->parent.cxx.reset();
  self->cxx.reset();
  Py_TYPE(&self->parent)->tp_free((PyObject*)self);

}

PyDoc_STRVAR(s_logistic_activation_str, "logistic_activation");
PyDoc_STRVAR(s_logistic_activation_doc,
"o.logistic_activation() -> bool\n\
\n\
Tells if this functor is set to operate together with a\n\
:py:class:`bob.learn.activation.Logistic` activation function.\n\
");

static PyObject* PyBobLearnCrossEntropyLoss_getLogisticActivation
(PyBobLearnCrossEntropyLossObject* self, void* /*closure*/) {
  if (self->cxx->logistic_activation()) Py_RETURN_TRUE;
  Py_RETURN_FALSE;
}

static PyGetSetDef PyBobLearnCrossEntropyLoss_getseters[] = {
    {
      s_logistic_activation_str,
      (getter)PyBobLearnCrossEntropyLoss_getLogisticActivation,
      0,
      s_logistic_activation_doc,
      0
    },
    {0}  /* Sentinel */
};

PyTypeObject PyBobLearnCrossEntropyLoss_Type = {
    PyVarObject_HEAD_INIT(0, 0)
    s_crossentropyloss_str,                        /*tp_name*/
    sizeof(PyBobLearnCrossEntropyLossObject),      /*tp_basicsize*/
    0,                                             /*tp_itemsize*/
    (destructor)PyBobLearnCrossEntropyLoss_delete, /*tp_dealloc*/
    0,                                             /*tp_print*/
    0,                                             /*tp_getattr*/
    0,                                             /*tp_setattr*/
    0,                                             /*tp_compare*/
    0,                                             /*tp_repr*/
    0,                                             /*tp_as_number*/
    0,                                             /*tp_as_sequence*/
    0,                                             /*tp_as_mapping*/
    0,                                             /*tp_hash */
    0,                                             /*tp_call*/
    0,                                             /*tp_str*/
    0,                                             /*tp_getattro*/
    0,                                             /*tp_setattro*/
    0,                                             /*tp_as_buffer*/
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,      /*tp_flags*/
    s_crossentropyloss_doc,                        /* tp_doc */
    0,		                                         /* tp_traverse */
    0,		                                         /* tp_clear */
    0,                                             /* tp_richcompare */
    0,		                                         /* tp_weaklistoffset */
    0,		                                         /* tp_iter */
    0,		                                         /* tp_iternext */
    0,                                             /* tp_methods */
    0,                                             /* tp_members */
    PyBobLearnCrossEntropyLoss_getseters,          /* tp_getset */
    0,                                             /* tp_base */
    0,                                             /* tp_dict */
    0,                                             /* tp_descr_get */
    0,                                             /* tp_descr_set */
    0,                                             /* tp_dictoffset */
    (initproc)PyBobLearnCrossEntropyLoss_init,     /* tp_init */
};


// Boost.Units - A C++ library for zero-overhead dimensional analysis and
// unit/quantity manipulation and conversion
//
// Copyright (C) 2003-2008 Matthias Christian Schabel
// Copyright (C) 2008 Steven Watanabe
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)

/**
\file

\brief test_units_1.cpp

\details
Test unit class.

Output:
@verbatim
@endverbatim
**/

#include "test_header.hpp"

#include <boost/units/cmath.hpp>
#include <boost/units/scale.hpp>
#include <boost/units/make_scaled_unit.hpp>

namespace bu = boost::units;

static const double E_ = 2.718281828459045235360287471352662497757;

typedef bu::make_scaled_unit<bu::length,
                             bu::scale<10, bu::static_rational<-3> > >::type milli_meter_unit;

typedef bu::make_scaled_unit<bu::area,
                             bu::scale<10, bu::static_rational<-6> > >::type micro_meter2_unit;

int test_main(int,char *[])
{
    const bu::quantity<micro_meter2_unit> E1 = E_*micro_meter2_unit();
    const bu::quantity<milli_meter_unit>  E2 = sqrt(E1);

    BOOST_CHECK(E1.value() == E_);
    BOOST_CHECK(E2.value() == sqrt(E_));
    return 0;
}


/*
Refactor the following program to calculate the total using two threads.
Since many processors nowadays have two cores, the execution time should
decrease by utilizing threads.
*/

/*
Generalize the program from exercise 1 by utilizing as many threads as the
processor can physically execute at the same time. For example, if the
processor contains four cores, a total of four threads should be utilized.
*/

#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/cstdint.hpp>
#include <iostream>

int main()
{
  boost::posix_time::ptime start = boost::posix_time::microsec_clock::local_time();

  boost::uint64_t sum = 0;
  for (int i = 0; i < 1000000000; ++i)
    sum += i;

  boost::posix_time::ptime end = boost::posix_time::microsec_clock::local_time();
  std::cout << end - start << std::endl;

  std::cout << sum << std::endl;
}


#ifndef TEST_OPENCL_HEADER_HH
#define TEST_OPENCL_HEADER_HH
#include <stdio.h>

#define BOOST_UBLAS_ENABLE_OPENCL
#include <boost/numeric/ublas/opencl.hpp>
#include <boost/numeric/ublas/matrix.hpp>
#include <time.h>
#include <math.h>

namespace ublas = boost::numeric::ublas;
namespace opencl = boost::numeric::ublas::opencl;
namespace compute = boost::compute;

template <class T, class F = ublas::basic_row_major<>>
class test_opencl
{
public:
  static bool compare(ublas::matrix<T, F>& a, ublas::matrix<T, F>& b)
  {
    typedef typename ublas::matrix<T, F>::size_type size_type;
	if ((a.size1() != b.size1()) || (a.size2() != b.size2()))
	  return false;

	for (size_type i = 0; i<a.size1(); i++)
	  for (size_type j = 0; j<a.size2(); j++)
		if (a(i, j) != b(i, j))
		{
		  return false;
		}
	return true;

  }

  static bool compare(ublas::vector<T>& a, ublas::vector<T>& b)
  {
    typedef typename ublas::vector<T>::size_type size_type;
	if (a.size() != b.size())
	  return false;

	for (size_type i = 0; i<a.size(); i++)
	  if ((a[i] != b[i]))
	  {
		return false;
	  }
	return true;

  }

  static void init_matrix(ublas::matrix<T, F>& m, int max_value)
  {
    typedef typename ublas::matrix<T, F>::size_type size_type;
	for (size_type i = 0; i < m.size1(); i++)
	{
	  for (size_type j = 0; j<m.size2(); j++)
		m(i, j) = (std::rand() % max_value) + 1;

	}
  }

  static void init_vector(ublas::vector<T>& v, int max_value)
  {
    typedef typename ublas::vector<T>::size_type size_type;
	for (size_type i = 0; i <v.size(); i++)
	{
	  v[i] = (std::rand() % max_value) + 1;
	}
  }

  virtual void run()
  {
  }

};

#endif


// Distributed under the MIT License.
// See LICENSE.txt for details.

#pragma once

#include <boost/functional/hash.hpp>
#include <cstddef>
#include <limits>
#include <tuple>

#include "DataStructures/DataVector.hpp"
#include "DataStructures/FixedHashMap.hpp"
#include "DataStructures/Index.hpp"
#include "DataStructures/Tensor/Tensor.hpp"
#include "DataStructures/Variables.hpp"
#include "Domain/Structure/Direction.hpp"
#include "Domain/Structure/ElementId.hpp"
#include "Domain/Structure/MaxNumberOfNeighbors.hpp"
#include "Utilities/ContainerHelpers.hpp"
#include "Utilities/ErrorHandling/Assert.hpp"

namespace LinearSolver::Schwarz {

/// Identifies a subdomain region that overlaps with another element
template <size_t Dim>
using OverlapId = std::pair<Direction<Dim>, ElementId<Dim>>;

/// Data structure that can store the `ValueType` on each possible overlap of an
/// element-centered subdomain with its neighbors. Overlaps are identified by
/// their `OverlapId`.
template <size_t Dim, typename ValueType>
using OverlapMap =
    FixedHashMap<maximum_number_of_neighbors(Dim), OverlapId<Dim>, ValueType,
                 boost::hash<OverlapId<Dim>>>;

/*!
 * \brief The number of points that an overlap extends into the `volume_extent`
 *
 * In a dimension where an element has `volume_extent` points, the overlap
 * extent is the largest number under these constraints:
 *
 * - It is at most `max_overlap`.
 * - It is smaller than the `volume_extent`.
 *
 * This means the overlap extent is always smaller than the `volume_extent`. The
 * reason for this constraint is that we define the _width_ of the overlap as
 * the element-logical coordinate distance from the face of the element to the
 * first collocation point _outside_ the overlap extent. Therefore, even an
 * overlap region that covers the full element in width does not include the
 * collocation point on the opposite side of the element.
 *
 * Here's a few notes on the definition of the overlap extent and width:
 *
 * - A typical smooth weighting function goes to zero at the overlap width, so
 *   if the grid points located at the overlap width were included in the
 *   subdomain, their solutions would not contribute to the weighted sum of
 *   subdomain solutions.
 * - Defining the overlap width as the distance to the first point _outside_ the
 *   overlap extent makes it non-zero even for a single point of overlap into a
 *   Gauss-Lobatto grid (which has points located at the element face).
 * - Boundary contributions for many (but not all) discontinuous Galerkin
 *   schemes on Gauss-Lobatto grids are limited to the grid points on the
 *   element face, e.g. for a DG operator that is pre-multiplied by the mass
 *   matrix, or one where boundary contributions are lifted using the diagonal
 *   mass-matrix approximation. Not including the grid points facing away from
 *   the subdomain in the overlap allows to ignore that face altogether in the
 *   subdomain operator.
 */
size_t overlap_extent(size_t volume_extent, size_t max_overlap) noexcept;

/*!
 * \brief Total number of grid points in an overlap region that extends
 * `overlap_extent` points into the `volume_extents` from either side in the
 * `overlap_dimension`
 *
 * The overlap region has `overlap_extent` points in the `overlap_dimension`,
 * and `volume_extents` points in the other dimensions. The number of grid
 * points returned by this function is the product of these extents.
 */
template <size_t Dim>
size_t overlap_num_points(const Index<Dim>& volume_extents,
                          size_t overlap_extent,
                          size_t overlap_dimension) noexcept;

/*!
 * \brief Width of an overlap extending `overlap_extent` points into the
 * `collocation_points` from either side.
 *
 * The "width" of an overlap is the element-logical coordinate distance from the
 * element boundary to the first collocation point outside the overlap region in
 * the overlap dimension, i.e. the dimension perpendicular to the element face.
 * See `LinearSolver::Schwarz::overlap_extent` for details.
 *
 * This function assumes the `collocation_points` are mirrored around 0.
 */
double overlap_width(size_t overlap_extent,
                     const DataVector& collocation_points) noexcept;

/*!
 * \brief Iterate over grid points in a region that extends partially into the
 * volume
 *
 * Here's an example how to use this iterator:
 *
 * \snippet Test_OverlapHelpers.cpp overlap_iterator
 */
class OverlapIterator {
 public:
  template <size_t Dim>
  OverlapIterator(const Index<Dim>& volume_extents, size_t overlap_extent,
                  const Direction<Dim>& direction) noexcept;

  explicit operator bool() const noexcept;

  OverlapIterator& operator++();

  /// Offset into a DataVector that holds full volume data
  size_t volume_offset() const noexcept;

  /// Offset into a DataVector that holds data only on the overlap region
  size_t overlap_offset() const noexcept;

  void reset() noexcept;

 private:
  size_t size_ = std::numeric_limits<size_t>::max();
  size_t num_slices_ = std::numeric_limits<size_t>::max();
  size_t stride_ = std::numeric_limits<size_t>::max();
  size_t stride_count_ = std::numeric_limits<size_t>::max();
  size_t jump_ = std::numeric_limits<size_t>::max();
  size_t initial_offset_ = std::numeric_limits<size_t>::max();
  size_t volume_offset_ = std::numeric_limits<size_t>::max();
  size_t overlap_offset_ = std::numeric_limits<size_t>::max();
};

// @{
/// The part of the tensor data that lies within the overlap region
template <size_t Dim, typename DataType, typename... TensorStructure>
void data_on_overlap(const gsl::not_null<Tensor<DataType, TensorStructure...>*>
                         restricted_tensor,
                     const Tensor<DataType, TensorStructure...>& tensor,
                     const Index<Dim>& volume_extents,
                     const size_t overlap_extent,
                     const Direction<Dim>& direction) noexcept {
  for (OverlapIterator overlap_iterator{volume_extents, overlap_extent,
                                        direction};
       overlap_iterator; ++overlap_iterator) {
    for (size_t tensor_component = 0; tensor_component < tensor.size();
         ++tensor_component) {
      (*restricted_tensor)[tensor_component][overlap_iterator
                                                 .overlap_offset()] =
          tensor[tensor_component][overlap_iterator.volume_offset()];
    }
  }
}

template <size_t Dim, typename DataType, typename... TensorStructure>
Tensor<DataType, TensorStructure...> data_on_overlap(
    const Tensor<DataType, TensorStructure...>& tensor,
    const Index<Dim>& volume_extents, const size_t overlap_extent,
    const Direction<Dim>& direction) noexcept {
  Tensor<DataType, TensorStructure...> restricted_tensor{overlap_num_points(
      volume_extents, overlap_extent, direction.dimension())};
  data_on_overlap(make_not_null(&restricted_tensor), tensor, volume_extents,
                  overlap_extent, direction);
  return restricted_tensor;
}

namespace detail {
template <size_t Dim>
void data_on_overlap_impl(double* overlap_data, const double* volume_data,
                          size_t num_components,
                          const Index<Dim>& volume_extents,
                          size_t overlap_extent,
                          const Direction<Dim>& direction) noexcept;
}  // namespace detail

template <size_t Dim, typename OverlapTagsList, typename VolumeTagsList>
void data_on_overlap(
    const gsl::not_null<Variables<OverlapTagsList>*> overlap_data,
    const Variables<VolumeTagsList>& volume_data,
    const Index<Dim>& volume_extents, const size_t overlap_extent,
    const Direction<Dim>& direction) noexcept {
  constexpr size_t num_components =
      Variables<VolumeTagsList>::number_of_independent_components;
  ASSERT(volume_data.number_of_grid_points() == volume_extents.product(),
         "volume_data has wrong number of grid points.  Expected "
             << volume_extents.product() << ", got "
             << volume_data.number_of_grid_points());
  ASSERT(overlap_data->number_of_grid_points() ==
             overlap_num_points(volume_extents, overlap_extent,
                                direction.dimension()),
         "overlap_data has wrong number of grid points.  Expected "
             << overlap_num_points(volume_extents, overlap_extent,
                                   direction.dimension())
             << ", got " << overlap_data->number_of_grid_points());
  detail::data_on_overlap_impl(overlap_data->data(), volume_data.data(),
                               num_components, volume_extents, overlap_extent,
                               direction);
}

template <size_t Dim, typename TagsList>
Variables<TagsList> data_on_overlap(const Variables<TagsList>& volume_data,
                                    const Index<Dim>& volume_extents,
                                    const size_t overlap_extent,
                                    const Direction<Dim>& direction) noexcept {
  Variables<TagsList> overlap_data{overlap_num_points(
      volume_extents, overlap_extent, direction.dimension())};
  data_on_overlap(make_not_null(&overlap_data), volume_data, volume_extents,
                  overlap_extent, direction);
  return overlap_data;
}
// @}

namespace detail {
template <size_t Dim>
void add_overlap_data_impl(double* volume_data, const double* overlap_data,
                           size_t num_components,
                           const Index<Dim>& volume_extents,
                           size_t overlap_extent,
                           const Direction<Dim>& direction) noexcept;
}  // namespace detail

/// Add the `overlap_data` to the `volume_data`
template <size_t Dim, typename VolumeTagsList, typename OverlapTagsList>
void add_overlap_data(
    const gsl::not_null<Variables<VolumeTagsList>*> volume_data,
    const Variables<OverlapTagsList>& overlap_data,
    const Index<Dim>& volume_extents, const size_t overlap_extent,
    const Direction<Dim>& direction) noexcept {
  constexpr size_t num_components =
      Variables<VolumeTagsList>::number_of_independent_components;
  ASSERT(volume_data->number_of_grid_points() == volume_extents.product(),
         "volume_data has wrong number of grid points.  Expected "
             << volume_extents.product() << ", got "
             << volume_data->number_of_grid_points());
  ASSERT(overlap_data.number_of_grid_points() ==
             overlap_num_points(volume_extents, overlap_extent,
                                direction.dimension()),
         "overlap_data has wrong number of grid points.  Expected "
             << overlap_num_points(volume_extents, overlap_extent,
                                   direction.dimension())
             << ", got " << overlap_data.number_of_grid_points());
  detail::add_overlap_data_impl(volume_data->data(), overlap_data.data(),
                                num_components, volume_extents, overlap_extent,
                                direction);
}

// @{
/// Extend the overlap data to the full mesh by filling it with zeros outside
/// the overlap region
template <size_t Dim, typename ExtendedTagsList, typename OverlapTagsList>
void extended_overlap_data(
    const gsl::not_null<Variables<ExtendedTagsList>*> extended_data,
    const Variables<OverlapTagsList>& overlap_data,
    const Index<Dim>& volume_extents, const size_t overlap_extent,
    const Direction<Dim>& direction) noexcept {
  *extended_data = Variables<ExtendedTagsList>{volume_extents.product(), 0.};
  add_overlap_data(extended_data, overlap_data, volume_extents, overlap_extent,
                   direction);
}

template <size_t Dim, typename TagsList>
Variables<TagsList> extended_overlap_data(
    const Variables<TagsList>& overlap_data, const Index<Dim>& volume_extents,
    const size_t overlap_extent, const Direction<Dim>& direction) noexcept {
  Variables<TagsList> extended_data{volume_extents.product()};
  extended_overlap_data(make_not_null(&extended_data), overlap_data,
                        volume_extents, overlap_extent, direction);
  return extended_data;
}
// @}

}  // namespace LinearSolver::Schwarz


#ifndef TCM_CONSTANTS_HPP
#define TCM_CONSTANTS_HPP

#include <functional>
#include <type_traits>
#include <map>
#include <unordered_map>
#include <cmath>

#include <boost/program_options.hpp>
#include <boost/algorithm/string/erase.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/range/adaptor/filtered.hpp>
#include <boost/range/adaptor/transformed.hpp>

///////////////////////////////////////////////////////////////////////////////
/// \file constants.hpp
/// \brief Defines the 'default' constants map.
///
/// \detail We implement some useful functions to manipulate _physical_
///         constants. These constants are represented as a symbol table
///         mapping #std::string to some numerical type _R representing real
///         field.
///
/// For the calculations, we're interested in the following constants:
/// |              Key             |     Notation     | Value(default) | Dimension |
/// | ---------------------------- | ---------------- | -------------- | --------- |
/// | `pi`                         | \f$\pi\f$        | 3.14159...     | 1         |
/// | `boltzmann-constant`         | \f$k_\text{B}\f$ | 8.61733...E-5  | eV        |
/// | `elementary-charge`          | \f$e\f$          | 1.60217...E-19 | C         |
/// | `planck-constant`            | \f$\hbar\f$      | 6.58212...E-16 | eV        |
/// | `self-interaction-potential` | \f$V_0\f$        | 15.78          | eV        |
/// | `temperature`                | \f$T\f$          | 300.0          | K         |
/// | `vacuum-permittivity`        | \f$\epsilon_0\f$ | 8.85419...E-12 | F/m       |
/// A table with these values can be easily obtained using the
/// #default_constants() function.
///
/// For different simulations different values of, for example, temperature
/// may be desired. To account for this case, we implement two additional
/// functions:
/// * #init_constants_options() simplifies the creation of
///   boost::program_options::options_description for the user to be able
///   to specify constants.
/// * #load_constants() allows the creation of constants table from
///   boost::program_options::variables_map, i.e. from command line arguments.
///
/// Here is an example how these functions can be used together:
/// \include src/constants_example.cpp
///////////////////////////////////////////////////////////////////////////////

namespace tcm {

//////////////////////////////////////////////////////////////////////////////
/// \brief Returns a table with default values for relevalnt physical
///        constants.

/// This function has the following behavior:
/// \snippet include/constants.hpp Default constants
///
/// \todo make this function return std::unordered_map in place of std::map.
/// Or even better, make the container type a template parameter.
//////////////////////////////////////////////////////////////////////////////
template <class _R>
auto default_constants() -> std::map<std::string, _R>
{
//! [Default constants]
	std::map<std::string, _R> constants =
		{ {"pi",                          M_PI}
		, {"boltzmann-constant",          8.6173303E-5}
		, {"chemical-potential",          0.4}
		, {"elementary-charge",           1.6021766208E-19}
		, {"planck-constant",             6.582119514E-16}
		, {"self-interaction-potential",  15.78}
		, {"temperature",                 300.0}
		, {"vacuum-permittivity",         8.854187817E-12}
		, {"tau",                         6.0E-3}
		};
	return constants;
//! [Default constants]
}

///////////////////////////////////////////////////////////////////////////////
/// \brief Simplifies the creation of #options_descriptions concerning
///        constants.

/// Creates a #boost::program_options::options_description called
/// _Simulation Constants_ with options of the form "in.constants.KEY" where
/// KEYs are the names of the constants such as `pi` or `planck-constant`.
/// Each of these options has a default value provided by the
/// #default_constants() function. Template parameter `_R` specifies the type
/// of the constants. It is used to create the default constants table and to
/// extract values from `any` where program_options stores the values.
///////////////////////////////////////////////////////////////////////////////
template <class _R>
auto init_constants_options() -> boost::program_options::options_description
{
	using namespace boost::program_options;
	options_description description{"Simulation Constants"};
	auto const cs = tcm::default_constants<_R>();
	description.add_options()
		( "in.constants.pi"
		, value<_R>()->default_value(cs.at("pi"))
		, "PI." )
		( "in.constants.boltzmann-constant"
		, value<_R>()->default_value(cs.at("boltzmann-constant"))
		, "Boltzmann constant." )
		( "in.constants.chemical-potential"
		, value<_R>()->default_value(cs.at("chemical-potential"))
		, "Chemical potential." )
		( "in.constants.elementary-charge"
		, value<_R>()->default_value(cs.at("elementary-charge"))
		, "Elementary charge." )
		( "in.constants.planck-constant"
		, value<_R>()->default_value(cs.at("planck-constant"))
		, "Planck constant" )
		( "in.constants.self-interaction-potential"
		, value<_R>()->default_value(cs.at("self-interaction-potential"))
		, "Self interaction Coulomb potential." )
		( "in.constants.temperature"
		, value<_R>()->default_value(cs.at("temperature"))
		, "Temperature" )
		( "in.constants.vacuum-permittivity"
		, value<_R>()->default_value(cs.at("vacuum-permittivity"))
		, "Vacuum permittivity." )
		( "in.constants.tau"
		, value<_R>()->default_value(cs.at("tau"))
		, "Relaxation time tau." );
	return description;
}

///////////////////////////////////////////////////////////////////////////////
/// \brief Creates a constants table from a
///        #boost::program_options::variables_map.

/// This function iterates over entries in \p vm, searches for all options
/// that are of the form "in.constants.*", converts the values from
/// boost::program_options::variable_value to _To and returns a table of
/// type _Map consisting only of those options.
/// \snippet include/constants.hpp Load constants
///////////////////////////////////////////////////////////////////////////////
template< class _To
        , class _From = _To
        , class _Map = std::unordered_map<std::string, _To>
        >
auto load_constants(boost::program_options::variables_map const& vm) -> _Map
{
	static_assert( std::is_same<typename _Map::key_type, std::string>::value
	             , "Wrong key_type: must be std::string." );
	static_assert( std::is_same<typename _Map::mapped_type, _To>::value
	             , "Wrong mapped_type: must be _To." );
//! [Load constants]
	constexpr char const prefix[] = "in.constants.";
	auto const is_constant = [](auto const& p) {
		return boost::algorithm::starts_with(p.first, prefix);
	};

	using value_type = boost::program_options::variables_map::value_type;
	auto const mk_constant = [](value_type const& p) {
		return std::make_pair(boost::algorithm::erase_head_copy(
			p.first, std::char_traits<char>::length(prefix)),
			p.second.as<_From>());
	};

	auto rng = vm | boost::adaptors::filtered(std::cref(is_constant))
	              | boost::adaptors::transformed(std::cref(mk_constant));
	return _Map{rng.begin(), rng.end()};
//! [Load constants]
}

///////////////////////////////////////////////////////////////////////////////
/// \brief Checks whether \p key is in \p constants map and throws if it is
///        not.

/// \snippet include/constants.hpp Require constant
///////////////////////////////////////////////////////////////////////////////
template<class _Map>
auto require( std::string const& func_name
            , _Map const& constants_map
            , std::string const& key ) -> void
{
//! [Require constant]
	if (!constants_map.count(key)) {
		throw std::runtime_error{ "Constant `" + key + "` is required to run "
		                          "`" + func_name + "`!" };
	}
//! [Require constant]
}

} // namespace tcm

#endif // TCM_CONSTANTS_HPP


#include "../../SDP_Solver.hxx"

#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp>

void read_text_block(El::DistMatrix<El::BigFloat> &block,
                     const boost::filesystem::path &block_path)
{
  boost::filesystem::ifstream block_stream(block_path);
  if(!block_stream)
    {
      throw std::runtime_error("Unable to open checkpoint file: '"
                               + block_path.string() + "'");
    }
  int64_t file_height, file_width;
  block_stream >> file_height >> file_width;
  if(!block_stream.good())
    {
      throw std::runtime_error("Corrupted header in file: "
                               + block_path.string());
    }
  if(file_height != block.Height() || file_width != block.Width())
    {
      std::stringstream ss;
      ss << "Incompatible checkpoint file: '" << block_path.string()
         << "'.  Expected dimensions (" << block.Height() << ","
         << block.Width() << "), but found (" << file_height << ","
         << file_width << ")";
      throw std::runtime_error(ss.str());
    }

  std::string element;
  for(int64_t row = 0; row < file_height; ++row)
    for(int64_t column = 0; column < file_width; ++column)
      {
        block_stream >> element;
        if(block.IsLocal(row, column))
          {
            block.SetLocal(block.LocalRow(row), block.LocalCol(column),
                           El::BigFloat(element));
          }
      }
  if(!block_stream.good())
    {
      throw std::runtime_error("Corrupted data in file: "
                               + block_path.string());
    }
}

void read_text_block(El::DistMatrix<El::BigFloat> &block,
                     const boost::filesystem::path &checkpoint_directory,
                     const std::string &prefix, const size_t &block_index)
{
  read_text_block(block, checkpoint_directory
                           / (prefix + std::to_string(block_index) + ".txt"));
}

bool load_text_checkpoint(const boost::filesystem::path &checkpoint_directory,
                          const std::vector<size_t> &block_indices,
                          const Verbosity &verbosity, SDP_Solver &solver)
{
  if(!exists(checkpoint_directory / "x_0.txt"))
    {
      return false;
    }

  if(verbosity >= Verbosity::regular && El::mpi::Rank() == 0)
    {
      std::cout << "Loading text checkpoint from : " << checkpoint_directory
                << '\n';
    }

  for(size_t block = 0; block != block_indices.size(); ++block)
    {
      size_t block_index(block_indices.at(block));
      read_text_block(solver.x.blocks.at(block), checkpoint_directory, "x_",
                      block_index);
      read_text_block(solver.y.blocks.at(block),
                      checkpoint_directory / "y.txt");

      for(size_t psd_block(0); psd_block < 2; ++psd_block)
        {
          // Constant constraints have empty odd parity blocks, so we do not
          // need to load them.
          if(solver.X.blocks.at(2 * block + psd_block).Height() != 0)
            {
              const size_t psd_index(2 * block_index + psd_block);
              read_text_block(solver.X.blocks.at(2 * block + psd_block),
                              checkpoint_directory, "X_matrix_", psd_index);
              read_text_block(solver.Y.blocks.at(2 * block + psd_block),
                              checkpoint_directory, "Y_matrix_", psd_index);
            }
        }
    }
  return true;
}


/**
 * @copyright Copyright (c) 2017 B-com http://www.b-com.com/
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#define USE_FREE
#include <iostream>
#include <string>
#include <vector>

#include <boost/log/core.hpp>

// ADD COMPONENTS HEADERS HERE
#include "xpcf/xpcf.h"
#include "core/Log.h"
#include "api/image/IImageLoader.h"
#include "api/input/devices/ICamera.h"
#include "api/features/IKeypointDetector.h"
#include "api/features/IDescriptorsExtractor.h"
#include "api/features/IDescriptorMatcher.h"
#include "api/features/IMatchesFilter.h"
#include "api/solver/pose/I3DTransformFinderFrom2D2D.h"
#include "api/solver/map/ITriangulator.h"
#include "api/solver/map/IMapFilter.h"
#include "api/display/IMatchesOverlay.h"
#include "api/display/IImageViewer.h"
#include "api/display/I3DPointsViewer.h"

using namespace SolAR;
using namespace SolAR::datastructure;
using namespace SolAR::api;

namespace xpcf = org::bcom::xpcf;

int main(int argc, char **argv){

#if NDEBUG
    boost::log::core::get()->set_logging_enabled(false);
#endif

    LOG_ADD_LOG_TO_CONSOLE();

    try {
        /* instantiate component manager*/
        /* this is needed in dynamic mode */
        SRef<xpcf::IComponentManager> xpcfComponentManager = xpcf::getComponentManagerInstance();

        if(xpcfComponentManager->load("SolARSample_Triangulation_Mono_conf.xml")!=org::bcom::xpcf::_SUCCESS)
        {
            LOG_ERROR("Failed to load the configuration file SolARSample_Triangulation_Mono_conf.xml")
            return -1;
        }

        // declare and create components
        LOG_INFO("Start creating components");

        // component declaration and creation
        auto camera = xpcfComponentManager->resolve<input::devices::ICamera>();
        LOG_INFO("Camera loaded");

        auto imageLoader1 = xpcfComponentManager->resolve<image::IImageLoader>("image1");
        LOG_INFO("Image 1 loaded");
        auto imageLoader2 = xpcfComponentManager->resolve<image::IImageLoader>("image2");
        LOG_INFO("Image 2 loaded");

        LOG_INFO("free keypoint detector");
        auto keypointsDetector =xpcfComponentManager->resolve<features::IKeypointDetector>();
        LOG_INFO("free keypoint extractor");
        auto descriptorExtractor =xpcfComponentManager->resolve<features::IDescriptorsExtractor>();
        auto matcher =xpcfComponentManager->resolve<features::IDescriptorMatcher>();
        auto overlayMatches =xpcfComponentManager->resolve<display::IMatchesOverlay>();
        auto viewerMatches =xpcfComponentManager->resolve<display::IImageViewer>();
        auto poseFinderFrom2D2D =xpcfComponentManager->resolve<solver::pose::I3DTransformFinderFrom2D2D>();
        auto triangulator =xpcfComponentManager->resolve<solver::map::ITriangulator>();
        auto mapFilter =xpcfComponentManager->resolve<solver::map::IMapFilter>();
        auto viewer3DPoints =xpcfComponentManager->resolve<display::I3DPointsViewer>();

        // declarations of data structures used to exange information between components
        SRef<Image>                                  image1;
        SRef<Image>                                  image2;

        std::vector<Keypoint>                        keypoints1;
        std::vector<Keypoint>                        keypoints2;

        SRef<DescriptorBuffer>                       descriptors1;
        SRef<DescriptorBuffer>                       descriptors2;
        std::vector<DescriptorMatch>                 matches;
        std::vector<SRef<CloudPoint>>                cloud, filteredCloud;

        SRef<Image>                                  matchesImage;

        Transform3Df                                 poseFrame1 = Transform3Df::Identity();
        Transform3Df                                 poseFrame2;

        // initialize components requiring the camera intrinsic parameters (please refeer to the use of intrinsic parameters file)
        poseFinderFrom2D2D->setCameraParameters(camera->getIntrinsicsParameters(), camera->getDistortionParameters());
        triangulator->setCameraParameters(camera->getIntrinsicsParameters(), camera->getDistortionParameters());
        LOG_INFO("SetCameraParameters OK - Images OK");

        // Get first image
        if (imageLoader1->getImage(image1) != FrameworkReturnCode::_SUCCESS)
        {
            LOG_ERROR("Cannot load image 1 with path {}", imageLoader1->bindTo<xpcf::IConfigurable>()->getProperty("pathFile")->getStringValue());
            return -1;
        }

        // Get second image
        if (imageLoader2->getImage(image2) != FrameworkReturnCode::_SUCCESS)
        {
            LOG_ERROR("Cannot load image 2 with path {}", imageLoader2->bindTo<xpcf::IConfigurable>()->getProperty("pathFile")->getStringValue());
            return -1;
        }
        // Detect the keypoints for the first image
        keypointsDetector->detect(image1, keypoints1);
        // Detect the keypoints for the second image
        keypointsDetector->detect(image2, keypoints2);
        // Compute the descriptor for each keypoint extracted from the first image
        descriptorExtractor->extract(image1, keypoints1, descriptors1);
        // Compute the descriptor for each keypoint extracted from the second image
        descriptorExtractor->extract(image2, keypoints2, descriptors2);
        // Compute the matches between the keypoints of the first image and the keypoints of the second image
        matcher->match(descriptors1, descriptors2, matches);
        int nbMatches = (int)matches.size();
        // Estimate the pose of the second frame (the first frame being the reference of our coordinate system)
        poseFinderFrom2D2D->estimate(keypoints1, keypoints2, poseFrame1, poseFrame2, matches);
        LOG_INFO("Number of matches used for triangulation {}//{}", matches.size(), nbMatches);
        LOG_INFO("Estimated pose of the camera for the frame 2: \n {}", poseFrame2.matrix());
        // Create a image showing the matches used for pose estimation of the second camera
        overlayMatches->draw(image1, image2, matchesImage, keypoints1, keypoints2, matches);

        // Triangulate the inliers keypoints which match
        double reproj_error = triangulator->triangulate(keypoints1,keypoints2,matches,std::make_pair(0, 1),poseFrame1,poseFrame2,cloud);
        LOG_INFO("Reprojection error: {}", reproj_error);
        mapFilter->filter(poseFrame1, poseFrame2, cloud, filteredCloud);

        // Display the matches and the 3D point cloud
        while (true){
            if (
                viewer3DPoints->display(filteredCloud, poseFrame2) == FrameworkReturnCode::_STOP ||
                viewerMatches->display(matchesImage) == FrameworkReturnCode::_STOP  )
            {
               LOG_INFO("End of Triangulation sample");
               break;
            }
        }
        return 0;
    }
    catch (xpcf::InjectableNotFoundException& e)
    {
        LOG_ERROR("The following Injectable Not Found exception has been catch {}", e.what());
    }
    catch (xpcf::Exception& e)
    {
        LOG_ERROR("The following exception has been catch {}", e.what());
    }

}


#ifndef CAFFE_DROPOUT_AND_INNER_PRODUCT_LAYER_HPP_
#define CAFFE_DROPOUT_AND_INNER_PRODUCT_LAYER_HPP_

#include <string>
#include <vector>

#include "caffe/blob.hpp"
#include "caffe/layer.hpp"
#include "caffe/proto/caffe.pb.h"
#ifdef USE_OPENCL
#include <boost/filesystem.hpp>
#endif

namespace caffe {

/**
 * @brief A combination of InnerProductLayer and DropoutLayer to remove redundant computation with Billy's algorithm.
 *    The inner product layer computes an inner product with a set of learned weights, and (optionally) adds biases.
 *
 */
template<typename Dtype, typename MItype, typename MOtype>
class DropoutAndInnerProductLayer : public Layer<Dtype, MItype, MOtype> {
 public:
  explicit DropoutAndInnerProductLayer(const LayerParameter& param)
      : Layer<Dtype, MItype, MOtype>(param) {
  }
  virtual void LayerSetUp(const vector<Blob<MItype>*>& bottom,
      const vector<Blob<MOtype>*>& top);
  virtual void Reshape(const vector<Blob<MItype>*>& bottom,
      const vector<Blob<MOtype>*>& top);

  virtual inline const char* type() const { return "DropoutAndInnerProduct"; }
  virtual inline int_tp ExactNumBottomBlobs() const { return 1; }
  virtual inline int_tp ExactNumTopBlobs() const { return 1; }

 protected:
  virtual void GenerateProgram();
  virtual void Forward_cpu(const vector<Blob<MItype>*>& bottom,
      const vector<Blob<MOtype>*>& top);
  virtual void Forward_gpu(const vector<Blob<MItype>*>& bottom,
      const vector<Blob<MOtype>*>& top);
  virtual void Backward_cpu(const vector<Blob<MOtype>*>& top,
      const vector<bool>& propagate_down,
      const vector<Blob<MItype>*>& bottom);
  virtual void Backward_gpu(const vector<Blob<MOtype>*>& top,
      const vector<bool>& propagate_down,
      const vector<Blob<MItype>*>& bottom);
  ///GenerateProgram is a memeber function of Dropout Layer. It is not needed here, I think

  int_tp M_;
  int_tp K_;
  int_tp N_;
  bool bias_term_;
  Blob<Dtype> bias_multiplier_;
  bool transpose_;  ///< if true, assume transposed weights
  QuantizerValues bias_multiplier_qv_;

  /// rand_vec_ should only be 0 or 1 in CPU mode and GPU's backward pass,
  /// and simulate real numbers in GPU mode's forward pass
  Blob<uint8_t> rand_vec_;
  /// threshold for GPU kernel
  uint8_t uint_thres_;
  /// the probability @f$ p @f$ of dropping any input
  float threshold_;
  /// the scale for undropped inputs at train time @f$ 1 / (1 - p) @f$
  float scale_;
  /// denotes the dimension where the mask is applied
  DropoutType type_;
};

}  // namespace caffe

#endif  //CAFFE_DROPOUT_AND_INNER_PRODUCT_LAYER_HPP_


#include <boost/dynamic_bitset.hpp>


//=======================================================================
// Copyright 2009 Trustees of Indiana University.
// Authors: Michael Hansen
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//=======================================================================

#include <fstream>
#include <iostream>
#include <string>

#include <boost/lexical_cast.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/filtered_graph.hpp>
#include <boost/graph/graph_utility.hpp>
#include <boost/graph/iteration_macros.hpp>
#include <boost/graph/mcgregor_common_subgraphs.hpp>
#include <boost/property_map/shared_array_property_map.hpp>

using namespace boost;

// Callback that looks for the first common subgraph whose size
// matches the user's preference.
template <typename Graph>
struct example_callback {

  typedef typename graph_traits<Graph>::vertices_size_type VertexSizeFirst;

  example_callback(const Graph& graph1) :
    m_graph1(graph1) { }

  template <typename CorrespondenceMapFirstToSecond,
            typename CorrespondenceMapSecondToFirst>
  bool operator()(CorrespondenceMapFirstToSecond correspondence_map_1_to_2,
                  CorrespondenceMapSecondToFirst correspondence_map_2_to_1,
                  VertexSizeFirst subgraph_size) {

    // Fill membership map for first graph
    typedef typename property_map<Graph, vertex_index_t>::type VertexIndexMap;
    typedef shared_array_property_map<bool, VertexIndexMap> MembershipMap;

    MembershipMap membership_map1(num_vertices(m_graph1),
                                  get(vertex_index, m_graph1));

    fill_membership_map<Graph>(m_graph1, correspondence_map_1_to_2, membership_map1);

    // Generate filtered graphs using membership map
    typedef typename membership_filtered_graph_traits<Graph, MembershipMap>::graph_type
      MembershipFilteredGraph;

    MembershipFilteredGraph subgraph1 =
      make_membership_filtered_graph(m_graph1, membership_map1);

    // Print the graph out to the console
    std::cout << "Found common subgraph (size " << subgraph_size << ")" << std::endl;
    print_graph(subgraph1);
    std::cout << std::endl;

    // Explore the entire space
    return (true);
  }

private:
  const Graph& m_graph1;
  VertexSizeFirst m_max_subgraph_size;
};

int main (int argc, char *argv[]) {

  // Using a vecS graph here so that we don't have to mess around with
  // a vertex index map; it will be implicit.
  typedef adjacency_list<listS, vecS, directedS,
    property<vertex_name_t, unsigned int,
    property<vertex_index_t, unsigned int> >,
    property<edge_name_t, unsigned int> > Graph;

  typedef property_map<Graph, vertex_name_t>::type VertexNameMap;

  // Test maximum and unique variants on known graphs
  Graph graph_simple1, graph_simple2;
  example_callback<Graph> user_callback(graph_simple1);

  VertexNameMap vname_map_simple1 = get(vertex_name, graph_simple1);
  VertexNameMap vname_map_simple2 = get(vertex_name, graph_simple2);

  // Graph that looks like a triangle
  put(vname_map_simple1, add_vertex(graph_simple1), 1);
  put(vname_map_simple1, add_vertex(graph_simple1), 2);
  put(vname_map_simple1, add_vertex(graph_simple1), 3);

  add_edge(0, 1, graph_simple1);
  add_edge(0, 2, graph_simple1);
  add_edge(1, 2, graph_simple1);

  std::cout << "First graph:" << std::endl;
  print_graph(graph_simple1);
  std::cout << std::endl;

  // Triangle with an extra vertex
  put(vname_map_simple2, add_vertex(graph_simple2), 1);
  put(vname_map_simple2, add_vertex(graph_simple2), 2);
  put(vname_map_simple2, add_vertex(graph_simple2), 3);
  put(vname_map_simple2, add_vertex(graph_simple2), 4);

  add_edge(0, 1, graph_simple2);
  add_edge(0, 2, graph_simple2);
  add_edge(1, 2, graph_simple2);
  add_edge(1, 3, graph_simple2);

  std::cout << "Second graph:" << std::endl;
  print_graph(graph_simple2);
  std::cout << std::endl;

  // All subgraphs
  std::cout << "mcgregor_common_subgraphs:" << std::endl;
  mcgregor_common_subgraphs
    (graph_simple1, graph_simple2, true, user_callback,
     vertices_equivalent(make_property_map_equivalent(vname_map_simple1, vname_map_simple2)));
  std::cout << std::endl;

  // Unique subgraphs
  std::cout << "mcgregor_common_subgraphs_unique:" << std::endl;
  mcgregor_common_subgraphs_unique
    (graph_simple1, graph_simple2, true, user_callback,
     vertices_equivalent(make_property_map_equivalent(vname_map_simple1, vname_map_simple2)));
  std::cout << std::endl;

  // Maximum subgraphs
  std::cout << "mcgregor_common_subgraphs_maximum:" << std::endl;
  mcgregor_common_subgraphs_maximum
    (graph_simple1, graph_simple2, true, user_callback,
     vertices_equivalent(make_property_map_equivalent(vname_map_simple1, vname_map_simple2)));
  std::cout << std::endl;

  // Maximum, unique subgraphs
  std::cout << "mcgregor_common_subgraphs_maximum_unique:" << std::endl;
  mcgregor_common_subgraphs_maximum_unique
    (graph_simple1, graph_simple2, true, user_callback,
     vertices_equivalent(make_property_map_equivalent(vname_map_simple1, vname_map_simple2)));

  return 0;
}


/**
 * @copyright Copyright (c) 2017 B-com http://www.b-com.com/
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <boost/log/core.hpp>
#include "xpcf/xpcf.h"
#include "core/Log.h"
#include "api/input/devices/IARDevice.h"
#include "api/display/I3DPointsViewer.h"
#include "api/solver/map/IBundler.h"
#include "api/storage/IMapManager.h"
#include "api/loop/IOverlapDetector.h"
#include "api/solver/map/IMapFusion.h"

using namespace SolAR;
using namespace SolAR::datastructure;
using namespace SolAR::api;
using namespace SolAR::api::storage;
namespace xpcf  = org::bcom::xpcf;

#define INDEX_USE_CAMERA 0

int main(int argc, char *argv[])
{

#if NDEBUG
    boost::log::core::get()->set_logging_enabled(false);
#endif

    LOG_ADD_LOG_TO_CONSOLE();
	try {
		/* instantiate component manager*/
		/* this is needed in dynamic mode */
		SRef<xpcf::IComponentManager> xpcfComponentManager = xpcf::getComponentManagerInstance();

        std::string configxml = std::string("SolARSample_MapUpdate_FloatingMapFusion_conf.xml");
		if (argc == 2)
			configxml = std::string(argv[1]);

		if (xpcfComponentManager->load(configxml.c_str()) != org::bcom::xpcf::_SUCCESS)
		{
			LOG_ERROR("Failed to load the configuration file {}", configxml.c_str());
			return -1;
		}

		// declare and create components
		LOG_INFO("Start creating components");
		auto arDevice = xpcfComponentManager->resolve<input::devices::IARDevice>();
		auto viewer3D = xpcfComponentManager->resolve<display::I3DPointsViewer>();
		auto globalMapManager = xpcfComponentManager->resolve<storage::IMapManager>("globalMapper");
		auto floatingMapManager = xpcfComponentManager->resolve<storage::IMapManager>("floatingMapper");
		auto fusionMapManager = xpcfComponentManager->resolve<storage::IMapManager>("fusionMapper");
		auto mapOverlapDetector = xpcfComponentManager->resolve<loop::IOverlapDetector>();
		auto mapFusion = xpcfComponentManager->resolve<solver::map::IMapFusion>();
		auto globalBundler = xpcfComponentManager->resolve<api::solver::map::IBundler>();

		// Load camera intrinsics parameters
		CameraRigParameters camRigParams = arDevice->getCameraParameters();
		CameraParameters camParams = camRigParams.cameraParams[INDEX_USE_CAMERA];
		mapOverlapDetector->setCameraParameters(camParams.intrinsic, camParams.distortion);
		if (globalMapManager->loadFromFile() != FrameworkReturnCode::_SUCCESS) {
			LOG_INFO("Cannot load global map");
			return -1;
		}
		if (floatingMapManager->loadFromFile() != FrameworkReturnCode::_SUCCESS) {
			LOG_INFO("Cannot load floating map");
			return -1;
		}

		// get map
		SRef<Map> floatingMap, globalMap;
		floatingMapManager->getMap(floatingMap);
		globalMapManager->getMap(globalMap);
		LOG_INFO("Global map");
		LOG_INFO("Number of point cloud: {}", globalMap->getConstPointCloud()->getNbPoints());
		LOG_INFO("Number of keyframes: {}", globalMap->getConstKeyframeCollection()->getNbKeyframes());
		LOG_INFO("Local map");
		LOG_INFO("Number of point cloud: {}", floatingMap->getConstPointCloud()->getNbPoints());
		LOG_INFO("Number of keyframes: {}", floatingMap->getConstKeyframeCollection()->getNbKeyframes());

		Transform3Df sim3Transform;
		std::vector<std::pair<uint32_t, uint32_t>>overlapsIndices;
		// detect overlap from global/floating map and extract sim3
		LOG_INFO("Overlap detection: ");
		if (mapOverlapDetector->detect(globalMap, floatingMap, sim3Transform, overlapsIndices) == FrameworkReturnCode::_SUCCESS) {
			LOG_INFO("	->overlap sim3 from {} overlap cloud points: \n{}", overlapsIndices.size(), sim3Transform.matrix());
			// map fusion
			uint32_t nbMatches;
			float error;
			if (mapFusion->merge(floatingMap, globalMap, sim3Transform, nbMatches, error) == FrameworkReturnCode::_ERROR_) {
				LOG_INFO("Cannot merge two maps");
				return 0;
			}
			LOG_INFO("The refined Transformation matrix: \n{}", sim3Transform.matrix());
			LOG_INFO("Number of matched cloud points: {}", nbMatches);
			LOG_INFO("Error: {}", error);

			// global bundle adjustment
			globalBundler->setMap(globalMap);
			double error_bundle  = globalBundler->bundleAdjustment(camParams.intrinsic, camParams.distortion);
			LOG_INFO("Error after bundler: {}", error_bundle);

			// pruning
            globalMapManager->pointCloudPruning();
            globalMapManager->keyframePruning();

			// get keyframes and point cloud to display
			std::vector<SRef<Keyframe>> globalKeyframes;
			std::vector<SRef<CloudPoint>> globalPointCloud;
			globalMap->getConstKeyframeCollection()->getAllKeyframes(globalKeyframes);
			globalMap->getConstPointCloud()->getAllPoints(globalPointCloud);

			std::vector<Transform3Df> globalKeyframesPoses;
			for (const auto &it : globalKeyframes)
				globalKeyframesPoses.push_back(it->getPose());

			while (true) {
                if (viewer3D->display(globalPointCloud, {}, {}, {}, {}, globalKeyframesPoses) == FrameworkReturnCode::_STOP)
					break;
			}

			// save the fusion map
			fusionMapManager->setMap(globalMap);
			fusionMapManager->saveToFile();
		}
		else {
			LOG_INFO(" no overlaps detected")
				return -1;
		}
	}
	catch (xpcf::Exception e){
		LOG_ERROR("The following exception has been catch : {}", e.what());
		return -1;
	}
    return 0;
}