pybind11 equivalent of boost::python::extract? - boost-python

I am considering port of a complex code from boost::python to pybind11, but I am puzzled by the absence of something like boost::python::extract<...>().check(). I read pybind11::cast<T> can be used to extract c++ object from a py::object, but the only way to check if the cast is possible is by calling it and catching the exception when the cast fails. Is there something I am overlooking?

isinstance will do the job (doc) :
namespace py = pybind11;
py::object obj = ...
if (py::isinstance<py::array_t<double>>(obj))
{
....
}
else if (py::isinstance<py::str>(obj))
{
std::string val = obj.cast<std::string>();
std::cout << val << std::endl;
}
else if (py::isinstance<py::list>(obj))
{
...
}

Related

How can I copy the parameters of one model to another in LibTorch?

How can I copy the parameters of one model to another in LibTorch? I know how to do it in Torch (Python).
net2.load_state_dict(net.state_dict())
I have tried with the code below in C++ with quite a bit of work. It didn't copy one to another.
I don't see an option to copy the parameters of one like model into another like model.
#include <torch/torch.h>
using namespace torch::indexing;
torch::Device device(torch::kCUDA);
void loadstatedict(torch::nn::Module& model, torch::nn::Module& target_model) {
torch::autograd::GradMode::set_enabled(false); // make parameters copying possible
auto new_params = target_model.named_parameters(); // implement this
auto params = model.named_parameters(true /*recurse*/);
auto buffers = model.named_buffers(true /*recurse*/);
for (auto& val : new_params) {
auto name = val.key();
auto* t = params.find(name);
if (t != nullptr) {
t->copy_(val.value());
} else {
t = buffers.find(name);
if (t != nullptr) {
t->copy_(val.value());
}
}
}
}
struct Critic_Net : torch::nn::Module {
torch::Tensor next_state_batch__sampled_action;
public:
Critic_Net() {
lin1 = torch::nn::Linear(3, 3);
lin2 = torch::nn::Linear(3, 1);
lin1->to(device);
lin2->to(device);
}
torch::Tensor forward(torch::Tensor next_state_batch__sampled_action) {
auto h = next_state_batch__sampled_action;
h = torch::relu(lin1->forward(h));
h = lin2->forward(h);
return h;
}
torch::nn::Linear lin1{nullptr}, lin2{nullptr};
};
auto net = Critic_Net();
auto net2 = Critic_Net();
auto the_ones = torch::ones({3, 3}).to(device);
int main() {
std::cout << net.forward(the_ones);
std::cout << net2.forward(the_ones);
loadstatedict(net, net2);
std::cout << net.forward(the_ones);
std::cout << net2.forward(the_ones);
}
Your solution with load_state_dict should work if I understand correctly. The problem here is the same as in your previous question : nothing is registered as either parameters or buffers or submodules. Add the register_module calls and it should work fine.
Link to the question
How this class should look like :
struct Critic_Net : torch::nn::Module {
public:
Critic_Net() {
lin1 = register_module("lin1", torch::nn::Linear(427, 42));
lin2 = register_module("lin1", torch::nn::Linear(42, 286));
lin3 = register_module("lin1", torch::nn::Linear(286, 1));
}
torch::Tensor forward(torch::Tensor next_state_batch__sampled_action) {
// unchanged
}
torch::nn::Linear lin1{nullptr}, lin2{nullptr}, lin3{nullptr};
};

Fully-transparent exposure of Eigen::Vector/Matrix types using pybind11

