From 62de01657a8ebddc4cff2d4b29dada30e5ea51cb Mon Sep 17 00:00:00 2001 From: Gabriele Roeger Date: Mon, 6 Feb 2023 21:31:44 +0100 Subject: [PATCH 1/5] [pybind11-playground] add what we tried at the retreat --- CMakeLists.txt | 40 ++++++++++++++++ python/test.cc | 47 +++++++++++++++++++ src/search/heuristics/ff_heuristic.cc | 17 +++++++ src/search/heuristics/ff_heuristic.h | 1 + src/search/search_engine.cc | 22 +++++++++ src/search/search_engine.h | 1 + .../enforced_hill_climbing_search.cc | 33 +++++++++++++ .../enforced_hill_climbing_search.h | 3 ++ src/search/tasks/root_task.cc | 5 ++ src/search/tasks/root_task.h | 2 + try-pybindings/test.py | 10 ++++ 11 files changed, 181 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 python/test.cc create mode 100644 try-pybindings/test.py diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000000..fa6732e6cd --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,40 @@ +cmake_minimum_required(VERSION 3.1) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release) +endif() + +set(CMAKE_CXX_FLAGS "-O3") +set(CMAKE_CXX_FLAGS_RELEASE "-O3") + +project(pydownward) + +include_directories("${CMAKE_SOURCE_DIR}/src/search/") +include_directories("${CMAKE_SOURCE_DIR}/src/search/ext/") +include_directories("${CMAKE_SOURCE_DIR}/python") + + +file (GLOB SOURCE_FILES "src/search/*.cc" "src/search/*/*.cc") +file (GLOB HEADER_FILES "src/search/*.h*" "src/search/*/*.h*") +file (GLOB PYTHON_FILES "python/*.cc" "python/*.h") + +# Set up such that XCode organizes the files +#source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${SOURCE_FILES} ${HEADER_FILES} ${PYTHON_FILES} ) + +find_package(pybind11 REQUIRED) +pybind11_add_module(pydownward + ${SOURCE_FILES} + ${HEADER_FILES} + ${PYTHON_FILES} +) + +target_link_libraries(pydownward PUBLIC) + +install(TARGETS pydownward + COMPONENT python + LIBRARY DESTINATION "${PYTHON_LIBRARY_DIR}" + ) diff --git a/python/test.cc b/python/test.cc new file mode 100644 index 0000000000..247849a0e7 --- /dev/null +++ b/python/test.cc @@ -0,0 +1,47 @@ +#include +#include +#include + +#include "command_line.h" +#include "search_engine.h" + +#include "search_engines/enforced_hill_climbing_search.h" +#include "heuristics/ff_heuristic.h" +#include "tasks/root_task.h" +#include "task_utils/task_properties.h" +#include "utils/logging.h" +#include "utils/system.h" +#include "utils/timer.h" + +namespace py = pybind11; + +//py::object test(const std::string &sas_file, const std::string &cmd_line) { +void read_task(const std::string &sas_file) { + std::ifstream task_file(sas_file); + tasks::read_root_task(task_file); +} + +PYBIND11_MODULE(pydownward, m) { + m.doc() = "Gabi's pybind11 example plugin"; // Optional module docstring + // + m.def("read_task", &read_task, "Read the task from sas_file", py::arg("sas_file")="output.sas"); + + m.def("get_root_task", &tasks::get_root_task, "Get the root task"); + + py::class_>(m, "AbstractTask") + .def("get_operator_name", &AbstractTask::get_operator_name); + + py::class_>(m, "Evaluator"); + + py::class_(m, "OperatorID") + .def("get_index", &OperatorID::get_index); + + py::class_, Evaluator>(m, "FFHeuristic") + .def(py::init>()); + + py::class_>(m, "EHCSearch") + .def(py::init>()) + .def("search", &enforced_hill_climbing_search::EnforcedHillClimbingSearch::search) + .def("found_solution", &enforced_hill_climbing_search::EnforcedHillClimbingSearch::found_solution) + .def("get_plan", &enforced_hill_climbing_search::EnforcedHillClimbingSearch::get_plan); +} diff --git a/src/search/heuristics/ff_heuristic.cc b/src/search/heuristics/ff_heuristic.cc index c782fee62f..015613df23 100644 --- a/src/search/heuristics/ff_heuristic.cc +++ b/src/search/heuristics/ff_heuristic.cc @@ -19,6 +19,23 @@ FFHeuristic::FFHeuristic(const plugins::Options &opts) } } +static plugins::Options mimic_options(shared_ptr task) { + plugins::Options opts; + opts.set("cache_estimates", true); + opts.set>("transform", task); + opts.set("verbosity", utils::Verbosity::NORMAL); + opts.set_unparsed_config("Manual FF"); + return opts; +} + +FFHeuristic::FFHeuristic(shared_ptr task) + : AdditiveHeuristic(mimic_options(task)), + relaxed_plan(task_proxy.get_operators().size(), false) { + if (log.is_at_least_normal()) { + log << "Initializing FF heuristic..." << endl; + } +} + void FFHeuristic::mark_preferred_operators_and_relaxed_plan( const State &state, PropID goal_id) { Proposition *goal = get_proposition(goal_id); diff --git a/src/search/heuristics/ff_heuristic.h b/src/search/heuristics/ff_heuristic.h index 5a7cad2ecf..6605237ddd 100644 --- a/src/search/heuristics/ff_heuristic.h +++ b/src/search/heuristics/ff_heuristic.h @@ -33,6 +33,7 @@ class FFHeuristic : public additive_heuristic::AdditiveHeuristic { virtual int compute_heuristic(const State &ancestor_state) override; public: explicit FFHeuristic(const plugins::Options &opts); + explicit FFHeuristic(std::shared_ptr task); }; } diff --git a/src/search/search_engine.cc b/src/search/search_engine.cc index 8a7b42cd93..1f1ae3d29d 100644 --- a/src/search/search_engine.cc +++ b/src/search/search_engine.cc @@ -40,6 +40,28 @@ successor_generator::SuccessorGenerator &get_successor_generator( return successor_generator; } +SearchEngine::SearchEngine(const std::string &description, OperatorCost cost_type, double max_time, int cost_bound) + : description(description), + status(IN_PROGRESS), + solution_found(false), + task(tasks::g_root_task), + task_proxy(*task), + log(utils::g_log), // TODO the options version can set the verbosity level + state_registry(task_proxy), + successor_generator(get_successor_generator(task_proxy, log)), + search_space(state_registry, log), + statistics(log), + cost_type(cost_type), + is_unit_cost(task_properties::is_unit_cost(task_proxy)), + max_time(max_time) { + if (cost_bound < 0) { + cerr << "error: negative cost bound " << cost_bound << endl; + utils::exit_with(ExitCode::SEARCH_INPUT_ERROR); + } + bound = cost_bound; + task_properties::print_variable_statistics(task_proxy); +} + SearchEngine::SearchEngine(const plugins::Options &opts) : description(opts.get_unparsed_config()), status(IN_PROGRESS), diff --git a/src/search/search_engine.h b/src/search/search_engine.h index 478fb39a36..08321f555c 100644 --- a/src/search/search_engine.h +++ b/src/search/search_engine.h @@ -61,6 +61,7 @@ class SearchEngine { int get_adjusted_cost(const OperatorProxy &op) const; public: SearchEngine(const plugins::Options &opts); + SearchEngine(const std::string &description, OperatorCost cost_type, double max_time, int cost_bound); virtual ~SearchEngine(); virtual void print_statistics() const = 0; virtual void save_plan_if_necessary(); diff --git a/src/search/search_engines/enforced_hill_climbing_search.cc b/src/search/search_engines/enforced_hill_climbing_search.cc index 7ae27218f2..ecd031d7c6 100644 --- a/src/search/search_engines/enforced_hill_climbing_search.cc +++ b/src/search/search_engines/enforced_hill_climbing_search.cc @@ -92,6 +92,39 @@ EnforcedHillClimbingSearch::EnforcedHillClimbingSearch( opts, use_preferred, preferred_usage)->create_edge_open_list(); } + +// TODO we do not support preferred operators, selection of preferred usage +// TODO verbosity fixed to NORMAL +EnforcedHillClimbingSearch::EnforcedHillClimbingSearch(const std::string &description, + int cost_type, double max_time, int cost_bound, + shared_ptr h) + : SearchEngine(description, OperatorCost(cost_type), max_time, cost_bound), + evaluator(h), + preferred_operator_evaluators(vector>()), + preferred_usage(PreferredUsage::RANK_PREFERRED_FIRST), + current_eval_context(state_registry.get_initial_state(), &statistics), + current_phase_start_g(-1), + num_ehc_phases(0), + last_num_expanded(-1) { + for (const shared_ptr &eval : preferred_operator_evaluators) { + eval->get_path_dependent_evaluators(path_dependent_evaluators); + } + evaluator->get_path_dependent_evaluators(path_dependent_evaluators); + + State initial_state = state_registry.get_initial_state(); + for (Evaluator *evaluator : path_dependent_evaluators) { + evaluator->notify_initial_state(initial_state); + } + use_preferred = find(preferred_operator_evaluators.begin(), + preferred_operator_evaluators.end(), evaluator) != + preferred_operator_evaluators.end(); + + plugins::Options options; + options.set("verbosity", utils::Verbosity::NORMAL); + open_list = create_ehc_open_list_factory( + options, use_preferred, preferred_usage)->create_edge_open_list(); +} + EnforcedHillClimbingSearch::~EnforcedHillClimbingSearch() { } diff --git a/src/search/search_engines/enforced_hill_climbing_search.h b/src/search/search_engines/enforced_hill_climbing_search.h index 49c9bbfc2e..ec813b301c 100644 --- a/src/search/search_engines/enforced_hill_climbing_search.h +++ b/src/search/search_engines/enforced_hill_climbing_search.h @@ -61,6 +61,9 @@ class EnforcedHillClimbingSearch : public SearchEngine { public: explicit EnforcedHillClimbingSearch(const plugins::Options &opts); + EnforcedHillClimbingSearch(const std::string &description, + int cost_type, double max_time, int cost_bound, + std::shared_ptr h); virtual ~EnforcedHillClimbingSearch() override; virtual void print_statistics() const override; diff --git a/src/search/tasks/root_task.cc b/src/search/tasks/root_task.cc index 2ed2241122..0ebc9db7e5 100644 --- a/src/search/tasks/root_task.cc +++ b/src/search/tasks/root_task.cc @@ -497,6 +497,11 @@ void read_root_task(istream &in) { g_root_task = make_shared(in); } +shared_ptr get_root_task() { + return g_root_task; +} + + class RootTaskFeature : public plugins::TypedFeature { public: RootTaskFeature() : TypedFeature("no_transform") { diff --git a/src/search/tasks/root_task.h b/src/search/tasks/root_task.h index 15961d1e87..6e1e3efca8 100644 --- a/src/search/tasks/root_task.h +++ b/src/search/tasks/root_task.h @@ -6,5 +6,7 @@ namespace tasks { extern std::shared_ptr g_root_task; extern void read_root_task(std::istream &in); + +extern std::shared_ptr get_root_task(); } #endif diff --git a/try-pybindings/test.py b/try-pybindings/test.py new file mode 100644 index 0000000000..6b4931e6a6 --- /dev/null +++ b/try-pybindings/test.py @@ -0,0 +1,10 @@ +import pydownward +pydownward.read_task("output.sas") +task = pydownward.get_root_task() +ff = pydownward.FFHeuristic(task) +search = pydownward.EHCSearch("hallo", 0, 100.0, 100, ff) +search.search() +assert search.found_solution() +op_id_plan = search.get_plan() +plan= [task.get_operator_name(op_id.get_index(), False) + for op_id in op_id_plan] From 87e0ceb1c3204286caea6aadca4d3713d73cad8b Mon Sep 17 00:00:00 2001 From: Gabriele Roeger Date: Mon, 6 Feb 2023 21:33:06 +0100 Subject: [PATCH 2/5] [pybind11-playground] add README_PYBIND with setup instructions --- README_PYBIND.txt | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 README_PYBIND.txt diff --git a/README_PYBIND.txt b/README_PYBIND.txt new file mode 100644 index 0000000000..b703e25fc1 --- /dev/null +++ b/README_PYBIND.txt @@ -0,0 +1,20 @@ +To test the current pybind setup, proceed as follows: + +$ mkdir pybind-build +$ cd pybind-build +$ cmake .. -DPYTHON_LIBRARY_DIR="/try-pybindings" -DPYTHON_EXECUTABLE="/usr/bin/python3" + (or wherever your python3 lives) +$ make +$ make install +$ cd ../try-pybindings +$ ls +pydownward.cpython-310-x86_64-linux-gnu.so test.py +$ cat test.py + +Put some output.sas file into the directory. The test only runs enforced +hill-climbing, which has a high likelihood to NOT solve a task. It can for +example solve the first driverlog task, so if in doubt, use that one. + +Enjoy: +$ python -i +(The plan is stored in variable "plan", have a look!) From f9c5374dfda39512aa55b457a6ca91acf74c0c1c Mon Sep 17 00:00:00 2001 From: Florian Pommerening Date: Wed, 8 Feb 2023 23:11:49 +0100 Subject: [PATCH 3/5] Add option to write heuristics in Python. --- python/test.cc | 39 ++++++++++++++++++++++++++++++++++++--- src/search/heuristic.cc | 17 +++++++++++++++++ src/search/heuristic.h | 7 +++++-- try-pybindings/test.py | 23 +++++++++++++++++++---- 4 files changed, 77 insertions(+), 9 deletions(-) mode change 100644 => 100755 try-pybindings/test.py diff --git a/python/test.cc b/python/test.cc index 247849a0e7..bf2eb806ab 100644 --- a/python/test.cc +++ b/python/test.cc @@ -3,7 +3,9 @@ #include #include "command_line.h" +#include "heuristic.h" #include "search_engine.h" +#include "task_proxy.h" #include "search_engines/enforced_hill_climbing_search.h" #include "heuristics/ff_heuristic.h" @@ -21,6 +23,25 @@ void read_task(const std::string &sas_file) { tasks::read_root_task(task_file); } +class EvaluatorTrampoline : public Evaluator { +public: + using Evaluator::Evaluator; +}; + +class HeuristicTrampoline : public Heuristic { +public: + using Heuristic::Heuristic; + + virtual int compute_heuristic(const State &ancestor_state) override { + PYBIND11_OVERRIDE( + int, /* Return type */ + Heuristic, /* Parent class */ + compute_heuristic, /* Name of function in C++ (must match Python name) */ + ancestor_state /* Argument(s) */ + ); + } +}; + PYBIND11_MODULE(pydownward, m) { m.doc() = "Gabi's pybind11 example plugin"; // Optional module docstring // @@ -31,12 +52,24 @@ PYBIND11_MODULE(pydownward, m) { py::class_>(m, "AbstractTask") .def("get_operator_name", &AbstractTask::get_operator_name); - py::class_>(m, "Evaluator"); - py::class_(m, "OperatorID") .def("get_index", &OperatorID::get_index); - py::class_, Evaluator>(m, "FFHeuristic") + py::class_(m, "FactProxy") + .def("get_name", &FactProxy::get_name); + + py::class_(m, "State") + .def("__getitem__", [](State &self, unsigned index) + { return self[index]; }) + .def("__iter__", [](const State &s) { return py::make_iterator(begin(s), end(s)); }, py::keep_alive<0, 1>()); + + py::class_, EvaluatorTrampoline>(m, "Evaluator"); + + py::class_, Evaluator, HeuristicTrampoline>(m, "Heuristic") + .def(py::init>()) + .def("compute_heuristic", &Heuristic::compute_heuristic); + + py::class_, Heuristic>(m, "FFHeuristic") .def(py::init>()); py::class_>(m, "EHCSearch") diff --git a/src/search/heuristic.cc b/src/search/heuristic.cc index 9030deb64e..89a1e313e4 100644 --- a/src/search/heuristic.cc +++ b/src/search/heuristic.cc @@ -7,6 +7,7 @@ #include "task_utils/task_properties.h" #include "tasks/cost_adapted_task.h" #include "tasks/root_task.h" +#include "utils/logging.h" #include #include @@ -22,6 +23,22 @@ Heuristic::Heuristic(const plugins::Options &opts) task_proxy(*task) { } + +plugins::Options create_dummy_options_for_python_binding() { + plugins::Options opts; + opts.set("verbosity", utils::Verbosity::NORMAL); + opts.set_unparsed_config("Evaluator created from Python"); + return opts; +} + +Heuristic::Heuristic(shared_ptr task) + : Evaluator(create_dummy_options_for_python_binding(), true, true, true), + heuristic_cache(HEntry(NO_VALUE, true)), + cache_evaluator_values(true), + task(task), + task_proxy(*task) { +} + Heuristic::~Heuristic() { } diff --git a/src/search/heuristic.h b/src/search/heuristic.h index 12e0770851..581775d317 100644 --- a/src/search/heuristic.h +++ b/src/search/heuristic.h @@ -61,8 +61,6 @@ class Heuristic : public Evaluator { enum {DEAD_END = -1, NO_VALUE = -2}; - virtual int compute_heuristic(const State &ancestor_state) = 0; - /* Usage note: Marking the same operator as preferred multiple times is OK -- it will only appear once in the list of preferred @@ -74,8 +72,13 @@ class Heuristic : public Evaluator { public: explicit Heuristic(const plugins::Options &opts); + explicit Heuristic(std::shared_ptr task); virtual ~Heuristic() override; + virtual int compute_heuristic(const State &ancestor_state) { + return 0; + } + virtual void get_path_dependent_evaluators( std::set & /*evals*/) override { } diff --git a/try-pybindings/test.py b/try-pybindings/test.py old mode 100644 new mode 100755 index 6b4931e6a6..81c324c7ce --- a/try-pybindings/test.py +++ b/try-pybindings/test.py @@ -1,10 +1,25 @@ +#! /usr/bin/env python + import pydownward + +class MyHeuristic(pydownward.Heuristic): + def __init__(self, task): + super().__init__(task) + self.ff = pydownward.FFHeuristic(task) + + def compute_heuristic(self, state): + ff = self.ff.compute_heuristic(state) + print(f"Returning h = {ff} for state: {[f.get_name() for f in state]}") + return ff + pydownward.read_task("output.sas") task = pydownward.get_root_task() -ff = pydownward.FFHeuristic(task) -search = pydownward.EHCSearch("hallo", 0, 100.0, 100, ff) + +h = MyHeuristic(task) +search = pydownward.EHCSearch("hallo", 0, 100.0, 100, h) search.search() assert search.found_solution() op_id_plan = search.get_plan() -plan= [task.get_operator_name(op_id.get_index(), False) - for op_id in op_id_plan] +plan = [task.get_operator_name(op_id.get_index(), False) + for op_id in op_id_plan] +print(plan) \ No newline at end of file From de1b3d88c8fd2ecd214ef9e0f1c70c12c81359e7 Mon Sep 17 00:00:00 2001 From: Florian Pommerening Date: Thu, 9 Feb 2023 13:52:48 +0100 Subject: [PATCH 4/5] Update documentation and switch Heuristic::compute_heuristic back to pure virtual. --- README_PYBIND.txt | 48 +++++++++++++++++++++++++++++++++++++++--- python/test.cc | 2 +- src/search/heuristic.h | 4 +--- 3 files changed, 47 insertions(+), 7 deletions(-) diff --git a/README_PYBIND.txt b/README_PYBIND.txt index b703e25fc1..f155cdc06d 100644 --- a/README_PYBIND.txt +++ b/README_PYBIND.txt @@ -1,10 +1,13 @@ +To install pybind11, use + apt install python3-pybind11 + To test the current pybind setup, proceed as follows: $ mkdir pybind-build $ cd pybind-build $ cmake .. -DPYTHON_LIBRARY_DIR="/try-pybindings" -DPYTHON_EXECUTABLE="/usr/bin/python3" (or wherever your python3 lives) -$ make +$ make -j8 $ make install $ cd ../try-pybindings $ ls @@ -16,5 +19,44 @@ hill-climbing, which has a high likelihood to NOT solve a task. It can for example solve the first driverlog task, so if in doubt, use that one. Enjoy: -$ python -i -(The plan is stored in variable "plan", have a look!) +$ python -i test.py + + +Notes on the interface: + +* We hacked the code in some places + - We made Heuristic::compute_heuristic public. This might be necessary for the + trampoline classes but have not looked for alternatives. + - We added a constructor Heuristic(task) that fixes some options (see + create_dummy_options_for_python_binding in heuristic.cc). The long term plan + would be t not pass the options object into the constructor and instead pass + individual parameters that are then also all exposed through the interface. + - Similarly, we added temporary constructors for the FF heuristic, search + engines, and EHC search. + - We added a global function get_root_task to access the global task. We have + to think about how to expose the task. + +* The order of template parameters in py::class_<...> does not matter. Pybind + will figure out which parameter is which. We should agree on some default order. + For Heuristic, we use four parameters: + - Heuristic is the class to expose to Python + - std::shared_ptr is the holder that is internally used for + reference counting, we use shared pointers to make this compatible with our + other features. + - Evaluator is the base class + - HeuristicTrampoline is a class used to handle function calls of virtual + functions. If we inherit from Heuristic in Python, the trampoline class is + responsible for forwarding calls to the Python class. See + https://pybind11.readthedocs.io/en/stable/advanced/classes.html + +* Trampoline classes (EvaluatorTrampoline, HeuristicTrampoline) are needed to + derive classes in Python. They are needed for all classes in the hierarchy. For + example, if we wanted to allow users to inherit from FFHeuristic, it would also + need a trampoline class. They have to define all virtual methods that we want to + make available in Python on every level (FFHeuristicTrampoline would have to + repeat the definition of compute_heuristic). It is not clear if the trampoline + for Evaluator is needed as long as no virtual method from Evaluator is in the + interface. Probably yes, because the constructor counts. + +* py::make_iterator can be used to set up an iterable but py::keep_alive is + required to keep the iterator alive while it is used. diff --git a/python/test.cc b/python/test.cc index bf2eb806ab..1a75c5321b 100644 --- a/python/test.cc +++ b/python/test.cc @@ -33,7 +33,7 @@ class HeuristicTrampoline : public Heuristic { using Heuristic::Heuristic; virtual int compute_heuristic(const State &ancestor_state) override { - PYBIND11_OVERRIDE( + PYBIND11_OVERRIDE_PURE( int, /* Return type */ Heuristic, /* Parent class */ compute_heuristic, /* Name of function in C++ (must match Python name) */ diff --git a/src/search/heuristic.h b/src/search/heuristic.h index 581775d317..dd74dcf4c8 100644 --- a/src/search/heuristic.h +++ b/src/search/heuristic.h @@ -75,9 +75,7 @@ class Heuristic : public Evaluator { explicit Heuristic(std::shared_ptr task); virtual ~Heuristic() override; - virtual int compute_heuristic(const State &ancestor_state) { - return 0; - } + virtual int compute_heuristic(const State &ancestor_state) = 0; virtual void get_path_dependent_evaluators( std::set & /*evals*/) override { From ee6c63ee659e12001ec7f995317d99d8100a151e Mon Sep 17 00:00:00 2001 From: Florian Pommerening Date: Mon, 3 Jul 2023 19:29:08 +0200 Subject: [PATCH 5/5] try factoring out python definitions --- python/test.cc | 47 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 37 insertions(+), 10 deletions(-) diff --git a/python/test.cc b/python/test.cc index 1a75c5321b..5b62806849 100644 --- a/python/test.cc +++ b/python/test.cc @@ -1,6 +1,8 @@ -#include + #include #include +#include #include +#include #include "command_line.h" #include "heuristic.h" @@ -42,9 +44,38 @@ class HeuristicTrampoline : public Heuristic { } }; +void init_ff(py::module_ &m) { + py::options options; + options.disable_function_signatures(); + + py::class_, Heuristic>(m, "FFHeuristic") + .def(py::init>(), py::arg("task"), py::doc(R"delimiter( + FFHeuristic + bla bla bla synopsis + + Parameters + ---------- + :param AbstractTask task: optional task transformation for this heuristic +)delimiter")); +} + +void init_ehc(py::module_ &m) { + py::options options; + options.disable_function_signatures(); + + py::class_>(m, "EHCSearch") + .def(py::init>()) + .def("search", &enforced_hill_climbing_search::EnforcedHillClimbingSearch::search, py::doc("this has some effect")) + .def("found_solution", &enforced_hill_climbing_search::EnforcedHillClimbingSearch::found_solution) + .def("get_plan", &enforced_hill_climbing_search::EnforcedHillClimbingSearch::get_plan); +} + PYBIND11_MODULE(pydownward, m) { m.doc() = "Gabi's pybind11 example plugin"; // Optional module docstring - // + + py::options options; + options.disable_function_signatures(); + m.def("read_task", &read_task, "Read the task from sas_file", py::arg("sas_file")="output.sas"); m.def("get_root_task", &tasks::get_root_task, "Get the root task"); @@ -69,12 +100,8 @@ PYBIND11_MODULE(pydownward, m) { .def(py::init>()) .def("compute_heuristic", &Heuristic::compute_heuristic); - py::class_, Heuristic>(m, "FFHeuristic") - .def(py::init>()); - - py::class_>(m, "EHCSearch") - .def(py::init>()) - .def("search", &enforced_hill_climbing_search::EnforcedHillClimbingSearch::search) - .def("found_solution", &enforced_hill_climbing_search::EnforcedHillClimbingSearch::found_solution) - .def("get_plan", &enforced_hill_climbing_search::EnforcedHillClimbingSearch::get_plan); + std::vector> init_functions = {init_ff, init_ehc}; + for(auto f : init_functions) { + f(m); + } }