Передача функции Python в контейнере объекту C++ и оберткам pybindPython

Программы на Python
Ответить
Anonymous
 Передача функции Python в контейнере объекту C++ и оберткам pybind

Сообщение Anonymous »

Я пишу набор оболочек Python через pybind11 для библиотеки оптимизации, сложный код которой был написан на C++.
Иерархия абстрактных классов моего кода C++, который в настоящее время нужно обернуть выглядит примерно так (multivariate.h):

Код: Выделить всё

typedef std::function multivariate;

struct multivariate_problem {

// objective
multivariate _f;
int _n;

// bound constraints
double *_lower;
double *_upper;

multivariate_problem(const multivariate f, const int n, double *lower,
double *upper):
_f(f), _n(n), _lower(lower), _upper(upper)) {
}
};

struct multivariate_solution {
...  // some code here for return a solution (seems to work ok)
};

class MultivariateOptimizer {

public:
virtual ~MultivariateOptimizer() {
}

// initialize the optimizer
virtual void init(const multivariate_problem &f, const double *guess) = 0;

// perform one step of iteration
virtual void iterate() = 0;

// retrieve current solution
virtual multivariate_solution solution() = 0;

// this essentially calls init(), performs a number of iterate() calls, returns solution()
virtual multivariate_solution optimize(const multivariate_problem &f,
const double *guess) = 0;
};

Теперь мой код pybind сейчас выглядит примерно так (multivariate_py.cpp):

Код: Выделить всё

// wrap the function expressions
typedef std::function py_multivariate;

// wrap the multivariable optimizer
void build_multivariate(py::module_ &m) {

// wrap the solution object
py::class_ solution(m, "MultivariateSolution");
...

// wrap the solver
py::class_ solver(m, "MultivariateSearch");

// corresponds to MultivariateSearch::optimize()
solver.def("optimize",
[](MultivariateOptimizer &self, py_multivariate py_f,
py::array_t &py_lower,
py::array_t &py_upper,
py::array_t &py_guess) {
const int n = py_lower.size();
double *lower = static_cast(py_lower.request().ptr);
double *upper = static_cast(py_upper.request().ptr);

const multivariate &f = [&py_f, &n](const double *x) -> double {
const auto &py_x = py::array_t(n, x);
return py_f(py_x);
};

const multivariate_problem &prob { f, n, lower, upper };
double *guess = static_cast(py_guess.request().ptr);
return self.optimize(prob, guess);
}, "f"_a, "lower"_a, "upper"_a, "guess"_a,
py::call_guard());

// corresponds to MultivariateSearch::init()
solver.def("initialize",
[](MultivariateOptimizer &self, py_multivariate py_f,
py::array_t &py_lower,
py::array_t &py_upper,
py::array_t &py_guess) {
const int n = py_lower.size();
double *lower = static_cast(py_lower.request().ptr);
double *upper = static_cast(py_upper.request().ptr);

const multivariate &f = [&py_f, &n](const double *x) ->  double {
const auto &py_x = py::array_t(n, x);
return py_f(py_x);
};

const multivariate_problem &prob { f, n, lower, upper };
double *guess = static_cast(py_guess.request().ptr);
return self.init(prob, guess);
}, "f"_a, "lower"_a, "upper"_a, "guess"_a,
py::call_guard());

// corresponds to MultivariateSearch::iterate()
solver.def("iterate", &MultivariateOptimizer::iterate);

// corresponds to MultivariateSearch::solution()
solver.def("solution", &MultivariateOptimizer::solution);

// put algorithm-specific bindings here
build_acd(m);
build_amalgam(m);
build_basin_hopping(m);
...
Как видите, я внутренне оборачиваю функцию Python в функцию C++, а затем заключаю функцию в объект multivariate_problem для передачи в мой сервер C++.
Который затем будет вызываться pybind со следующей точкой входа (mylibrary.cpp):

Код: Выделить всё

namespace py = pybind11;

void build_multivariate(py::module_&);

PYBIND11_MODULE(mypackagename, m) {
build_multivariate(m);
...
}

pybind11 скомпилирует это без ошибок через setup.py с помощью обычных команд pip install .. Фактически, большая часть кода работает правильно как для вызовов инициализации(), так и для оптимизации(). Например, следующий код Python будет работать нормально (при условии, что mylibrary заменен именем моего пакета в setup.py, а MySolverName является конкретным экземпляром MultivariateSearch:

Код: Выделить всё

import numpy as np
from mylibrary import MySolverName

# function to optimize
def fx(x):
total = 0.0
for x1, x2 in zip(x[:-1], x[1:]):
total += 100 * (x2 - x1 ** 2) ** 2 + (1 - x1) ** 2
return total

n = 10  # dimension of problem
alg = MySolverName(some_args_to_pass...)
sol = alg.optimize(fx,
lower=-10 * np.ones(n),
upper=10 * np.ones(n),
guess=np.random.uniform(low=-10., high=10., size=n))
print(sol)
Однако именно здесь я сейчас застрял. Я также хотел бы, чтобы у пользователя была возможность запускать решатели в интерактивном режиме, и именно здесь в игру вступают функции инициализации и итерации. Однако приведенная выше привязка, которая работает для оптимизации, не работает для итерации.
Чтобы проиллюстрировать вариант использования, следующий код не запускается:

Код: Выделить всё

import numpy as np
from mylibrary import MySolverName

# function to optimize
def fx(x):
... return result here for np.ndarray x

n = 10  # dimension of problem
alg = MySolverName(some_args_to_pass...)
alg.initialize(f=fx,
lower=-10 * np.ones(n),
upper=10 * np.ones(n),
guess=np.random.uniform(low=-10., high=10., size=n))
alg.iterate()
print(alg.solution())
При выполнении команды iterate сценарий зависает, а затем через некоторое время завершается, обычно без ошибок, и не печатает последнюю строку. В редких случаях на стороне Python происходит сбой, что «размерность не может быть отрицательной», но нет указаний, к какой памяти это относится. Без строки iterate() код успешно напечатает последнюю строку, как и ожидалось.
Когда я использую std::cout внутри C++ iterate()< /code>, она всегда зависает в точке прямо перед любыми вызовами функции fx и ничего не печатает после вызова, поэтому я сузил проблему до функции Python, которая не сохраняется правильно с помощью iterate() в качестве точки входа. Тем не менее, оптимизация() вызывает iterate() внутри кода C++, и предыдущий пример работает успешно, поэтому ошибка довольно странная.
Я Я пытался просмотреть документацию по pybind, но не смог найти пример, который бы достиг именно того, что я пытаюсь сделать выше. Я пробовал другие решения, такие как Keep_alive, но безрезультатно.
Можете ли вы помочь мне изменить оболочки pybind, чтобы сохранялась внутренне сохраненная функция Python? внутри объекта C++ и корректно выполняться даже после повторного восстановления управления интерпретатором Python между вызовами iterate()? Спасибо за помощь!

Подробнее здесь: https://stackoverflow.com/questions/791 ... d-wrappers
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «Python»