Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace SWIG bindings with Pybind11, and remove SWIG dependency #4949

Merged
merged 40 commits into from
Feb 13, 2017

Conversation

rdeits
Copy link
Contributor

@rdeits rdeits commented Jan 28, 2017

This isn't quite ready for review yet, but I want to see how it does on CI. This also includes the changes from #4944 which should come in first. I'll update this branch when that happens.

This replaces all of the pydrake bindings with identical bindings generated with Pybind11. With this change and #4944 we no longer have any dependencies on SWIG, so this also deletes the SWIG and swigmake externals.

This also:

  • changes the python binding build process, so there should no longer be any python build files in the source tree
  • removes all the #ifndef SWIG guards

Resolves #4486
Fixes #4454
Fixes #4243
Fixes #2646
Fixes #4469

Todo before merging:

  • Undo my evil hacks to the build system
  • Make sure there aren't any other methods that SWIG was generating that aren't present here
  • Discuss cleaning up the Python APIs

This change is Reviewable

# else()
# find_package(Python 2.7 MODULE REQUIRED)
# endif()
find_package(pybind11 REQUIRED)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Todo: something that isn't this

@@ -61,7 +61,7 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror=all -Werror=ignored-qualifiers -
if(NOT CMAKE_CXX_FLAGS MATCHES "-fsanitize=") # sanitizers are extremely brittle without lazy linking
if(APPLE)
set(CMAKE_SHARED_LINKER_FLAGS "-Wl,-undefined -Wl,error ${CMAKE_SHARED_LINKER_FLAGS}")
set(CMAKE_MODULE_LINKER_FLAGS "-Wl,-undefined -Wl,error ${CMAKE_MODULE_LINKER_FLAGS}")
# set(CMAKE_MODULE_LINKER_FLAGS "-Wl,-undefined -Wl,error ${CMAKE_MODULE_LINKER_FLAGS}")
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Todo: restore this flag in a way that doesn't break the python bindings

@rdeits
Copy link
Contributor Author

rdeits commented Feb 1, 2017

Ok, modulo the clang issue, this is getting pretty close to ready. @patmarion what do I need to do to make sure all the director IK interfaces are working?

@rdeits
Copy link
Contributor Author

rdeits commented Feb 1, 2017

More cmake issues here... I'm currently running into the fact that pybind11 ships its own FindPythonLibsNew.cmake, but that file won't be run if python has already been found. Not running it causes various subtle bugs like the python modules having the wrong file extension. I'm currently hacking around it by just calling find_package(pybind11) earlier in the drake build process, but that's not a good solution.

@rdeits
Copy link
Contributor Author

rdeits commented Feb 1, 2017

@patmarion OK, pydrakeik tests are now working.

@rdeits
Copy link
Contributor Author

rdeits commented Feb 1, 2017

Ok, this is good enough that I'd appreciate some feedback on it, and some help resolving the clang issues.

@rdeits
Copy link
Contributor Author

rdeits commented Feb 2, 2017

The clang issues appear to be due to pybind/pybind11#639

@rdeits
Copy link
Contributor Author

rdeits commented Feb 2, 2017

And that issue is fixed in pybind/pybind11#604

@rdeits
Copy link
Contributor Author

rdeits commented Feb 8, 2017

Huge thanks to @mwoehlke-kitware for figuring out the automagic Eigen-Numpy conversions for custom scalar types. That's let me substantially simplify the python wrapper code, and will also help with #4771 . I've also switched over to our temporary pybind11 fork, which should (hopefully) resolve the clang issues.

@rdeits
Copy link
Contributor Author

rdeits commented Feb 8, 2017

Review status: 0 of 47 files reviewed at latest revision, 2 unresolved discussions.


drake/CMakeLists.txt, line 64 at r1 (raw file):

Previously, rdeits (Robin Deits) wrote…

Todo: restore this flag in a way that doesn't break the python bindings

Done.


cmake/config.cmake, line 212 at r1 (raw file):

Previously, rdeits (Robin Deits) wrote…

Todo: something that isn't this

Done.


Comments from Reviewable

@rdeits
Copy link
Contributor Author

rdeits commented Feb 8, 2017

woohoo! this is finally working!

@rdeits
Copy link
Contributor Author

rdeits commented Feb 8, 2017

+@sammy-tri +@jamiesnape +@mwoehlke-kitware


Review status: 0 of 47 files reviewed at latest revision, 2 unresolved discussions.


Comments from Reviewable

@jamiesnape
Copy link
Contributor