I have a simple class definition:
class State {
private:
Eigen::Vector3f m_data;
public:
State(const Eigen::Vector3f& state) : m_data(state) { }
Eigen::Vector3f get() const { return m_data; }
void set(const Eigen::Vector3f& _state) { m_data = _state; }
std::string repr() const {
return "state data: [x=" + std::to_string(m_data[0]) + ", y=" + std::to_string(m_data[1]) + ", theta=" + std::to_string(m_data[2]) + "]";
}
};
I then expose the above in python with pybind11:
namespace py = pybind11;
PYBIND11_MODULE(bound_state, m) {
m.doc() = "python bindings for State";
py::class_<State>(m, "State")
.def(py::init<Eigen::Vector3f>())
.def("get", &_State::get)
.def("set", &_State::set)
.def("__repr__", &_State::repr);
}
And everything works fine; I am able to import this module into python and construct a State instance with a numpy array. This isn't exactly what I want though. I want to be able to access this object as if it were a numpy array; I want to be able to do something like the following in python:
import bound_state as bs
arr = np.array([1, 2, 3])
a = bs.State(arr)
print(a[0])
(the above throws a TypeError: 'bound_state.State' object does not support indexing)
In the past, I've used boost::python to expose lists by using add_property and this allowed indexing of the underlying data in C++. does pybind11 have something similar that can work with Eigen? Could someone provide an example showing how to expose a State instance that is indexable?
Per the API Docs, this can be done easily with the def_property method.
Turn this bit:
namespace py = pybind11;
PYBIND11_MODULE(bound_state, m) {
m.doc() = "python bindings for State";
py::class_<State>(m, "State")
.def(py::init<Eigen::Vector3f>())
.def("get", &State::get)
.def("set", &State::set)
.def("__repr__", &State::repr);
}
Into this:
namespace py = pybind11;
PYBIND11_MODULE(bound_state, m) {
m.doc() = "python bindings for State";
py::class_<State>(m, "State")
.def(py::init<Eigen::Vector3f>())
.def_property("m_data", &State::get, &State::set)
.def("__repr__", &State::repr);
}
Now, from the python-side, I can do:
import bound_state as bs
arr = np.array([1, 2, 3])
a = bs.State(arr)
print(a.m_data[0])
This is not exactly what I want, but is a step in the right direction.

Why is the code failing in the Destructor?

I have gone through stackoverflow questions similar to "Why is the destructor called twice?". My question can be a similar one but with a small change. I am getting an error when running the following code:
struct Employee{
char *name;
char *tag;
Employee *employee;
Employee(){
name = NULL;
tag = NULL;
employee = NULL;
}
//copy constructor
Employee(const Employee &obj){
cout << "Copy constructor called" << endl;
name = (char*)malloc(sizeof(char)*strlen(obj.name));
strcpy(name, obj.name);
tag = (char*)malloc(sizeof(char)*strlen(obj.tag));
strcpy(tag, obj.tag);
employee = (struct Employee*)malloc(sizeof(obj.employee));
employee = obj.employee;
}
//overloaded assignment operator
void operator = (const Employee &obj){
cout << "Assignment operator called" << endl;
if (this == &obj){
return;
}
strcpy(name, obj.name);
strcpy(tag, obj.tag);
employee = obj.employee;
}
//destructor
~Employee(){
cout << "Destructor called" << endl;
if (name != NULL){
cout << "Freeing name" << endl;
free(name);
name = NULL;
}
if (tag != NULL){
cout << "Freeing tag" << endl;
free(tag);
tag = NULL;
}
if (employee != NULL){
cout << "Freeing employee" << endl;
free(employee);
employee = NULL;
}
}
};
Employee createNode(){
Employee emp;
emp.name = (char*)malloc(sizeof(char)* 25);
strcpy(emp.name, "Alan");
emp.tag = (char*)malloc(sizeof(char)* 25);
strcpy(emp.tag, "Engineer");
emp.employee = (struct Employee*)malloc(sizeof(struct Employee));//just created memory, no initialization
return emp;
}
Employee get(){
//Employee emp = createNode();
//return emp;
return createNode();
}
int main(){
Employee emp = get();
getchar();
return 0;
}
I debugged the code and found that error is raising when the destructor is called second time when the main function exits.
1) I want to know why the code fails to run?
2) Are there any memory leaks?
3) How can I fix the error properly deallocating memory?
Thanks in advance.
Update:
As per the three Rule I have also added a copy constructor and overloaded the assignment operator. But the error(Expression : _crtisvalidheappointer(puserdata)) is raising. After checking in the Google I could see that some where the Heap corruption is happening. When I comment the initialization of the Struct member employee in the createNode() then I could see the error raising when trying to free the employee in destructor. So I suspect the problem is with the employee struct member. Please help me with this.I am using Visual studio for debugging and running.
Your problem is a lack of copy construct and assignment operator in your class. As a result you are freeing the strings within the class multiple times.
Just tried your code and found out a few issues that causes a crash:
1) strlen returns length of string without null terminator character, but strcpy requires that additional byte, so your allocating should look like that:
name = (char*)malloc(strlen(obj.name)+1);
2) When you copy employee, you copy pointer, so you have memory leak and employee pointer as a dangling one.
Also malloc cannot work with constructors, therefore after
employee = (struct Employee*)malloc(sizeof(obj.employee));
employee has a garbage inside.

