-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add preliminary binding example for systems framework.
- Loading branch information
1 parent
c8a6b51
commit ec458cf
Showing
9 changed files
with
520 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
# -*- python -*- | ||
|
||
load("@drake//tools/install:install.bzl", "install") | ||
load("//tools/lint:lint.bzl", "add_lint_tests") | ||
load( | ||
"//tools/skylark:pybind.bzl", | ||
"drake_pybind_library", | ||
"get_drake_pybind_installs", | ||
"get_pybind_library_dest", | ||
) | ||
load( | ||
"//tools/skylark:drake_py.bzl", | ||
"drake_py_binary", | ||
"drake_py_library", | ||
"drake_py_test", | ||
) | ||
load("//tools/skylark:6996.bzl", "adjust_labels_for_drake_hoist") | ||
|
||
package(default_visibility = adjust_labels_for_drake_hoist([ | ||
"//drake/bindings/pydrake:__subpackages__", | ||
])) | ||
|
||
drake_py_library( | ||
name = "module_py", | ||
srcs = ["__init__.py"], | ||
deps = [ | ||
"//drake/bindings/pydrake:common_py", | ||
# TODO(eric.cousineau): This does NOT include direct dependencies to | ||
# submodule. This is to preserve C++-like namespaces. | ||
], | ||
) | ||
|
||
drake_pybind_library( | ||
name = "framework_py", | ||
cc_so_name = "framework", | ||
cc_srcs = ["framework_py.cc"], | ||
py_deps = [ | ||
":module_py", | ||
], | ||
) | ||
|
||
drake_pybind_library( | ||
name = "primitives_py", | ||
cc_so_name = "primitives", | ||
cc_srcs = ["primitives_py.cc"], | ||
py_deps = [ | ||
":framework_py", | ||
":module_py", | ||
], | ||
) | ||
|
||
drake_pybind_library( | ||
name = "analysis_py", | ||
cc_so_name = "analysis", | ||
cc_srcs = ["analysis_py.cc"], | ||
py_deps = [ | ||
":framework_py", | ||
":module_py", | ||
], | ||
) | ||
|
||
drake_py_library( | ||
name = "drawing_py", | ||
srcs = ["drawing.py"], | ||
deps = [":module_py"], | ||
# TODO(eric.cousineau): Expose information to allow `imports = ...` to be | ||
# defined, rather than rely on `module_py`. | ||
) | ||
|
||
PYBIND_LIBRARIES = [ | ||
":analysis_py", | ||
":framework_py", | ||
":primitives_py", | ||
] | ||
|
||
PY_LIBRARIES = [ | ||
":drawing_py", | ||
":module_py", | ||
] | ||
|
||
drake_py_library( | ||
name = "systems", | ||
deps = PYBIND_LIBRARIES + PY_LIBRARIES, | ||
) | ||
|
||
install( | ||
name = "install", | ||
targets = PY_LIBRARIES, | ||
py_dest = get_pybind_library_dest(), | ||
deps = get_drake_pybind_installs(PYBIND_LIBRARIES), | ||
) | ||
|
||
drake_py_test( | ||
name = "system_general_test", | ||
size = "small", | ||
srcs = ["test/system_general_test.py"], | ||
deps = [ | ||
":analysis_py", | ||
":framework_py", | ||
":primitives_py", | ||
], | ||
) | ||
|
||
drake_py_binary( | ||
name = "system_graphviz_example", | ||
srcs = ["test/system_graphviz_example.py"], | ||
deps = [ | ||
":analysis_py", | ||
":drawing_py", | ||
":framework_py", | ||
":primitives_py", | ||
], | ||
) | ||
|
||
add_lint_tests() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
from __future__ import absolute_import, print_function | ||
|
||
# TODO(eric.cousineau): Should we attempt to modularize this dependency graph? | ||
# How about separating this setup (development) from a more pure approach | ||
# (deployment)? | ||
|
||
try: | ||
from .analysis import * | ||
except ImportError as e: | ||
print(e) | ||
pass | ||
|
||
try: | ||
from .framework import * | ||
except ImportError as e: | ||
print(e) | ||
pass | ||
|
||
try: | ||
from .primitives import * | ||
except ImportError as e: | ||
print(e) | ||
pass |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
#include <pybind11/pybind11.h> | ||
|
||
#include "drake/systems/analysis/simulator.h" | ||
|
||
namespace py = pybind11; | ||
|
||
using std::unique_ptr; | ||
|
||
PYBIND11_MODULE(analysis, m) { | ||
// NOLINTNEXTLINE(build/namespaces): Emulate placement in namespace. | ||
using namespace drake::systems; | ||
|
||
auto py_iref = py::return_value_policy::reference_internal; | ||
|
||
m.doc() = "Bindings for the analysis portion of the Systems framework."; | ||
|
||
using T = double; | ||
|
||
py::class_<Simulator<T>>(m, "Simulator") | ||
.def(py::init<const System<T>&>()) | ||
.def(py::init<const System<T>&, unique_ptr<Context<T>>>()) | ||
.def("Initialize", &Simulator<T>::Initialize) | ||
.def("StepTo", &Simulator<T>::StepTo) | ||
.def("get_mutable_context", &Simulator<T>::get_mutable_context, py_iref); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
# @file | ||
# Provides general visualization utilities. This is NOT related to `rendering`. | ||
# @note This is an optional module, dependent on `pydot` and `matplotlib` being | ||
# installed. | ||
|
||
from StringIO import StringIO | ||
|
||
import matplotlib.image as mpimg | ||
import matplotlib.pyplot as plt | ||
import pydot | ||
|
||
|
||
# TODO(eric.cousineau): Move `plot_graphviz` to something more accessible to | ||
# `call_python_client`. | ||
|
||
|
||
def plot_graphviz(dot_text): | ||
"""Renders a DOT graph in matplotlib.""" | ||
# @ref https://stackoverflow.com/a/18522941/7829525 | ||
# Tried (reason ignored): pydotplus (`pydot` works), networkx | ||
# (`read_dot` does not work robustly?), pygraphviz (coupled with | ||
# `networkx`). | ||
g = pydot.graph_from_dot_data(dot_text) | ||
s = StringIO() | ||
g.write_png(s) | ||
s.seek(0) | ||
plt.axis('off') | ||
return plt.imshow(plt.imread(s), aspect="equal") | ||
|
||
|
||
def plot_system_graphviz(system): | ||
"""Renders a System's Graphviz representation in `matplotlib`. """ | ||
return plot_graphviz(system.GetGraphvizString()) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
#include <pybind11/eigen.h> | ||
#include <pybind11/eval.h> | ||
#include <pybind11/pybind11.h> | ||
#include <pybind11/stl.h> | ||
|
||
#include "drake/common/nice_type_name.h" | ||
#include "drake/systems/framework/abstract_values.h" | ||
#include "drake/systems/framework/basic_vector.h" | ||
#include "drake/systems/framework/context.h" | ||
#include "drake/systems/framework/diagram.h" | ||
#include "drake/systems/framework/diagram_builder.h" | ||
#include "drake/systems/framework/leaf_context.h" | ||
#include "drake/systems/framework/leaf_system.h" | ||
#include "drake/systems/framework/output_port_value.h" | ||
#include "drake/systems/framework/subvector.h" | ||
#include "drake/systems/framework/supervector.h" | ||
#include "drake/systems/framework/system.h" | ||
|
||
namespace py = pybind11; | ||
|
||
using std::make_unique; | ||
using std::unique_ptr; | ||
using std::vector; | ||
|
||
PYBIND11_MODULE(framework, m) { | ||
// NOLINTNEXTLINE(build/namespaces): Emulate placement in namespace. | ||
using namespace drake; | ||
// NOLINTNEXTLINE(build/namespaces): Emulate placement in namespace. | ||
using namespace drake::systems; | ||
|
||
// Aliases for commonly used return value policies. | ||
// `py_ref` is used when `keep_alive` is explicitly used (e.g. for extraction | ||
// methods, like `GetMutableSubsystemState`). | ||
auto py_ref = py::return_value_policy::reference; | ||
// `py_iref` is used when pointers / lvalue references are returned (no need | ||
// for `keep_alive`, as it is implicit. | ||
auto py_iref = py::return_value_policy::reference_internal; | ||
|
||
m.doc() = "Bindings for the core Systems framework."; | ||
|
||
// TODO(eric.cousineau): At present, we only bind doubles. | ||
// In the future, we will bind more scalar types, and enable scalar | ||
// conversion. | ||
using T = double; | ||
|
||
// TODO(eric.cousineau): Resolve `str_py` workaround. | ||
auto str_py = py::eval("str"); | ||
|
||
// TODO(eric.cousineau): Show constructor, but somehow make sure `pybind11` | ||
// knows this is abstract? | ||
py::class_<System<T>>(m, "System") | ||
.def("set_name", &System<T>::set_name) | ||
.def("get_input_port", &System<T>::get_input_port, py_iref) | ||
.def("get_output_port", &System<T>::get_output_port, py_iref) | ||
.def("CreateDefaultContext", &System<T>::CreateDefaultContext) | ||
.def("AllocateOutput", &System<T>::AllocateOutput) | ||
.def( | ||
"GetGraphvizString", | ||
[str_py](const System<T>* self) { | ||
// @note This is a workaround; for some reason, | ||
// casting this using `py::str` does not work, but directly | ||
// calling the Python function (`str_py`) does. | ||
return str_py(self->GetGraphvizString()); | ||
}); | ||
|
||
py::class_<LeafSystem<T>, System<T>>(m, "LeafSystem"); | ||
|
||
py::class_<Context<T>>(m, "Context") | ||
.def("FixInputPort", | ||
py::overload_cast<int, unique_ptr<BasicVector<T>>>( | ||
&Context<T>::FixInputPort)) | ||
.def("get_time", &Context<T>::get_time) | ||
.def("Clone", &Context<T>::Clone) | ||
.def("__copy__", &Context<T>::Clone) | ||
.def("get_state", &Context<T>::get_state, py_iref) | ||
.def("get_mutable_state", &Context<T>::get_mutable_state, py_iref); | ||
|
||
py::class_<LeafContext<T>, Context<T>>(m, "LeafContext"); | ||
|
||
py::class_<Diagram<T>, System<T>>(m, "Diagram") | ||
.def("GetMutableSubsystemState", | ||
[](Diagram<T>* self, const System<T>& arg1, Context<T>* arg2) | ||
-> auto&& { | ||
// @note Use `auto&&` to get perfect forwarding. | ||
// @note Compiler does not like `py::overload_cast` with this setup? | ||
return self->GetMutableSubsystemState(arg1, arg2); | ||
}, py_ref, py::keep_alive<0, 3>()); | ||
|
||
// Glue mechanisms. | ||
py::class_<DiagramBuilder<T>>(m, "DiagramBuilder") | ||
.def(py::init<>()) | ||
.def( | ||
"AddSystem", | ||
[](DiagramBuilder<T>* self, unique_ptr<System<T>> arg1) { | ||
return self->AddSystem(std::move(arg1)); | ||
}) | ||
.def("Connect", | ||
py::overload_cast<const OutputPort<T>&, const InputPortDescriptor<T>&>( | ||
&DiagramBuilder<T>::Connect)) | ||
.def("ExportInput", &DiagramBuilder<T>::ExportInput) | ||
.def("ExportOutput", &DiagramBuilder<T>::ExportOutput) | ||
.def("Build", &DiagramBuilder<T>::Build) | ||
.def("BuildInto", &DiagramBuilder<T>::BuildInto); | ||
|
||
py::class_<OutputPort<T>>(m, "OutputPort"); | ||
py::class_<SystemOutput<T>>(m, "SystemOutput"); | ||
|
||
py::class_<InputPortDescriptor<T>>(m, "InputPortDescriptor"); | ||
|
||
// Value types. | ||
py::class_<VectorBase<T>>(m, "VectorBase") | ||
.def("CopyToVector", &VectorBase<T>::CopyToVector) | ||
.def("SetFromVector", &VectorBase<T>::SetFromVector); | ||
|
||
py::class_<BasicVector<T>, VectorBase<T>>(m, "BasicVector") | ||
.def(py::init<VectorX<T>>()) | ||
.def("get_value", &BasicVector<T>::get_value); | ||
|
||
py::class_<Supervector<T>, VectorBase<T>>(m, "Supervector"); | ||
|
||
py::class_<Subvector<T>, VectorBase<T>>(m, "Subvector"); | ||
|
||
// TODO(eric.cousineau): Interfacing with the C++ abstract value types may be | ||
// a tad challenging. This should be more straightforward once | ||
// scalar-type conversion is supported, as the template-exposure mechanisms | ||
// should be relatively similar. | ||
py::class_<AbstractValue>(m, "AbstractValue"); | ||
|
||
// Parameters. | ||
// TODO(eric.cousineau): Fill this out. | ||
py::class_<Parameters<T>>(m, "Parameters"); | ||
|
||
// State. | ||
py::class_<State<T>>(m, "State") | ||
.def(py::init<>()) | ||
.def("get_continuous_state", | ||
&State<T>::get_continuous_state, py_iref) | ||
.def("get_mutable_continuous_state", | ||
&State<T>::get_mutable_continuous_state, py_iref); | ||
|
||
// - Constituents. | ||
py::class_<ContinuousState<T>>(m, "ContinuousState") | ||
.def(py::init<>()) | ||
.def("get_vector", &ContinuousState<T>::get_vector, py_iref) | ||
.def("get_mutable_vector", | ||
&ContinuousState<T>::get_mutable_vector, py_iref); | ||
|
||
py::class_<DiscreteValues<T>>(m, "DiscreteValues"); | ||
|
||
py::class_<AbstractValues>(m, "AbstractValues"); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
#include <pybind11/eigen.h> | ||
#include <pybind11/pybind11.h> | ||
|
||
#include "drake/systems/primitives/adder.h" | ||
#include "drake/systems/primitives/constant_value_source.h" | ||
#include "drake/systems/primitives/constant_vector_source.h" | ||
#include "drake/systems/primitives/integrator.h" | ||
|
||
namespace py = pybind11; | ||
|
||
PYBIND11_MODULE(primitives, m) { | ||
// NOLINTNEXTLINE(build/namespaces): Emulate placement in namespace. | ||
using namespace drake; | ||
// NOLINTNEXTLINE(build/namespaces): Emulate placement in namespace. | ||
using namespace drake::systems; | ||
|
||
m.doc() = "Bindings for the primitives portion of the Systems framework."; | ||
|
||
using T = double; | ||
|
||
py::class_<ConstantVectorSource<T>, LeafSystem<T>>(m, "ConstantVectorSource") | ||
.def(py::init<VectorX<T>>()); | ||
|
||
py::class_<ConstantValueSource<T>, LeafSystem<T>>(m, "ConstantValueSource"); | ||
|
||
py::class_<Adder<T>, LeafSystem<T>>(m, "Adder") | ||
.def(py::init<int, int>()); | ||
|
||
py::class_<Integrator<T>, LeafSystem<T>>(m, "Integrator") | ||
.def(py::init<int>()); | ||
} |
Oops, something went wrong.