diff --git a/wrap/Argument.cpp b/wrap/Argument.cpp index 2edd206f9..67d5c2870 100644 --- a/wrap/Argument.cpp +++ b/wrap/Argument.cpp @@ -100,7 +100,7 @@ void Argument::proxy_check(FileWriter& proxyFile, const string& s) const { /* ************************************************************************* */ void Argument::emit_cython_pxd(FileWriter& file) const { - string typeName = type.qualifiedName("_"); + string typeName = type.cythonClassName(); string cythonType = typeName; if (type.isEigen()) { cythonType = "Map[" + typeName + "Xd]&"; diff --git a/wrap/Class.cpp b/wrap/Class.cpp index 2397899ab..0eb28e55c 100644 --- a/wrap/Class.cpp +++ b/wrap/Class.cpp @@ -686,7 +686,7 @@ void Class::emit_cython_pxd(FileWriter& pxdFile) const { } pxdFile.oss << "]"; } - if (parentClass) pxdFile.oss << "(" << parentClass->qualifiedName("_") << ")"; + if (parentClass) pxdFile.oss << "(" << parentClass->cythonClassName() << ")"; pxdFile.oss << ":\n"; constructor.emit_cython_pxd(pxdFile, cythonClassName()); @@ -741,7 +741,10 @@ void Class::emit_cython_pyx(FileWriter& pyxFile, const std::vector& allCl // __cinit___ pyxFile.oss << "\tdef __cinit__(self):\n" "\t\tself." << pyxCythonObj() << " = " - << pyxSharedCythonClass() << "(new " << pyxCythonClass() << "())\n"; + << pyxSharedCythonClass() << "("; + if (constructor.hasDefaultConstructor()) + pyxFile.oss << "new " << pyxCythonClass() << "()"; + pyxFile.oss << ")\n"; pyxInitParentObj(pyxFile, "\t\tself", "self." + pyxCythonObj(), allClasses); // cyCreateFromShared @@ -753,17 +756,23 @@ void Class::emit_cython_pyx(FileWriter& pyxFile, const std::vector& allCl pyxInitParentObj(pyxFile, "\t\tret", "other", allClasses); pyxFile.oss << "\t\treturn ret" << "\n"; - // cyCreateFromValue - pyxFile.oss << "\t@staticmethod\n"; - pyxFile.oss << "\tcdef " << pythonClassName() << " cyCreateFromValue(const " - << pyxCythonClass() << "& value):\n" - << "\t\tcdef " << pythonClassName() - << " ret = " << pythonClassName() << "()\n" - << "\t\tret." << pyxCythonObj() << " = " << pyxSharedCythonClass() - << "(new " << pyxCythonClass() << "(value))\n"; - pyxInitParentObj(pyxFile, "\t\tret", "ret." + pyxCythonObj(), allClasses); - pyxFile.oss << "\t\treturn ret" << "\n"; - pyxFile.oss << "\n"; + // Generate cyCreateFromValue by default, although for some classes it can't be used + // It's only usable if its copy constructor AND its copy assignment operator exist + // Copy assignment operator is needed because Cython might assign the obj to its temp variable. + // Some class (e.g. noiseModel::Robust) have copy constructor but no copy assignment operator + if (constructor.nrOverloads() >= 1) { + // cyCreateFromValue + pyxFile.oss << "\t@staticmethod\n"; + pyxFile.oss << "\tcdef " << pythonClassName() << " cyCreateFromValue(const " + << pyxCythonClass() << "& value):\n" + << "\t\tcdef " << pythonClassName() + << " ret = " << pythonClassName() << "()\n" + << "\t\tret." << pyxCythonObj() << " = " << pyxSharedCythonClass() + << "(new " << pyxCythonClass() << "(value))\n"; + pyxInitParentObj(pyxFile, "\t\tret", "ret." + pyxCythonObj(), allClasses); + pyxFile.oss << "\t\treturn ret" << "\n"; + pyxFile.oss << "\n"; + } // Constructors constructor.emit_cython_pyx(pyxFile, *this); diff --git a/wrap/Constructor.cpp b/wrap/Constructor.cpp index 17c708cda..b221b77c2 100644 --- a/wrap/Constructor.cpp +++ b/wrap/Constructor.cpp @@ -160,10 +160,17 @@ void Constructor::emit_cython_pyx(FileWriter& pyxFile, const Class& cls) const { << ((i > 0) ? to_string(i) : "") << "__("; args.emit_cython_pyx(pyxFile); pyxFile.oss << "): \n"; + + // Don't use cyCreateFromValue because the class might not have + // copy constructor and copy assignment operator!! + // For example: noiseModel::Robust doesn't have the copy assignment operator + // because its members are shared_ptr to abstract base classes. That fails + // Cython to generate the object as it assigns the new obj to a temp variable. pyxFile.oss << "\t\treturn " << cls.cythonClassName() - << ".cyCreateFromValue(" << cls.pyxCythonClass() << "("; + << ".cyCreateFromShared(" << cls.pyxSharedCythonClass() + << "(new " << cls.pyxCythonClass() << "("; args.emit_cython_pyx_asParams(pyxFile); - pyxFile.oss << ")" + pyxFile.oss << "))" << ")\n"; } } diff --git a/wrap/Method.cpp b/wrap/Method.cpp index d3acf7e51..6d297d58d 100644 --- a/wrap/Method.cpp +++ b/wrap/Method.cpp @@ -81,7 +81,7 @@ void Method::emit_cython_pxd(FileWriter& file) const { for(size_t i = 0; i < nrOverloads(); ++i) { file.oss << "\t\t"; returnVals_[i].emit_cython_pxd(file); - file.oss << name_ << "("; + file.oss << ((name_ == "print") ? "_print \"print\"" : name_) << "("; argumentList(i).emit_cython_pxd(file); file.oss << ")"; if (is_const_) file.oss << " const"; @@ -93,11 +93,17 @@ void Method::emit_cython_pxd(FileWriter& file) const { /* ************************************************************************* */ void Method::emit_cython_pyx(FileWriter& file, const Class& cls) const { string funcName = ((name_ == "print") ? "_print" : name_); - // don't support overloads for static method :-( size_t N = nrOverloads(); for(size_t i = 0; i < N; ++i) { file.oss << "\tdef " << funcName; + // modify name of function instantiation as python doesn't allow overloads + // e.g. template funcName(...) --> funcNameA, funcNameB, funcNameC + // TODO: handle overloading properly!! This is lazy... if (templateArgValue_) file.oss << templateArgValue_->name(); + // change function overload's name: funcName(...) --> funcName_1, funcName_2 + // TODO: handle overloading properly!! This is lazy... + file.oss << ((i>0)? "_" + to_string(i):""); + // funtion arguments file.oss << "(self"; if (argumentList(i).size() > 0) file.oss << ", "; argumentList(i).emit_cython_pyx(file); @@ -109,8 +115,8 @@ void Method::emit_cython_pyx(FileWriter& file, const Class& cls) const { // ... casting return value returnVals_[i].emit_cython_pyx_casting(file); if (!returnVals_[i].isVoid()) file.oss << "("; - file.oss << "self." << cls.pyxCythonObj() << "." << funcName; - // if (templateArgValue_) file.oss << "[" << templateArgValue_->pyxCythonClass() << "]"; + file.oss << "self." << cls.pyxCythonObj() << ".get()." << funcName; + if (templateArgValue_) file.oss << "[" << templateArgValue_->pyxCythonClass() << "]"; // ... argument list file.oss << "("; diff --git a/wrap/Module.cpp b/wrap/Module.cpp index 7c27f9170..9b2481433 100644 --- a/wrap/Module.cpp +++ b/wrap/Module.cpp @@ -334,9 +334,11 @@ void Module::emit_cython_pxd(FileWriter& pxdFile) const { void Module::emit_cython_pyx(FileWriter& pyxFile) const { // headers... pyxFile.oss << "cimport numpy as np\n" - "cimport gtsam_wrapper as gtsam\n" - "from gtsam_wrapper cimport shared_ptr\n" + "cimport cythontest_wrapper as gtsam\n" + "from cythontest_wrapper cimport shared_ptr\n" "from eigency.core cimport *\n" + "from libcpp cimport bool\n\n" + "from libcpp.pair cimport pair\n" "from libcpp.string cimport string\n" "from cython.operator cimport dereference as deref\n\n\n"; for(const Class& cls: expandedClasses) diff --git a/wrap/Qualified.h b/wrap/Qualified.h index 879efac31..69ed3e7d2 100644 --- a/wrap/Qualified.h +++ b/wrap/Qualified.h @@ -185,16 +185,17 @@ public: /// return the Cython class in pxd corresponding to a Python class in pyx std::string pyxCythonClass() const { - if (isNonBasicType()) + if (isNonBasicType()) { if (namespaces_.size() > 0) return namespaces_[0] + "." + cythonClassName(); else { std::cerr << "Class without namespace: " << cythonClassName() << std::endl; throw std::runtime_error("Error: User type without namespace!!"); } + } else if (isEigen()) { return name_ + "Xd"; - } else + } else // basic types and not Eigen return name_; } diff --git a/wrap/ReturnType.cpp b/wrap/ReturnType.cpp index 2d7f63591..48c9de648 100644 --- a/wrap/ReturnType.cpp +++ b/wrap/ReturnType.cpp @@ -67,13 +67,11 @@ void ReturnType::wrapTypeUnwrap(FileWriter& wrapperFile) const { /* ************************************************************************* */ void ReturnType::emit_cython_pxd(FileWriter& file) const { - string typeName = qualifiedName("_"); - string cythonType = typeName; - if (typeName=="Vector" || typeName == "Matrix") { - cythonType = typeName + "Xd"; - } else { - if (isPtr) cythonType = "shared_ptr[" + typeName + "]"; - } + string typeName = cythonClassName(); + string cythonType; + if (isEigen()) cythonType = typeName + "Xd"; + else if (isPtr) cythonType = "shared_ptr[" + typeName + "]"; + else cythonType = typeName; file.oss << cythonType; } @@ -85,6 +83,8 @@ void ReturnType::emit_cython_pyx_casting(FileWriter& file) const { if (isPtr) file.oss << pythonClassName() << ".cyCreateFromShared"; else { + // if the function return an object, it must be copy constructible and copy assignable + // so it's safe to use cyCreateFromValue file.oss << pythonClassName() << ".cyCreateFromValue"; } } diff --git a/wrap/ReturnValue.cpp b/wrap/ReturnValue.cpp index 113109b3d..d0af2d60a 100644 --- a/wrap/ReturnValue.cpp +++ b/wrap/ReturnValue.cpp @@ -86,11 +86,17 @@ void ReturnValue::emit_cython_pxd(FileWriter& file) const { void ReturnValue::emit_cython_pyx_casting(FileWriter& file) const { if (isVoid()) return; if (isPair) { - file.oss << "("; - type1.emit_cython_pyx_casting(file); - file.oss << ","; - type2.emit_cython_pyx_casting(file); - file.oss << ")"; + // file.oss << "cdef pair[" << type1.pyxCythonClass() << "," << type2.pyxCythonClass() << "]" + // << "ret = "; + // file.oss << "("; + // type1.emit_cython_pyx_casting(file); + // file.oss << "("; + // file.oss << type1.pyxCythonClass(); + // file.oss << "),"; + // type2.emit_cython_pyx_casting(file); + // file.oss << "("; + // file.oss << type2.pyxCythonClass(); + // file.oss << "))"; } else { type1.emit_cython_pyx_casting(file); } diff --git a/wrap/StaticMethod.cpp b/wrap/StaticMethod.cpp index 05374f979..2e5f9928f 100644 --- a/wrap/StaticMethod.cpp +++ b/wrap/StaticMethod.cpp @@ -63,7 +63,9 @@ void StaticMethod::emit_cython_pxd(FileWriter& file) const { file.oss << "\t\t@staticmethod\n"; file.oss << "\t\t"; returnVals_[i].emit_cython_pxd(file); - file.oss << name_ << ((i>0)?"_"+to_string(i):"") << "("; + file.oss << name_ << ((i > 0) ? "_" + to_string(i) : "") << " \"" << name_ + << "\"" + << "("; argumentList(i).emit_cython_pxd(file); file.oss << ")\n"; } @@ -74,17 +76,23 @@ void StaticMethod::emit_cython_pyx(FileWriter& file, const Class& cls) const { // don't support overloads for static method :-( for(size_t i = 0; i < nrOverloads(); ++i) { file.oss << "\t@staticmethod\n"; - file.oss << "\tdef " << name_ << ((i>0)? "_" + to_string(i):"") << "("; + file.oss << "\tdef " << name_ << ((i > 0) ? "_" + to_string(i) : "") + << "("; argumentList(i).emit_cython_pyx(file); file.oss << "):\n"; file.oss << "\t\t"; if (!returnVals_[i].isVoid()) file.oss << "return "; - file.oss << cls.pythonClassName() << ".cyCreate(" - << cls.pyxCythonClass() << "." - << name_ << ((i>0)? "_" + to_string(i):"") - << "("; + //... casting return value + returnVals_[i].emit_cython_pyx_casting(file); + if (!returnVals_[i].isVoid()) file.oss << "("; + + file.oss << cls.pyxCythonClass() << "." + << name_ << ((i>0)? "_" + to_string(i):""); + if (templateArgValue_) file.oss << "[" << templateArgValue_->pyxCythonClass() << "]"; + file.oss << "("; argumentList(i).emit_cython_pyx_asParams(file); - file.oss << "))\n"; + if (!returnVals_[i].isVoid()) file.oss << ")"; + file.oss << ")\n"; } } diff --git a/wrap/tests/cythontest.h b/wrap/tests/cythontest.h index 62597bcd8..d154ff778 100644 --- a/wrap/tests/cythontest.h +++ b/wrap/tests/cythontest.h @@ -15,10 +15,129 @@ typedef gtsam::FastSet KeySet; #include template class FastMap{}; +#include +class Point2 { + // Standard Constructors + Point2(); + Point2(double x, double y); + // Point2(Vector v); + + // Testable + void print(string s) const; + bool equals(const gtsam::Point2& pose, double tol) const; + + // Group + static gtsam::Point2 identity(); + + // Standard Interface + double x() const; + double y() const; + Vector vector() const; + double distance(const gtsam::Point2& p2) const; + double norm() const; + + // enabling serialization functionality + void serialize() const; +}; + +#include +class Rot2 { + // Standard Constructors and Named Constructors + Rot2(); + Rot2(double theta); + static gtsam::Rot2 fromAngle(double theta); + static gtsam::Rot2 fromDegrees(double theta); + static gtsam::Rot2 fromCosSin(double c, double s); + + // Testable + void print(string s) const; + bool equals(const gtsam::Rot2& rot, double tol) const; + + // Group + static gtsam::Rot2 identity(); + gtsam::Rot2 inverse(); + gtsam::Rot2 compose(const gtsam::Rot2& p2) const; + gtsam::Rot2 between(const gtsam::Rot2& p2) const; + + // Manifold + gtsam::Rot2 retract(Vector v) const; + Vector localCoordinates(const gtsam::Rot2& p) const; + + // Lie Group + static gtsam::Rot2 Expmap(Vector v); + static Vector Logmap(const gtsam::Rot2& p); + + // Group Action on Point2 + gtsam::Point2 rotate(const gtsam::Point2& point) const; + gtsam::Point2 unrotate(const gtsam::Point2& point) const; + + // Standard Interface + static gtsam::Rot2 relativeBearing(const gtsam::Point2& d); // Ignoring derivative + static gtsam::Rot2 atan2(double y, double x); + double theta() const; + double degrees() const; + double c() const; + double s() const; + Matrix matrix() const; + + // enabling serialization functionality + void serialize() const; +}; + +#include +class Pose2 { + // Standard Constructor + Pose2(); + Pose2(const gtsam::Pose2& pose); + Pose2(double x, double y, double theta); + Pose2(double theta, const gtsam::Point2& t); + Pose2(const gtsam::Rot2& r, const gtsam::Point2& t); + // Pose2(Vector v); + + // Testable + void print(string s) const; + bool equals(const gtsam::Pose2& pose, double tol) const; + + // Group + static gtsam::Pose2 identity(); + gtsam::Pose2 inverse() const; + gtsam::Pose2 compose(const gtsam::Pose2& p2) const; + gtsam::Pose2 between(const gtsam::Pose2& p2) const; + + // Manifold + gtsam::Pose2 retract(Vector v) const; + Vector localCoordinates(const gtsam::Pose2& p) const; + + // Lie Group + static gtsam::Pose2 Expmap(Vector v); + static Vector Logmap(const gtsam::Pose2& p); + Matrix AdjointMap() const; + Vector Adjoint(const Vector& xi) const; + static Matrix wedge(double vx, double vy, double w); + + // Group Actions on Point2 + gtsam::Point2 transform_from(const gtsam::Point2& p) const; + gtsam::Point2 transform_to(const gtsam::Point2& p) const; + + // Standard Interface + double x() const; + double y() const; + double theta() const; + gtsam::Rot2 bearing(const gtsam::Point2& point) const; + double range(const gtsam::Point2& point) const; + gtsam::Point2 translation() const; + gtsam::Rot2 rotation() const; + Matrix matrix() const; + + // enabling serialization functionality + void serialize() const; +}; + #include class Point3 { // Standard Constructors Point3(); + Point3(const Point3& p); Point3(double x, double y, double z); Point3(Vector v); @@ -43,6 +162,7 @@ class Point3 { class Rot3 { // Standard Constructors and Named Constructors Rot3(); + Rot3(const Rot3& R); Rot3(Matrix R); static gtsam::Rot3 Rx(double t); static gtsam::Rot3 Ry(double t); @@ -62,7 +182,7 @@ class Rot3 { // Group static gtsam::Rot3 identity(); - gtsam::Rot3 inverse() const; + gtsam::Rot3 inverse() const; gtsam::Rot3 compose(const gtsam::Rot3& p2) const; gtsam::Rot3 between(const gtsam::Rot3& p2) const; @@ -94,6 +214,54 @@ class Rot3 { void serialize() const; }; +#include +class Pose3 { + // Standard Constructors + Pose3(); + Pose3(const gtsam::Pose3& pose); + Pose3(const gtsam::Rot3& r, const gtsam::Point3& t); + Pose3(const gtsam::Pose2& pose2); // FIXME: shadows Pose3(Pose3 pose) + Pose3(Matrix t); + + // Testable + void print(string s) const; + bool equals(const gtsam::Pose3& pose, double tol) const; + + // Group + static gtsam::Pose3 identity(); + gtsam::Pose3 inverse() const; + gtsam::Pose3 compose(const gtsam::Pose3& p2) const; + gtsam::Pose3 between(const gtsam::Pose3& p2) const; + + // Manifold + gtsam::Pose3 retract(Vector v) const; + Vector localCoordinates(const gtsam::Pose3& T2) const; + + // Lie Group + static gtsam::Pose3 Expmap(Vector v); + static Vector Logmap(const gtsam::Pose3& p); + Matrix AdjointMap() const; + Vector Adjoint(Vector xi) const; + static Matrix wedge(double wx, double wy, double wz, double vx, double vy, double vz); + + // Group Action on Point3 + gtsam::Point3 transform_from(const gtsam::Point3& p) const; + gtsam::Point3 transform_to(const gtsam::Point3& p) const; + + // Standard Interface + gtsam::Rot3 rotation() const; + gtsam::Point3 translation() const; + double x() const; + double y() const; + double z() const; + Matrix matrix() const; + gtsam::Pose3 transform_to(const gtsam::Pose3& pose) const; // FIXME: shadows other transform_to() + double range(const gtsam::Point3& point); + double range(const gtsam::Pose3& pose); + + // enabling serialization functionality + void serialize() const; +}; #include class VectorValues { @@ -153,8 +321,6 @@ virtual class Diagonal : gtsam::noiseModel::Gaussian { static gtsam::noiseModel::Diagonal* Sigmas(Vector sigmas); static gtsam::noiseModel::Diagonal* Variances(Vector variances); static gtsam::noiseModel::Diagonal* Precisions(Vector precisions); - Matrix R() const; - void print(string s) const; // enabling serialization functionality void serializable() const; @@ -213,7 +379,7 @@ virtual class GaussianFactor { Matrix augmentedInformation() const; Matrix information() const; Matrix augmentedJacobian() const; - pair jacobian() const; + // pair jacobian() const; size_t size() const; bool empty() const; }; @@ -230,24 +396,20 @@ virtual class JacobianFactor : gtsam::GaussianFactor { const gtsam::noiseModel::Diagonal* model); JacobianFactor(size_t i1, Matrix A1, size_t i2, Matrix A2, size_t i3, Matrix A3, Vector b, const gtsam::noiseModel::Diagonal* model); - JacobianFactor(const gtsam::GaussianFactorGraph& graph); + // JacobianFactor(const gtsam::GaussianFactorGraph& graph); //Testable - void print(string s) const; void printKeys(string s) const; - bool equals(const gtsam::GaussianFactor& lf, double tol) const; - size_t size() const; Vector unweighted_error(const gtsam::VectorValues& c) const; Vector error_vector(const gtsam::VectorValues& c) const; - double error(const gtsam::VectorValues& c) const; //Standard Interface - Matrix getA() const; - Vector getb() const; + Matrix py_getA() const; + Vector py_getb() const; size_t rows() const; size_t cols() const; bool isConstrained() const; - pair jacobianUnweighted() const; + // pair jacobianUnweighted() const; Matrix augmentedJacobianUnweighted() const; void transposeMultiplyAdd(double alpha, const Vector& e, gtsam::VectorValues& x) const; @@ -298,10 +460,10 @@ class Values { // void update(size_t j, const gtsam::Value& val); // gtsam::Value at(size_t j) const; - template + template void insert(size_t j, const T& t); - template + template void update(size_t j, const T& t); template