Reviewed 21 of 47 files at r1, 1 of 2 files at r2, 6 of 27 files at r3, 5 of 13 files at r5, 2 of 10 files at r6.
Review status: 34 of 47 files reviewed at latest revision, 1 unresolved discussion.


drake/bindings/pybind11/CMakeLists.txt, line 9 at r6 (raw file):

endif()

# Look up the python module suffix (which varies by python version).

Could this be added to FindPython.cmake?


Comments from Reviewable

@rdeits
Copy link
Contributor Author

rdeits commented Feb 8, 2017

Review status: 34 of 47 files reviewed at latest revision, 1 unresolved discussion.


drake/bindings/pybind11/CMakeLists.txt, line 9 at r6 (raw file):

Previously, jamiesnape (Jamie Snape) wrote…

Could this be added to FindPython.cmake?

good point. I forgot we were shipping our own FindPython


Comments from Reviewable

@rdeits
Copy link
Contributor Author

rdeits commented Feb 8, 2017

Review status: 34 of 48 files reviewed at latest revision, 1 unresolved discussion.


drake/bindings/pybind11/CMakeLists.txt, line 9 at r6 (raw file):

Previously, rdeits (Robin Deits) wrote…

good point. I forgot we were shipping our own FindPython

Done.


Comments from Reviewable

@jamiesnape
Copy link
Contributor

Review status: 34 of 48 files reviewed at latest revision, 2 unresolved discussions.


cmake/modules/FindPython.cmake, line 38 at r8 (raw file):

    OUTPUT_STRIP_TRAILING_WHITESPACE)

if(NOT PYTHON_MODULE_EXTENSION_SUCCESS MATCHES 0)

Remove this and add PYTHON_MODULE_EXTENSION to REQUIRED_VARS. If PYTHON_MODULE_EXTENSION is empty, then find_package will fail.


Comments from Reviewable

@rdeits
Copy link
Contributor Author

rdeits commented Feb 8, 2017

Review status: 34 of 48 files reviewed at latest revision, 2 unresolved discussions.


cmake/modules/FindPython.cmake, line 38 at r8 (raw file):

Previously, jamiesnape (Jamie Snape) wrote…

Remove this and add PYTHON_MODULE_EXTENSION to REQUIRED_VARS. If PYTHON_MODULE_EXTENSION is empty, then find_package will fail.

Done.


Comments from Reviewable

@jamiesnape
Copy link
Contributor

Review status: 34 of 48 files reviewed at latest revision, 3 unresolved discussions, some commit checks failed.


cmake/modules/FindPython.cmake, line 38 at r8 (raw file):

Previously, rdeits (Robin Deits) wrote…

Done.

You should only set PYTHON_MODULE_EXTENSION if PYTHON_MODULE_EXTENSION_SUCCESS indicates success. You probably do not need to capture ERROR_VARIABLE.


cmake/modules/FindPython.cmake, line 40 at r9 (raw file):

find_package_handle_standard_args(Python
  REQUIRED_VARS PYTHON_EXECUTABLE PYTHON_INCLUDE_DIR PYTHON_LIBRARY
  VERSION_VAR PYTHON_VERSION_STRING PYTHON_MODULE_EXTENSION)

Needs to be on the line above. VERSION_VAR is the name of an argument rather than a required variable.


Comments from Reviewable

@rdeits
Copy link
Contributor Author

rdeits commented Feb 8, 2017

Review status: 34 of 48 files reviewed at latest revision, 3 unresolved discussions.


cmake/modules/FindPython.cmake, line 38 at r8 (raw file):

Previously, jamiesnape (Jamie Snape) wrote…

You should only set PYTHON_MODULE_EXTENSION if PYTHON_MODULE_EXTENSION_SUCCESS indicates success. You probably do not need to capture ERROR_VARIABLE.

Done.


cmake/modules/FindPython.cmake, line 40 at r9 (raw file):

Previously, jamiesnape (Jamie Snape) wrote…

Needs to be on the line above. VERSION_VAR is the name of an argument rather than a required variable.

Agh, sorry. Done.


Comments from Reviewable

@sammy-tri
Copy link
Contributor

+(status: curate commits before merging)

Partial pass completed. Left a few comments.


Reviewed 21 of 47 files at r1, 10 of 27 files at r3, 1 of 9 files at r4, 3 of 13 files at r5, 7 of 10 files at r6, 1 of 1 files at r7, 1 of 2 files at r8, 1 of 1 files at r10.
Review status: 46 of 48 files reviewed at latest revision, 8 unresolved discussions.