Flex and Lemon Parser

I'm trying to learn flex and lemon, in order to parse a (moderately) complex file format. So far, I have my grammar and lex file and I believe it is correctly parsing an example file. Right now, I want to pass the token text scanned with flex to lemon.
The flex YYSTYPE is defined as
#define YYSTYPE char*
The lemon token type is
%token_type {char *}
However, if I have a set of rules in lemon:
start ::= MATDEF IDENTIFIER(matName) LEFT_CURLY_BRACE(left) materialDefinitionBody(mBody) RIGHT_CURLY_BRACE(right) .
{
std::string r = std::string(matName) + std::string(left) + mBody + std::string(right);
std::cout << "result " << r << std::endl;
}
materialDefinitionBody(r) ::= techniqueList .
{
r = "a";
}
the output will be
result a
when it should be something like
mat1 { a }
My main parsing loop is:
void parse(const string& commandLine) {
// Set up the scanner
yyscan_t scanner;
yylex_init(&scanner);
YY_BUFFER_STATE bufferState = yy_scan_string(commandLine.c_str(), scanner);
// Set up the parser
void* shellParser = ParseAlloc(malloc);
yylval = new char[512];
int lexCode;
do {
yylval[0] = 0;
lexCode = yylex(scanner);
cout << lexCode << " : " << yylval << std::endl;
Parse(shellParser, lexCode, yylval);
}
while (lexCode > 0);
if (-1 == lexCode) {
cerr << "The scanner encountered an error.\n";
}
// Cleanup the scanner and parser
yy_delete_buffer(bufferState, scanner);
yylex_destroy(scanner);
ParseFree(shellParser, free);
}
The cout line is printing the correct lexCode/yylval combination.
What is the best way? I can't find anything that works.
You need to have
yylval = new char[512];
inside the do-while loop.

How to expose aligned class with boost.python

