gtsam/wrap/GlobalFunction.cpp

225 lines
8.0 KiB
C++
Raw Normal View History

/**
* @file GlobalFunction.cpp
*
* @date Jul 22, 2012
* @author Alex Cunningham
*/
#include "GlobalFunction.h"
#include "Class.h"
#include "utilities.h"
#include <boost/lexical_cast.hpp>
namespace wrap {
using namespace std;
/* ************************************************************************* */
void GlobalFunction::addOverload(const Qualified& overload,
2016-11-23 06:09:35 +08:00
const ArgumentList& args, const ReturnValue& retVal, const std::string& _includeFile,
boost::optional<const Qualified> instName, bool verbose) {
2014-12-01 03:12:03 +08:00
FullyOverloadedFunction::addOverload(overload.name(), args, retVal, instName,
verbose);
overloads.push_back(overload);
2016-11-23 06:09:35 +08:00
includeFile = _includeFile;
}
/* ************************************************************************* */
void GlobalFunction::matlab_proxy(const string& toolboxPath,
const string& wrapperName, const TypeAttributesTable& typeAttributes,
FileWriter& file, vector<string>& functionNames) const {
// cluster overloads with same namespace
// create new GlobalFunction structures around namespaces - same namespaces and names are overloads
// map of namespace to global function
typedef map<string, GlobalFunction> GlobalFunctionMap;
GlobalFunctionMap grouped_functions;
for (size_t i = 0; i < overloads.size(); ++i) {
Qualified overload = overloads.at(i);
// use concatenated namespaces as key
2014-12-01 03:12:03 +08:00
string str_ns = qualifiedName("", overload.namespaces());
const ReturnValue& ret = returnValue(i);
const ArgumentList& args = argumentList(i);
grouped_functions[str_ns].addOverload(overload, args, ret);
}
size_t lastcheck = grouped_functions.size();
for(const GlobalFunctionMap::value_type& p: grouped_functions) {
2014-05-26 04:01:30 +08:00
p.second.generateSingleFunction(toolboxPath, wrapperName, typeAttributes,
file, functionNames);
if (--lastcheck != 0)
2014-05-26 04:01:30 +08:00
file.oss << endl;
}
}
/* ************************************************************************* */
void GlobalFunction::generateSingleFunction(const string& toolboxPath,
const string& wrapperName, const TypeAttributesTable& typeAttributes,
FileWriter& file, vector<string>& functionNames) const {
// create the folder for the namespace
const Qualified& overload1 = overloads.front();
2014-12-01 03:12:03 +08:00
createNamespaceStructure(overload1.namespaces(), toolboxPath);
// open destination mfunctionFileName
string mfunctionFileName = overload1.matlabName(toolboxPath);
FileWriter mfunctionFile(mfunctionFileName, verbose_, "%");
// get the name of actual matlab object
const string matlabQualName = overload1.qualifiedName(".");
const string matlabUniqueName = overload1.qualifiedName("");
const string cppName = overload1.qualifiedName("::");
mfunctionFile.oss << "function varargout = " << name_ << "(varargin)\n";
for (size_t i = 0; i < nrOverloads(); ++i) {
const ArgumentList& args = argumentList(i);
const ReturnValue& returnVal = returnValue(i);
const int id = functionNames.size();
// Output proxy matlab code
mfunctionFile.oss << " " << (i == 0 ? "" : "else");
emit_conditional_call(mfunctionFile, returnVal, args, wrapperName, id);
// Output C++ wrapper code
2014-05-26 04:01:30 +08:00
const string wrapFunctionName = matlabUniqueName + "_"
+ boost::lexical_cast<string>(id);
// call
2014-05-26 04:01:30 +08:00
file.oss << "void " << wrapFunctionName
<< "(int nargout, mxArray *out[], int nargin, const mxArray *in[])\n";
// start
2014-05-26 04:01:30 +08:00
file.oss << "{\n";
// check arguments
// NOTE: for static functions, there is no object passed
2014-05-26 04:01:30 +08:00
file.oss << " checkArguments(\"" << matlabUniqueName
<< "\",nargout,nargin," << args.size() << ");\n";
// unwrap arguments, see Argument.cpp
2014-05-26 04:01:30 +08:00
args.matlab_unwrap(file, 0); // We start at 0 because there is no self object
// call method with default type and wrap result
2014-12-01 03:12:03 +08:00
if (returnVal.type1.name() != "void")
2014-05-26 04:01:30 +08:00
returnVal.wrap_result(cppName + "(" + args.names() + ")", file,
typeAttributes);
else
2014-05-26 04:01:30 +08:00
file.oss << cppName + "(" + args.names() + ");\n";
// finish
2014-05-26 04:01:30 +08:00
file.oss << "}\n";
// Add to function list
functionNames.push_back(wrapFunctionName);
}
mfunctionFile.oss << " else\n";
2014-05-26 04:01:30 +08:00
mfunctionFile.oss
<< " error('Arguments do not match any overload of function "
2014-05-26 04:01:30 +08:00
<< matlabQualName << "');" << endl;
mfunctionFile.oss << " end" << endl;
// Close file
mfunctionFile.emit(true);
}
2014-11-15 00:47:25 +08:00
/* ************************************************************************* */
void GlobalFunction::python_wrapper(FileWriter& wrapperFile) const {
wrapperFile.oss << "def(\"" << name_ << "\", " << name_ << ");\n";
}
2016-11-23 06:09:35 +08:00
/* ************************************************************************* */
void GlobalFunction::emit_cython_pxd(FileWriter& file) const {
file.oss << "cdef extern from \"" << includeFile << "\" namespace \""
<< overloads[0].qualifiedNamespaces("::")
<< "\":" << endl;
for (size_t i = 0; i < nrOverloads(); ++i) {
2016-11-30 18:56:07 +08:00
file.oss << " ";
returnVals_[i].emit_cython_pxd(file, "", vector<string>());
file.oss << pxdName() + " \"" + overloads[0].qualifiedName("::") +
2016-11-23 06:09:35 +08:00
"\"(";
argumentList(i).emit_cython_pxd(file, "", vector<string>());
2016-11-23 06:09:35 +08:00
file.oss << ")";
file.oss << "\n";
}
}
/* ************************************************************************* */
2016-11-23 06:32:48 +08:00
void GlobalFunction::emit_cython_pyx_no_overload(FileWriter& file) const {
string funcName = pyxName();
2016-11-23 06:09:35 +08:00
// Function definition
file.oss << "def " << funcName;
2016-11-23 06:09:35 +08:00
// modify name of function instantiation as python doesn't allow overloads
// e.g. template<T={A,B,C}> funcName(...) --> funcNameA, funcNameB, funcNameC
if (templateArgValue_) file.oss << templateArgValue_->pyxClassName();
2016-11-23 06:09:35 +08:00
// funtion arguments
file.oss << "(";
argumentList(0).emit_cython_pyx(file);
file.oss << "):\n";
/// Call cython corresponding function and return
convert numpy input params to dtype float and order 'F' automatically using numpy.astype(...). No copy if the params are already in the correct dtype and storage order. For a function f(Matrix A, Matrix B), simply wrapping it to pyx as f(A.astype(float, order='F', copy=False), B.astype(float, order='F', copy=False)) won't work. It produces a strange side-effect that the content of A is overwritten by B and the two inputs are the same (data address) inside the function! This is because Cython decreases the ref count for the temporary variable resulted from A.astype(...) before generates the wrap for B.astype(...). Hence, the A.astype temp var is probably reused for B.astype, and they were pointing to the same data address. For that reason, we have to go a longer route and wrap it as: A = A.astype(float, order='F', copy=False) B = B.astype(float, order='F', copy=False) f(A, B) For future ref., here is a sample of the wrongly generated code that wraps the JacobianFactor constructor: Jacobian(Key i1, Matrix A1, Key i2, Matrix A2, Vector b, noiseModel::Diagonal model) Wrongly wrapped pyx code: self.shared_CJacobianFactor_ = shared_ptr[CJacobianFactor](new CJacobianFactor(i1, <MatrixXd>(Map[MatrixXd](A1.astype(float, order='F',copy=False)), i2, <MatrixXd>(Map[MatrixXd](A2.astype(float, order='F', copy=False)), <VectorXd>(Map[VectorXd](b.astype(float, order='F', copy=False))), model.shared_CnoiseModel_Diagonal_)) The problematic Cython generated CPP code with a comment on the problematic line: ///////////////////////////////////////// // WRONG VERSION ///////////////////////////////////////// __pyx_t_12 = __Pyx_PyObject_GetAttrStr(((PyObject *)__pyx_v_A1), __pyx_n_s_astype); if (unlikely(!__pyx_t_12)) __PYX_ERR(0, 2107, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_12); __pyx_t_4 = PyTuple_New(1); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 2107, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_4); __Pyx_INCREF(((PyObject *)(&PyFloat_Type))); __Pyx_GIVEREF(((PyObject *)(&PyFloat_Type))); PyTuple_SET_ITEM(__pyx_t_4, 0, ((PyObject *)(&PyFloat_Type))); __pyx_t_5 = PyDict_New(); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 2107, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_5); if (PyDict_SetItem(__pyx_t_5, __pyx_n_s_order, __pyx_n_s_F) < 0) __PYX_ERR(0, 2107, __pyx_L1_error) if (PyDict_SetItem(__pyx_t_5, __pyx_n_s_copy, Py_False) < 0) __PYX_ERR(0, 2107, __pyx_L1_error) __pyx_t_13 = __Pyx_PyObject_Call(__pyx_t_12, __pyx_t_4, __pyx_t_5); if (unlikely(!__pyx_t_13)) __PYX_ERR(0, 2107, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_13); __Pyx_DECREF(__pyx_t_12); __pyx_t_12 = 0; __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0; __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0; if (!(likely(((__pyx_t_13) == Py_None) || likely(__Pyx_TypeTest(__pyx_t_13, __pyx_ptype_5numpy_ndarray))))) __PYX_ERR(0, 2107, __pyx_L1_error) try { __pyx_t_14 = eigency::Map<Eigen::MatrixXd> (((PyArrayObject *)__pyx_t_13)); } catch(...) { __Pyx_CppExn2PyErr(); __PYX_ERR(0, 2107, __pyx_L1_error) } /////////////////////////////////////////////// __Pyx_DECREF(__pyx_t_13); __pyx_t_13 = 0; //<------- Problematic line!!! Killing this will result in the correct result! /////////////////////////////////////////////// __pyx_t_13 = __Pyx_PyObject_GetAttrStr(((PyObject *)__pyx_v_A2), __pyx_n_s_astype); if (unlikely(!__pyx_t_13)) __PYX_ERR(0, 2107, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_13); __pyx_t_5 = PyTuple_New(1); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 2107, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_5); __Pyx_INCREF(((PyObject *)(&PyFloat_Type))); __Pyx_GIVEREF(((PyObject *)(&PyFloat_Type))); PyTuple_SET_ITEM(__pyx_t_5, 0, ((PyObject *)(&PyFloat_Type))); __pyx_t_4 = PyDict_New(); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 2107, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_4); if (PyDict_SetItem(__pyx_t_4, __pyx_n_s_order, __pyx_n_s_F) < 0) __PYX_ERR(0, 2107, __pyx_L1_error) if (PyDict_SetItem(__pyx_t_4, __pyx_n_s_copy, Py_False) < 0) __PYX_ERR(0, 2107, __pyx_L1_error) __pyx_t_12 = __Pyx_PyObject_Call(__pyx_t_13, __pyx_t_5, __pyx_t_4); if (unlikely(!__pyx_t_12)) __PYX_ERR(0, 2107, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_12); __Pyx_DECREF(__pyx_t_13); __pyx_t_13 = 0; __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0; __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0; if (!(likely(((__pyx_t_12) == Py_None) || likely(__Pyx_TypeTest(__pyx_t_12, __pyx_ptype_5numpy_ndarray))))) __PYX_ERR(0, 2107, __pyx_L1_error) try { __pyx_t_15 = eigency::Map<Eigen::MatrixXd> (((PyArrayObject *)__pyx_t_12)); } catch(...) { __Pyx_CppExn2PyErr(); __PYX_ERR(0, 2107, __pyx_L1_error) } __Pyx_DECREF(__pyx_t_12); __pyx_t_12 = 0; __pyx_t_12 = __Pyx_PyObject_GetAttrStr(((PyObject *)__pyx_v_b), __pyx_n_s_astype); if (unlikely(!__pyx_t_12)) __PYX_ERR(0, 2107, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_12); __pyx_t_4 = PyTuple_New(1); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 2107, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_4); __Pyx_INCREF(((PyObject *)(&PyFloat_Type))); __Pyx_GIVEREF(((PyObject *)(&PyFloat_Type))); PyTuple_SET_ITEM(__pyx_t_4, 0, ((PyObject *)(&PyFloat_Type))); __pyx_t_5 = PyDict_New(); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 2107, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_5); if (PyDict_SetItem(__pyx_t_5, __pyx_n_s_order, __pyx_n_s_F) < 0) __PYX_ERR(0, 2107, __pyx_L1_error) if (PyDict_SetItem(__pyx_t_5, __pyx_n_s_copy, Py_False) < 0) __PYX_ERR(0, 2107, __pyx_L1_error) __pyx_t_13 = __Pyx_PyObject_Call(__pyx_t_12, __pyx_t_4, __pyx_t_5); if (unlikely(!__pyx_t_13)) __PYX_ERR(0, 2107, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_13); __Pyx_DECREF(__pyx_t_12); __pyx_t_12 = 0; __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0; __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0; if (!(likely(((__pyx_t_13) == Py_None) || likely(__Pyx_TypeTest(__pyx_t_13, __pyx_ptype_5numpy_ndarray))))) __PYX_ERR(0, 2107, __pyx_L1_error) try { __pyx_t_16 = eigency::Map<Eigen::VectorXd> (((PyArrayObject *)__pyx_t_13)); } catch(...) { __Pyx_CppExn2PyErr(); __PYX_ERR(0, 2107, __pyx_L1_error) } __Pyx_DECREF(__pyx_t_13); __pyx_t_13 = 0; try { __pyx_t_17 = new gtsam::JacobianFactor(__pyx_v_i1, ((Eigen::MatrixXd)__pyx_t_14), __pyx_v_i2, ((Eigen::MatrixXd)__pyx_t_15), ((Eigen::VectorXd)__pyx_t_16), __pyx_v_model->shared_CnoiseModel_Diagonal_); } catch(...) { __Pyx_CppExn2PyErr(); __PYX_ERR(0, 2107, __pyx_L1_error) } __pyx_v_self->shared_CJacobianFactor_ = boost::shared_ptr<gtsam::JacobianFactor> (__pyx_t_17);
2017-03-16 01:47:11 +08:00
file.oss << argumentList(0).pyx_convertEigenTypeAndStorageOrder(" ");
string ret = pyx_functionCall("", pxdName(), 0);
2016-11-23 06:09:35 +08:00
if (!returnVals_[0].isVoid()) {
2016-11-30 18:56:07 +08:00
file.oss << " cdef " << returnVals_[0].pyx_returnType()
2016-11-23 06:09:35 +08:00
<< " ret = " << ret << "\n";
2016-11-30 18:56:07 +08:00
file.oss << " return " << returnVals_[0].pyx_casting("ret") << "\n";
2016-11-23 06:09:35 +08:00
} else {
2016-11-30 18:56:07 +08:00
file.oss << " " << ret << "\n";
2016-11-23 06:09:35 +08:00
}
}
2016-11-23 06:32:48 +08:00
/* ************************************************************************* */
void GlobalFunction::emit_cython_pyx(FileWriter& file) const {
string funcName = pyxName();
2016-11-23 06:32:48 +08:00
size_t N = nrOverloads();
if (N == 1) {
emit_cython_pyx_no_overload(file);
return;
}
// Dealing with overloads..
file.oss << "def " << funcName << "(*args, **kwargs):\n";
for (size_t i = 0; i < N; ++i) {
2016-11-30 18:56:07 +08:00
file.oss << " success, results = " << funcName << "_" << i
<< "(args, kwargs)\n";
2016-11-30 18:56:07 +08:00
file.oss << " if success:\n return results\n";
2016-11-23 06:32:48 +08:00
}
2016-11-30 18:56:07 +08:00
file.oss << " raise TypeError('Could not find the correct overload')\n";
2016-11-23 06:32:48 +08:00
for (size_t i = 0; i < N; ++i) {
ArgumentList args = argumentList(i);
file.oss << "def " + funcName + "_" + to_string(i) + "(args, kwargs):\n";
file.oss << " cdef list __params\n";
if (!returnVals_[i].isVoid()) {
file.oss << " cdef " << returnVals_[i].pyx_returnType() << " return_value\n";
}
file.oss << " try:\n";
file.oss << pyx_resolveOverloadParams(args, false, 2); // lazy: always return None even if it's a void function
2016-11-23 06:32:48 +08:00
/// Call corresponding cython function
file.oss << argumentList(i).pyx_convertEigenTypeAndStorageOrder(" ");
string call = pyx_functionCall("", pxdName(), i);
2016-11-23 06:32:48 +08:00
if (!returnVals_[i].isVoid()) {
file.oss << " return_value = " << call << "\n";
file.oss << " return True, " << returnVals_[i].pyx_casting("return_value") << "\n";
2016-11-23 06:32:48 +08:00
} else {
file.oss << " " << call << "\n";
file.oss << " return True, None\n";
2016-11-23 06:32:48 +08:00
}
file.oss << " except:\n";
file.oss << " return False, None\n\n";
2016-11-23 06:32:48 +08:00
}
}
/* ************************************************************************* */
} // \namespace wrap