drake/bindings/CMakeLists.txt, line 1 at r10 (raw file):

if(NOT DISABLE_PYTHON)

BTW It seems odd to duplicate this check in drake/CMakeLists.txt and this file.


drake/bindings/pybind11/pydrake_autodiffutils.cc, line 13 at r10 (raw file):

using std::cos;

template <typename Derived>

BTW this function could maybe use a comment... it's not obvious where an AutoDiffScalar templated on a Derived which isn't VectorXd would come from.


drake/bindings/pybind11/pydrake_rbtree.cc, line 38 at r10 (raw file):

                urdf_filename, floating_base_type, &instance);
        },
        py::arg("urdf_filename"), py::arg("joint_type")="ROLLPITCHYAW"

Missing whitespace around = operator


drake/bindings/python/pydrake/init.py, line 1 at r10 (raw file):

from __future__ import absolute_import, division, print_function

do division and print_function do anything by being imported in this file? (or several other places where they're used?)


drake/bindings/python/pydrake/autodiffutils.py, line 3 at r10 (raw file):

from __future__ import absolute_import, division, print_function

import numpy as np

What effect does this have here when we don't reference np later?


Comments from Reviewable

@rdeits
Copy link
Contributor Author

rdeits commented Feb 9, 2017

This currently doesn't compile on OSX due to issues finding the numpy headers. We may need one more update to our pybind11 fork

@patmarion
Copy link
Member

btw, director uses a copy of this file to find NumPy headers:

https://raw.githubusercontent.com/pydata/numexpr/master/FindNumPy.cmake

and uses it like this:

find_package(NumPy)

if (NOT NUMPY_FOUND)
  message(WARNING "Optimized transformations C extension module is disabled because numpy is not found.")
  return()
endif()

if (NUMPY_VERSION VERSION_LESS 1.7)
  message(WARNING "Optimized transformations C extension module is disabled because numpy version < 1.7 was found.")
  return()
endif()


include_directories(${NUMPY_INCLUDE_DIRS})

@rdeits
Copy link
Contributor Author

rdeits commented Feb 9, 2017

Review status: 42 of 48 files reviewed at latest revision, 5 unresolved discussions.


drake/bindings/CMakeLists.txt, line 1 at r10 (raw file):

Previously, sammy-tri (Sam Creasey) wrote…

BTW It seems odd to duplicate this check in drake/CMakeLists.txt and this file.

Done.


drake/bindings/pybind11/pydrake_autodiffutils.cc, line 13 at r10 (raw file):

Previously, sammy-tri (Sam Creasey) wrote…

BTW this function could maybe use a comment... it's not obvious where an AutoDiffScalar templated on a Derived which isn't VectorXd would come from.

Done. The other Derived type is Eigen::CWiseBinaryOp which represents an un-evaluated computation.


drake/bindings/pybind11/pydrake_rbtree.cc, line 38 at r10 (raw file):

Previously, sammy-tri (Sam Creasey) wrote…

Missing whitespace around = operator

Done.


drake/bindings/python/pydrake/init.py, line 1 at r10 (raw file):

Previously, sammy-tri (Sam Creasey) wrote…

do division and print_function do anything by being imported in this file? (or several other places where they're used?)

No, they have no effect. I'm personally trying t get into the habit of just adding this boilerplate to the top of all my python files because I'm accustomed to the python3 behavior of division and print() and would like to use them everywhere.

This is different from an unused import of a package because it doesn't actually load any code, and it prevents subtle bugs later on.

For example, if I forget to import numpy and then try to write numpy.zeros(), I get a helpful error message that numpy was not found. But if I forget to from __future__ import division and then try to write 1 / 2, I don't get an error: I just get the wrong answer. So, better to just have the import and not need it than to not have it and cause problems later.


drake/bindings/python/pydrake/autodiffutils.py, line 3 at r10 (raw file):

Previously, sammy-tri (Sam Creasey) wrote…

What effect does this have here when we don't reference np later?

Done. Just a leftover import that I forgot to remove.


Comments from Reviewable

@rdeits
Copy link
Contributor Author

rdeits commented Feb 10, 2017

@drake-jenkins-bot mac-clang-experimental please

@rdeits
Copy link
Contributor Author

rdeits commented Feb 10, 2017

Oops, had a conflict with master. Let's try this again, post-rebase:

@drake-jenkins-bot mac-clang-experimental please

@rdeits
Copy link
Contributor Author

rdeits commented Feb 11, 2017

-@mwoehlke-kitware since this already has two assigned reviewers


Review status: 41 of 50 files reviewed at latest revision, all discussions resolved, some commit checks failed.


Comments from Reviewable

@rdeits
Copy link
Contributor Author

rdeits commented Feb 11, 2017

I don't know what's up with the linux-gcc-experimental-ros build. Is that a new one?

@jwnimmer-tri
Copy link
Collaborator

Its another #3429 flake.
@drake-jenkins-bot linux-gcc-experimental-ros please

@jamiesnape
Copy link
Contributor

:lgtm:


Reviewed 8 of 8 files at r13, 1 of 1 files at r14.
Review status: all files reviewed at latest revision, all discussions resolved.


Comments from Reviewable

@sammy-tri
Copy link
Contributor

Reviewed 1 of 1 files at r12, 8 of 8 files at r13, 1 of 1 files at r14.
Review status: all files reviewed at latest revision, all discussions resolved.


Comments from Reviewable

@rdeits
Copy link
Contributor Author

rdeits commented Feb 13, 2017

Is this good to go? Reviewable doesn't like my commit history, but I assume we can just squash-merge this PR in, right?

@jamiesnape
Copy link
Contributor

Yes to both questions.

@rdeits rdeits merged commit 09bf14b into RobotLocomotion:master Feb 13, 2017
@rdeits rdeits deleted the pybind-rbtree branch February 13, 2017 16:42
@SeanCurtis-TRI
Copy link
Contributor

For the record, does this produce a bunch of files that should be included in .gitignore?

I pulled master and built, and now I've got a number of files in drake/bindings/python as untracked changes.

@jamiesnape
Copy link
Contributor

Should be the opposite. You have old SWIG build products.

@SeanCurtis-TRI
Copy link
Contributor

Ah...so you're suggesting they didn't appear as part of the build process, but in simply updating my master. And, in fact, these are files that are old and are just now visible to git. I certainly have externals/swigmake and externals/swig_matlab appearing. So, I guess the drake/bindings/python/pydrake/ stuff is related. Thanks.

@rdeits
Copy link
Contributor Author

rdeits commented Feb 13, 2017 via email

kunimatsu-tri pushed a commit to kunimatsu-tri/drake that referenced this pull request Mar 1, 2017
…tLocomotion#4949)

* hack-y first pass at using pybind11 in drake

* evil hacks to make pybind11 work

* most of the RBT tests now pass with pybind11

* autodiff types work in pybind11 wrappers

* pybind11 bindings now work for rbtree and IK tests

* work around weird IK constraint argument requirements

* add ik interfaces needed by director

* remove swig_matlab and swigmake dependencies

* move new pybind11 bindings into the bindings folder

* remove all #ifndef SWIG checks

* slightly fewer evil build hacks

* only build pybind11 bindings if pybind11 was found

* fix drake autogenerated path file

* localize build system hacks to the pybind11 folder

* fix pybind11 linking on osx

* clean up and simplify the pydrake autodiff interface

Rather than exposing raw autodiff wrappers to the user, we now provide
the pydrake.forwarddiff module, which has the following functions:

derivative(f, x)
gradient(f, x)
jacobian(f, x)

which all take a function f and an argument x and compute the relevant
quantity. The exciting part is that now that function f can include any
mix of drake code and python code, and the derivatives should still just
work. However, this feature will require some additional bindings (like
operator overloading for autodiff types) in order to be useful.

* add basic operator support to forwarddiff.py

* remove accidentally-committed file

* clean up

* more cleanup

* fix issues identified by cpplint

* rename all bindings files with pydrake prefix

* various fixes for director's pydrake IK

* comments

* make python boilerplate consistent

* attempt to avoid having to call find_package(pybind11) early

* fix linting issues

* restore default symbol visibility to fix cross-module pybind11 errors

* Use mwoehlke-kitware's automatic numpy-eigen conversions for autodiff

This substantially simplifies the python interface code and removes all
of the monkey-patching that I had previously done to make autodiff types
work naturally with numpy arrays.

* todos

* move python module suffix lookup to FindPython.cmake

* make PYTHON_MODULE_EXTENSION required

* fix cmake syntax

* only set python_module_extension on success

* the 0 of success

* rename result var for clarity

* address some comments

* try to handle finding numpy

* one more unused import

* update iris for pybind11 compatibility
@EricCousineau-TRI
Copy link
Contributor

Linking to #1267.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment