From fa4231ef92e918090592e20e310058653a21b3de Mon Sep 17 00:00:00 2001 From: bcumming <bcumming@cscs.ch> Date: Wed, 5 Jun 2019 11:58:15 +0200 Subject: [PATCH] move python wrapper for schedules to their own files --- .ycm_extra_conf.py | 4 + python/CMakeLists.txt | 1 + python/context.cpp | 15 +-- python/conversion.hpp | 16 +++ python/error.hpp | 16 +-- python/event_generator.cpp | 229 +---------------------------------- python/event_generator.hpp | 6 +- python/pyarb.cpp | 5 +- python/recipe.cpp | 11 +- python/schedule.cpp | 236 +++++++++++++++++++++++++++++++++++++ python/schedule.hpp | 79 +++++++++++++ 11 files changed, 356 insertions(+), 262 deletions(-) create mode 100644 python/schedule.cpp create mode 100644 python/schedule.hpp diff --git a/.ycm_extra_conf.py b/.ycm_extra_conf.py index f3ea3705..27093e79 100644 --- a/.ycm_extra_conf.py +++ b/.ycm_extra_conf.py @@ -51,6 +51,10 @@ flags = [ '-I', 'arbor', '-I', + 'arbor/include', + '-I', + 'build/arbor/include', + '-I', 'ext/json/single_include', '-I', 'build/include', diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index 8065522a..9a000b3e 100644 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -23,6 +23,7 @@ add_library(pyarb MODULE mpi.cpp pyarb.cpp recipe.cpp + schedule.cpp strings.cpp ) diff --git a/python/context.cpp b/python/context.cpp index 04cb3ab0..bab2b278 100644 --- a/python/context.cpp +++ b/python/context.cpp @@ -8,7 +8,6 @@ #include <pybind11/pybind11.h> - #include "context.hpp" #include "conversion.hpp" #include "error.hpp" @@ -20,25 +19,21 @@ namespace pyarb { -namespace { -auto is_nonneg_int = [](int n){ return n>=0; }; -} - // A Python shim that holds the information that describes an arb::proc_allocation. struct proc_allocation_shim { arb::util::optional<int> gpu_id = {}; int num_threads = 1; - proc_allocation_shim(): proc_allocation_shim(1, pybind11::none()) {} - proc_allocation_shim(int threads, pybind11::object gpu) { set_num_threads(threads); set_gpu_id(gpu); } + proc_allocation_shim(): proc_allocation_shim(1, pybind11::none()) {} + // getter and setter (in order to assert when being set) void set_gpu_id(pybind11::object gpu) { - gpu_id = py2optional<int>(gpu, "gpu_id must be None, or a non-negative integer", is_nonneg_int); + gpu_id = py2optional<int>(gpu, "gpu_id must be None, or a non-negative integer", is_nonneg()); }; void set_num_threads(int threads) { @@ -133,7 +128,7 @@ void register_contexts(pybind11::module& m) { const char* gpu_err_str = "gpu_id must be None, or a non-negative integer"; const char* mpi_err_str = "mpi must be None, or an MPI communicator"; - auto gpu_id = py2optional<int>(gpu, gpu_err_str, is_nonneg_int); + auto gpu_id = py2optional<int>(gpu, gpu_err_str, is_nonneg()); arb::proc_allocation alloc(threads, gpu_id.value_or(-1)); if (can_convert_to_mpi_comm(mpi)) { @@ -153,7 +148,7 @@ void register_contexts(pybind11::module& m) { #else .def(pybind11::init( [](int threads, pybind11::object gpu){ - auto gpu_id = py2optional<int>(gpu, "gpu_id must be None, or a non-negative integer", is_nonneg_int); + auto gpu_id = py2optional<int>(gpu, "gpu_id must be None, or a non-negative integer", is_nonneg()); return context_shim(arb::make_context(arb::proc_allocation(threads, gpu_id.value_or(-1)))); }), "threads"_a=1, "gpu_id"_a=pybind11::none(), diff --git a/python/conversion.hpp b/python/conversion.hpp index 811eb74e..ad0ae582 100644 --- a/python/conversion.hpp +++ b/python/conversion.hpp @@ -2,11 +2,27 @@ #include <pybind11/pybind11.h> #include <pybind11/pytypes.h> +#include <pybind11/stl.h> +#include <arbor/util/optional.hpp> #include "error.hpp" +// from https://pybind11.readthedocs.io/en/stable/advanced/cast/stl.html?highlight=boost%3A%3Aoptional#c-17-library-containers +namespace pybind11 { namespace detail { + template <typename T> + struct type_caster<arb::util::optional<T>>: optional_caster<arb::util::optional<T>> {}; +}} + namespace pyarb { +struct is_nonneg { + template<typename T> + constexpr + bool operator()(const T& v) { + return v>=T(0); + } +}; + // A helper function for converting from a Python object to a C++ optional wrapper. // Throws an runtime_error exception with msg if either the Python object // can't be converted to type T, or if the predicate is false for the value. diff --git a/python/error.hpp b/python/error.hpp index fbcd5ba7..f3b0cb6e 100644 --- a/python/error.hpp +++ b/python/error.hpp @@ -3,18 +3,6 @@ #include <stdexcept> #include <string> -#include <arbor/util/optional.hpp> - -#include <pybind11/pybind11.h> -#include <pybind11/pytypes.h> -#include <pybind11/stl.h> - -// from https://pybind11.readthedocs.io/en/stable/advanced/cast/stl.html?highlight=boost%3A%3Aoptional#c-17-library-containers -namespace pybind11 { namespace detail { - template <typename T> - struct type_caster<arb::util::optional<T>> : optional_caster<arb::util::optional<T>> {}; -}} - namespace pyarb { // Python wrapper errors @@ -22,9 +10,11 @@ namespace pyarb { struct pyarb_error: std::runtime_error { pyarb_error(const std::string& what_msg): std::runtime_error(what_msg) {} + pyarb_error(const char* what_msg): + std::runtime_error(what_msg) {} }; -static +inline void assert_throw(bool pred, const char* msg) { if (!pred) throw pyarb_error(msg); } diff --git a/python/event_generator.cpp b/python/event_generator.cpp index 9344a3fd..96505ba9 100644 --- a/python/event_generator.cpp +++ b/python/event_generator.cpp @@ -4,6 +4,7 @@ #include <arbor/common_types.hpp> #include <arbor/schedule.hpp> +#include <arbor/util/optional.hpp> #include <pybind11/pybind11.h> #include <pybind11/pytypes.h> @@ -12,135 +13,10 @@ #include "conversion.hpp" #include "error.hpp" #include "event_generator.hpp" +#include "schedule.hpp" namespace pyarb { -namespace { -auto is_nonneg = [](auto&& t){ return t>=0.; }; -} - -// A Python shim that holds the information that describes an -// arb::regular_schedule. This is wrapped in pybind11, and users constructing -// a regular_schedule in python are manipulating this type. This is converted to -// an arb::regular_schedule when a C++ recipe is created from a Python recipe. -struct regular_schedule_shim { - using time_type = arb::time_type; - using opt_time_type = arb::util::optional<time_type>; - - opt_time_type tstart = {}; - opt_time_type tstop = {}; - time_type dt = 0; - - regular_schedule_shim() = default; - - regular_schedule_shim(pybind11::object t0, time_type deltat, pybind11::object t1) { - set_tstart(t0); - set_tstop(t1); - set_dt(deltat); - } - - // getter and setter (in order to assert when being set) - void set_tstart(pybind11::object t) { - tstart = py2optional<time_type>(t, "tstart must a non-negative number, or None", is_nonneg); - }; - void set_tstop(pybind11::object t) { - tstop = py2optional<time_type>(t, "tstop must a non-negative number, or None", is_nonneg); - }; - void set_dt(time_type delta_t) { - pyarb::assert_throw(is_nonneg(delta_t), "dt must be a non-negative number"); - dt = delta_t; - }; - - opt_time_type get_tstart() const { return tstart; } - opt_time_type get_dt() const { return dt; } - opt_time_type get_tstop() const { return tstop; } - - arb::schedule schedule() const { - return arb::regular_schedule( - tstart.value_or(arb::terminal_time), - dt, - tstop.value_or(arb::terminal_time)); - } - -}; - -// A Python shim for arb::explicit_schedule. -// This is wrapped in pybind11, and users constructing an explicit_schedule in -// Python are manipulating this type. This is converted to an -// arb::explicit_schedule when a C++ recipe is created from a Python recipe. - -struct explicit_schedule_shim { - using time_type = arb::time_type; - - std::vector<time_type> times; - - explicit_schedule_shim() = default; - - explicit_schedule_shim(std::vector<time_type> t) { - set_times(t); - } - - // getter and setter (in order to assert when being set) - void set_times(std::vector<time_type> t) { - times = std::move(t); - // Sort the times in ascending order if necessary - if (!std::is_sorted(times.begin(), times.end())) { - std::sort(times.begin(), times.end()); - } - // Assert that there are no negative times - if (times.size()) { - pyarb::assert_throw(is_nonneg(times[0]), - "explicit time schedule can not contain negative values"); - } - }; - - std::vector<time_type> get_times() const { return times; } - - arb::schedule schedule() const { - return arb::explicit_schedule(times); - } -}; - -// A Python shim for arb::poisson_schedule. -// This is wrapped in pybind11, and users constructing a poisson_schedule in -// Python are manipulating this type. This is converted to an -// arb::poisson_schedule when a C++ recipe is created from a Python recipe. - -struct poisson_schedule_shim { - using rng_type = std::mt19937_64; - using time_type = arb::time_type; - - time_type tstart = arb::terminal_time; - time_type freq = 10.; - rng_type::result_type seed = 0; - - poisson_schedule_shim() = default; - - poisson_schedule_shim(time_type ts, time_type f, rng_type::result_type s) { - set_tstart(ts); - set_freq(f); - seed = s; - } - - void set_tstart(time_type t) { - pyarb::assert_throw(is_nonneg(t), "tstart must be a non-negative number"); - tstart = t; - }; - - void set_freq(time_type f) { - pyarb::assert_throw(is_nonneg(f), "frequency must be a non-negative number"); - freq = f; - }; - - const time_type get_tstart() const { return tstart; } - const time_type get_freq() const { return freq; } - - arb::schedule schedule() const { - // convert frequency to kHz. - return arb::poisson_schedule(tstart, freq/1000., rng_type(seed)); - } -}; - template <typename Sched> event_generator_shim make_event_generator( arb::cell_member_type target, @@ -150,110 +26,9 @@ event_generator_shim make_event_generator( return event_generator_shim(target, weight, sched.schedule()); } -// Helper template for printing C++ optional types in Python. -// Prints either the value, or None if optional value is not set. -template <typename T> -std::string to_string(const arb::util::optional<T>& o, std::string unit) { - if (!o) return "None"; - - std::stringstream s; - s << *o << " " << unit; - return s.str(); -} - -std::string schedule_regular_string(const regular_schedule_shim& r) { - std::stringstream s; - s << "<regular_schedule: " - << "tstart " << to_string(r.tstart, "ms") << ", " - << "dt " << r.dt << " ms, " - << "tstop " << to_string(r.tstop, "ms") << ">"; - return s.str(); -}; - -std::string schedule_explicit_string(const explicit_schedule_shim& e) { - std::stringstream s; - s << "<explicit_schedule: times ["; - bool first = true; - for (auto t: e.times) { - if (!first) { - s << " "; - } - s << t; - first = false; - } - s << "] ms>"; - return s.str(); -}; - -std::string schedule_poisson_string(const poisson_schedule_shim& p) { - std::stringstream s; - s << "<poisson_schedule: tstart " << p.tstart << " ms" - << ", freq " << p.freq << " Hz" - << ", seed " << p.seed << ">"; - return s.str(); -}; - void register_event_generators(pybind11::module& m) { using namespace pybind11::literals; - using time_type = arb::time_type; - - // Regular schedule - pybind11::class_<regular_schedule_shim> regular_schedule(m, "regular_schedule", - "Describes a regular schedule with multiples of dt within the interval [tstart, tstop)."); - - regular_schedule - .def(pybind11::init<pybind11::object, time_type, pybind11::object>(), - "tstart"_a = pybind11::none(), "dt"_a = 0., "tstop"_a = pybind11::none(), - "Construct a regular schedule with arguments:\n" - " tstart: The delivery time of the first event in the sequence (in ms, default None).\n" - " dt: The interval between time points (in ms, default 0).\n" - " tstop: No events delivered after this time (in ms, default None).") - .def_property("tstart", ®ular_schedule_shim::get_tstart, ®ular_schedule_shim::set_tstart, - "The delivery time of the first event in the sequence (in ms).") - .def_property("tstop", ®ular_schedule_shim::get_tstop, ®ular_schedule_shim::set_tstop, - "No events delivered after this time (in ms).") - .def_property("dt", ®ular_schedule_shim::get_dt, ®ular_schedule_shim::set_dt, - "The interval between time points (in ms).") - .def("__str__", &schedule_regular_string) - .def("__repr__",&schedule_regular_string); - - // Explicit schedule - pybind11::class_<explicit_schedule_shim> explicit_schedule(m, "explicit_schedule", - "Describes an explicit schedule at a predetermined (sorted) sequence of times."); - - explicit_schedule - .def(pybind11::init<>(), - "Construct an empty explicit schedule.\n") - .def(pybind11::init<std::vector<time_type>>(), - "times"_a, - "Construct an explicit schedule with argument:\n" - " times: A list of times (in ms, default []).") - .def_property("times", &explicit_schedule_shim::get_times, &explicit_schedule_shim::set_times, - "A list of times (in ms).") - .def("__str__", &schedule_explicit_string) - .def("__repr__",&schedule_explicit_string); - - // Poisson schedule - pybind11::class_<poisson_schedule_shim> poisson_schedule(m, "poisson_schedule", - "Describes a schedule according to a Poisson process."); - - poisson_schedule - .def(pybind11::init<time_type, time_type, std::mt19937_64::result_type>(), - "tstart"_a = 0., "freq"_a = 10., "seed"_a = 0, - "Construct a Poisson schedule with arguments:\n" - " tstart: The delivery time of the first event in the sequence (in ms, default 0 ms).\n" - " freq: The expected frequency (in Hz, default 10 Hz).\n" - " seed: The seed for the random number generator (default 0).") - .def_property("tstart", &poisson_schedule_shim::get_tstart, &poisson_schedule_shim::set_tstart, - "The delivery time of the first event in the sequence (in ms).") - .def_property("freq", &poisson_schedule_shim::get_freq, &poisson_schedule_shim::set_freq, - "The expected frequency (in Hz).") - .def_readwrite("seed", &poisson_schedule_shim::seed, - "The seed for the random number generator.") - .def("__str__", &schedule_poisson_string) - .def("__repr__",&schedule_poisson_string); -// Event generator pybind11::class_<event_generator_shim> event_generator(m, "event_generator"); event_generator diff --git a/python/event_generator.hpp b/python/event_generator.hpp index ed41c10e..d58d8d4d 100644 --- a/python/event_generator.hpp +++ b/python/event_generator.hpp @@ -3,8 +3,6 @@ #include <arbor/common_types.hpp> #include <arbor/schedule.hpp> -#include <pybind11/pybind11.h> - namespace pyarb { struct event_generator_shim { @@ -12,8 +10,8 @@ struct event_generator_shim { double weight; arb::schedule time_sched; - event_generator_shim(arb::cell_member_type cell, double event_weight, arb::schedule sched): - target(cell), + event_generator_shim(arb::cell_member_type gid, double event_weight, arb::schedule sched): + target(gid), weight(event_weight), time_sched(std::move(sched)) {} diff --git a/python/pyarb.cpp b/python/pyarb.cpp index c964caa4..965d6406 100644 --- a/python/pyarb.cpp +++ b/python/pyarb.cpp @@ -10,10 +10,12 @@ void register_config(pybind11::module& m); void register_contexts(pybind11::module& m); void register_event_generators(pybind11::module& m); void register_identifiers(pybind11::module& m); +void register_recipe(pybind11::module& m); +void register_schedules(pybind11::module& m); + #ifdef ARB_MPI_ENABLED void register_mpi(pybind11::module& m); #endif -void register_recipe(pybind11::module& m); } PYBIND11_MODULE(arbor, m) { @@ -28,4 +30,5 @@ PYBIND11_MODULE(arbor, m) { pyarb::register_mpi(m); #endif pyarb::register_recipe(m); + pyarb::register_schedules(m); } diff --git a/python/recipe.cpp b/python/recipe.cpp index 93d1e2bf..8bb0980e 100644 --- a/python/recipe.cpp +++ b/python/recipe.cpp @@ -19,7 +19,6 @@ namespace pyarb { -// ========================================= Unwrap ========================================= // The py::recipe::cell_decription returns a pybind11::object, that is // unwrapped and copied into a arb::util::unique_any. @@ -58,10 +57,9 @@ arb::util::unique_any py_recipe_shim::get_cell_description(arb::cell_gid_type gi // The py::recipe::global_properties returns a pybind11::object, that is // unwrapped and copied into a arb::util::any. arb::util::any py_recipe_shim::get_global_properties(arb::cell_kind kind) const { - using pybind11::isinstance; using pybind11::cast; - // Aquire the GIL because it must be held when calling isinstance and cast. + // Aquire the GIL because it must be held when calling cast. auto guard = pybind11::gil_scoped_acquire(); // Get the python object pyarb::global_properties from the python front end @@ -73,10 +71,9 @@ arb::util::any py_recipe_shim::get_global_properties(arb::cell_kind kind) const else return arb::util::any{}; - throw pyarb_error( - "recipe.global_properties returned \"" - + std::string(pybind11::str(o)) - + "\" which does not describe a known Arbor global property description"); + throw pyarb_error( "recipe.global_properties returned \"" + + std::string(pybind11::str(o)) + + "\" which does not describe a known Arbor global property description"); } diff --git a/python/schedule.cpp b/python/schedule.cpp new file mode 100644 index 00000000..2b81a148 --- /dev/null +++ b/python/schedule.cpp @@ -0,0 +1,236 @@ +#include <arbor/schedule.hpp> +#include <arbor/common_types.hpp> +#include <arbor/util/optional.hpp> + +#include <pybind11/pybind11.h> + +#include "conversion.hpp" +#include "schedule.hpp" + +namespace pyarb { + +// +// regular_schedule shim +// + +regular_schedule_shim::regular_schedule_shim( + pybind11::object t0, + time_type deltat, + pybind11::object t1) +{ + set_tstart(t0); + set_tstop(t1); + set_dt(deltat); +} + +void regular_schedule_shim::set_tstart(pybind11::object t) { + tstart = py2optional<time_type>( + t, "tstart must a non-negative number, or None", is_nonneg()); +}; + +void regular_schedule_shim::set_tstop(pybind11::object t) { + tstop = py2optional<time_type>( + t, "tstop must a non-negative number, or None", is_nonneg()); +}; + +void regular_schedule_shim::set_dt(arb::time_type delta_t) { + pyarb::assert_throw(is_nonneg()(delta_t), "dt must be a non-negative number"); + dt = delta_t; +}; + +regular_schedule_shim::opt_time_type regular_schedule_shim::get_tstart() const { + return tstart; +} + +regular_schedule_shim::opt_time_type regular_schedule_shim::get_dt() const { + return dt; +} + +regular_schedule_shim::opt_time_type regular_schedule_shim::get_tstop() const { + return tstop; +} + +arb::schedule regular_schedule_shim::schedule() const { + return arb::regular_schedule( + tstart.value_or(arb::terminal_time), + dt, + tstop.value_or(arb::terminal_time)); +} + +// +// explicit_schedule shim +// + +//struct explicit_schedule_shim { +explicit_schedule_shim::explicit_schedule_shim(std::vector<arb::time_type> t) { + set_times(t); +} + +// getter and setter (in order to assert when being set) +void explicit_schedule_shim::set_times(std::vector<arb::time_type> t) { + times = std::move(t); + + // Sort the times in ascending order if necessary + if (!std::is_sorted(times.begin(), times.end())) { + std::sort(times.begin(), times.end()); + } + + // Assert that there are no negative times + if (times.size()) { + pyarb::assert_throw(is_nonneg()(times[0]), + "explicit time schedule can not contain negative values"); + } +}; + +std::vector<arb::time_type> explicit_schedule_shim::get_times() const { + return times; +} + +arb::schedule explicit_schedule_shim::schedule() const { + return arb::explicit_schedule(times); +} + +// +// poisson_schedule shim +// + +poisson_schedule_shim::poisson_schedule_shim( + arb::time_type ts, + arb::time_type f, + rng_type::result_type s) +{ + set_tstart(ts); + set_freq(f); + seed = s; +} + +void poisson_schedule_shim::set_tstart(arb::time_type t) { + pyarb::assert_throw(is_nonneg()(t), "tstart must be a non-negative number"); + tstart = t; +}; + +void poisson_schedule_shim::set_freq(arb::time_type f) { + pyarb::assert_throw(is_nonneg()(f), "frequency must be a non-negative number"); + freq = f; +}; + +arb::time_type poisson_schedule_shim::get_tstart() const { + return tstart; +} + +arb::time_type poisson_schedule_shim::get_freq() const { + return freq; +} + +arb::schedule poisson_schedule_shim::schedule() const { + // convert frequency to kHz. + return arb::poisson_schedule(tstart, freq/1000., rng_type(seed)); +} + +void register_schedules(pybind11::module& m) { + using namespace pybind11::literals; + using time_type = arb::time_type; + + // Regular schedule + pybind11::class_<regular_schedule_shim> regular_schedule(m, "regular_schedule", + "Describes a regular schedule with multiples of dt within the interval [tstart, tstop)."); + + regular_schedule + .def(pybind11::init<pybind11::object, time_type, pybind11::object>(), + "tstart"_a = pybind11::none(), "dt"_a = 0., "tstop"_a = pybind11::none(), + "Construct a regular schedule with arguments:\n" + " tstart: The delivery time of the first event in the sequence (in ms, default None).\n" + " dt: The interval between time points (in ms, default 0).\n" + " tstop: No events delivered after this time (in ms, default None).") + .def_property("tstart", ®ular_schedule_shim::get_tstart, ®ular_schedule_shim::set_tstart, + "The delivery time of the first event in the sequence (in ms).") + .def_property("tstop", ®ular_schedule_shim::get_tstop, ®ular_schedule_shim::set_tstop, + "No events delivered after this time (in ms).") + .def_property("dt", ®ular_schedule_shim::get_dt, ®ular_schedule_shim::set_dt, + "The interval between time points (in ms).") + ; //.def("__str__", &schedule_regular_string) + //.def("__repr__",&schedule_regular_string); + + // Explicit schedule + pybind11::class_<explicit_schedule_shim> explicit_schedule(m, "explicit_schedule", + "Describes an explicit schedule at a predetermined (sorted) sequence of times."); + + explicit_schedule + .def(pybind11::init<>(), + "Construct an empty explicit schedule.\n") + .def(pybind11::init<std::vector<time_type>>(), + "times"_a, + "Construct an explicit schedule with argument:\n" + " times: A list of times (in ms, default []).") + .def_property("times", &explicit_schedule_shim::get_times, &explicit_schedule_shim::set_times, + "A list of times (in ms).") + ; //.def("__str__", &schedule_explicit_string) + //.def("__repr__",&schedule_explicit_string); + + // Poisson schedule + pybind11::class_<poisson_schedule_shim> poisson_schedule(m, "poisson_schedule", + "Describes a schedule according to a Poisson process."); + + poisson_schedule + .def(pybind11::init<time_type, time_type, std::mt19937_64::result_type>(), + "tstart"_a = 0., "freq"_a = 10., "seed"_a = 0, + "Construct a Poisson schedule with arguments:\n" + " tstart: The delivery time of the first event in the sequence (in ms, default 0 ms).\n" + " freq: The expected frequency (in Hz, default 10 Hz).\n" + " seed: The seed for the random number generator (default 0).") + .def_property("tstart", &poisson_schedule_shim::get_tstart, &poisson_schedule_shim::set_tstart, + "The delivery time of the first event in the sequence (in ms).") + .def_property("freq", &poisson_schedule_shim::get_freq, &poisson_schedule_shim::set_freq, + "The expected frequency (in Hz).") + .def_readwrite("seed", &poisson_schedule_shim::seed, + "The seed for the random number generator.") + ; //.def("__str__", &schedule_poisson_string) + //.def("__repr__",&schedule_poisson_string); +} + + +} +/* +// Helper template for printing C++ optional types in Python. +// Prints either the value, or None if optional value is not set. +template <typename T> +std::string to_string(const arb::util::optional<T>& o, std::string unit) { + if (!o) return "None"; + + std::stringstream s; + s << *o << " " << unit; + return s.str(); +} + +std::string schedule_regular_string(const regular_schedule_shim& r) { + std::stringstream s; + s << "<regular_schedule: " + << "tstart " << to_string(r.tstart, "ms") << ", " + << "dt " << r.dt << " ms, " + << "tstop " << to_string(r.tstop, "ms") << ">"; + return s.str(); +}; + +std::string schedule_explicit_string(const explicit_schedule_shim& e) { + std::stringstream s; + s << "<explicit_schedule: times ["; + bool first = true; + for (auto t: e.times) { + if (!first) { + s << " "; + } + s << t; + first = false; + } + s << "] ms>"; + return s.str(); +}; + +std::string schedule_poisson_string(const poisson_schedule_shim& p) { + std::stringstream s; + s << "<poisson_schedule: tstart " << p.tstart << " ms" + << ", freq " << p.freq << " Hz" + << ", seed " << p.seed << ">"; + return s.str(); +}; +*/ diff --git a/python/schedule.hpp b/python/schedule.hpp new file mode 100644 index 00000000..9f43ccc1 --- /dev/null +++ b/python/schedule.hpp @@ -0,0 +1,79 @@ +#pragma once + +#include <arbor/schedule.hpp> +#include <arbor/common_types.hpp> +#include <arbor/util/optional.hpp> + +#include <pybind11/pybind11.h> + +namespace pyarb { + +// A Python shim that holds the information that describes an +// arb::regular_schedule. This is wrapped in pybind11, and users constructing +// a regular_schedule in python are manipulating this type. This is converted to +// an arb::regular_schedule when a C++ recipe is created from a Python recipe. +struct regular_schedule_shim { + using time_type = arb::time_type; + using opt_time_type = arb::util::optional<time_type>; + + opt_time_type tstart = {}; + opt_time_type tstop = {}; + time_type dt = 0; + + regular_schedule_shim() = default; + + regular_schedule_shim(pybind11::object t0, time_type deltat, pybind11::object t1); + + // getter and setter (in order to assert when being set) + void set_tstart(pybind11::object t); + void set_tstop(pybind11::object t); + void set_dt(time_type delta_t); + + opt_time_type get_tstart() const; + opt_time_type get_dt() const; + opt_time_type get_tstop() const; + + arb::schedule schedule() const; +}; + +// A Python shim for arb::explicit_schedule. +// This is wrapped in pybind11, and users constructing an explicit_schedule in +// Python are manipulating this type. This is converted to an +// arb::explicit_schedule when a C++ recipe is created from a Python recipe. +struct explicit_schedule_shim { + std::vector<arb::time_type> times; + + explicit_schedule_shim() = default; + explicit_schedule_shim(std::vector<arb::time_type> t); + + // getter and setter (in order to assert when being set) + void set_times(std::vector<arb::time_type> t); + std::vector<arb::time_type> get_times() const; + + arb::schedule schedule() const; +}; + +// A Python shim for arb::poisson_schedule. +// This is wrapped in pybind11, and users constructing a poisson_schedule in +// Python are manipulating this type. This is converted to an +// arb::poisson_schedule when a C++ recipe is created from a Python recipe. +struct poisson_schedule_shim { + using rng_type = std::mt19937_64; + + arb::time_type tstart = arb::terminal_time; + arb::time_type freq = 10.; // Hz + rng_type::result_type seed = 0; + + poisson_schedule_shim() = default; + poisson_schedule_shim(arb::time_type ts, arb::time_type f, rng_type::result_type s); + + void set_tstart(arb::time_type t); + void set_freq(arb::time_type f); + + arb::time_type get_tstart() const; + arb::time_type get_freq() const; + + arb::schedule schedule() const; +}; + +} -- GitLab