When trying to expose aligned class like this:
class __declspec(align(16)) foo
{
public:
void foo_method() {}
};
BOOST_PYTHON_MODULE(foo_module)
{
class_<foo>("foo")
.def("foo_method", &foo::foo_method);
}
You end up with error (msvs 2010):
error C2719: 'unnamed-parameter': formal parameter with __declspec(align('16')) won't be aligned,
see reference to class template instantiation 'boost::python::converter::as_to_python_function<T,ToPython>' being compiled
The solution I found so far, is to use smart pointer to store object:
BOOST_PYTHON_MODULE(foo_module)
{
class_<foo, boost::shared_ptr<foo>, boost::noncopyable>("foo")
.def("foo_method", &foo::foo_method);
}
Isn't there a better solution? This is quite annoying, because you should wrap all your functions returning objects by value to return smart pointers instead, and performance also degrades.
I had the same problem and wanted a solution that doesn't involve shared_ptr. It involves specializing some boost::python classes to make sure we get a storage area big enough to be able to align our object within it.
I have written a somewhat long blog post explaining how I arrived at this solution here. Below is the solution I found. I feel it is quite a hack, so maybe it will break other things. But so far it seems to work and I haven't found anything better.
I was trying to expose an Eigen::Quaternionf (which requires 16 bytes alignment) :
bp::class_<Quaternionf>("Quaternion", bp::init<float, float, float, float>())
.def(bp::init<Matrix3f>())
.add_property("w", get_prop_const(&Quaternionf::w))
.add_property("x", get_prop_const(&Quaternionf::x))
.add_property("y", get_prop_const(&Quaternionf::y))
.add_property("z", get_prop_const(&Quaternionf::z))
.def("matrix", &Quaternionf::matrix)
.def("rotvec", &quaternion_to_rotvec);
The solution involves specializing 3 classes :
boost::python::objects::instance to request 16 bytes more than what our type requires to ensure we can align
...
union
{
align_t align;
char bytes[sizeof(Data) + 16];
} storage;
...
boost::python::objects::make_instance_impl to correctly set the Py_SIZE of our instance
...
Holder* holder = Derived::construct(
&instance->storage, (PyObject*)instance, x);
holder->install(raw_result);
// Note the position of the internally-stored Holder,
// for the sake of destruction
// Since the holder not necessarily allocated at the start of
// storage (to respect alignment), we have to add the holder
// offset relative to storage
size_t holder_offset = reinterpret_cast<size_t>(holder)
- reinterpret_cast<size_t>(&instance->storage)
+ offsetof(instance_t, storage);
Py_SIZE(instance) = holder_offset;
...
boost::python::objects::make_instance so that the construct method will align the holder in the storage
...
static inline QuaternionfHolder* construct(void* storage, PyObject* instance, reference_wrapper<Quaternionf const> x)
{
// From the specialized make_instance_impl above, we are guaranteed to
// be able to align our storage
void* aligned_storage = reinterpret_cast<void*>(
(reinterpret_cast<size_t>(storage) & ~(size_t(15))) + 16);
QuaternionfHolder* new_holder = new (aligned_storage)
QuaternionfHolder(instance, x);
return new_holder;
}
...
The full code is below :
typedef bp::objects::value_holder<Eigen::Quaternionf> QuaternionfHolder;
namespace boost { namespace python { namespace objects {
using namespace Eigen;
//template <class Data = char>
template<>
struct instance<QuaternionfHolder>
{
typedef QuaternionfHolder Data;
PyObject_VAR_HEAD
PyObject* dict;
PyObject* weakrefs;
instance_holder* objects;
typedef typename type_with_alignment<
::boost::alignment_of<Data>::value
>::type align_t;
union
{
align_t align;
char bytes[sizeof(Data) + 16];
} storage;
};
// Adapted from boost/python/object/make_instance.hpp
//template <class T, class Holder, class Derived>
template<class Derived>
struct make_instance_impl<Quaternionf, QuaternionfHolder, Derived>
{
typedef Quaternionf T;
typedef QuaternionfHolder Holder;
typedef objects::instance<Holder> instance_t;
template <class Arg>
static inline PyObject* execute(Arg& x)
{
BOOST_MPL_ASSERT((mpl::or_<is_class<T>, is_union<T> >));
PyTypeObject* type = Derived::get_class_object(x);
if (type == 0)
return python::detail::none();
PyObject* raw_result = type->tp_alloc(
type, objects::additional_instance_size<Holder>::value);
if (raw_result != 0)
{
python::detail::decref_guard protect(raw_result);
instance_t* instance = (instance_t*)raw_result;
// construct the new C++ object and install the pointer
// in the Python object.
//Derived::construct(&instance->storage, (PyObject*)instance, x)->install(raw_result);
Holder* holder = Derived::construct(
&instance->storage, (PyObject*)instance, x);
holder->install(raw_result);
// Note the position of the internally-stored Holder,
// for the sake of destruction
// Since the holder not necessarily allocated at the start of
// storage (to respect alignment), we have to add the holder
// offset relative to storage
size_t holder_offset = reinterpret_cast<size_t>(holder)
- reinterpret_cast<size_t>(&instance->storage)
+ offsetof(instance_t, storage);
Py_SIZE(instance) = holder_offset;
// Release ownership of the python object
protect.cancel();
}
return raw_result;
}
};
//template <class T, class Holder>
template<>
struct make_instance<Quaternionf, QuaternionfHolder>
: make_instance_impl<Quaternionf, QuaternionfHolder, make_instance<Quaternionf,QuaternionfHolder> >
{
template <class U>
static inline PyTypeObject* get_class_object(U&)
{
return converter::registered<Quaternionf>::converters.get_class_object();
}
static inline QuaternionfHolder* construct(void* storage, PyObject* instance, reference_wrapper<Quaternionf const> x)
{
LOG(INFO) << "Into make_instance";
LOG(INFO) << "storage : " << storage;
LOG(INFO) << "&x : " << x.get_pointer();
LOG(INFO) << "&x alignment (0 = aligned): " << (reinterpret_cast<size_t>(x.get_pointer()) & 0xf);
// From the specialized make_instance_impl above, we are guaranteed to
// be able to align our storage
void* aligned_storage = reinterpret_cast<void*>(
(reinterpret_cast<size_t>(storage) & ~(size_t(15))) + 16);
QuaternionfHolder* new_holder = new (aligned_storage) QuaternionfHolder(instance, x);
LOG(INFO) << "&new_holder : " << new_holder;
return new_holder;
//return new (storage) QuaternionfHolder(instance, x);
}
};
}}} // namespace boost::python::objects