#include <optional> #include <stdexcept> #include <pybind11/pybind11.h> #include "pybind11/pytypes.h" #include <pybind11/stl.h> #include <arbor/cable_cell_param.hpp> #include <arbor/mechanism.hpp> #include <arbor/mechcat.hpp> #include "arbor/mechinfo.hpp" #include "conversion.hpp" #include "strprintf.hpp" namespace pyarb { void apply_derive(arb::mechanism_catalogue& m, const std::string& name, const std::string& parent, const std::unordered_map<std::string, double>& globals, const std::unordered_map<std::string, std::string>& ions) { if (globals.empty() && ions.empty()) { m.derive(name, parent); return; } std::vector<std::pair<std::string, double>> G; for (auto& g: globals) { G.push_back({g.first, g.second}); } std::vector<std::pair<std::string, std::string>> I; for (auto& i: ions) { I.push_back({i.first, i.second}); } m.derive(name, parent, G, I); } void register_mechanisms(pybind11::module& m) { using std::optional; using namespace pybind11::literals; pybind11::class_<arb::mechanism_field_spec> field_spec(m, "mechanism_field", "Basic information about a mechanism field."); field_spec .def(pybind11::init<const arb::mechanism_field_spec&>()) .def_readonly("units", &arb::mechanism_field_spec::units) .def_readonly("default", &arb::mechanism_field_spec::default_value) .def_readonly("min", &arb::mechanism_field_spec::lower_bound) .def_readonly("max", &arb::mechanism_field_spec::upper_bound) .def("__repr__", [](const arb::mechanism_field_spec& spec) { return util::pprintf("{units: '{}', default: {}, min: {}, max: {}}", (spec.units.size()? spec.units.c_str(): "1"), spec.default_value, spec.lower_bound, spec.upper_bound); }) .def("__str__", [](const arb::mechanism_field_spec& spec) { return util::pprintf("{units: '{}', default: {}, min: {}, max: {}}", (spec.units.size()? spec.units.c_str(): "1"), spec.default_value, spec.lower_bound, spec.upper_bound); }); pybind11::class_<arb::ion_dependency> ion_dep(m, "ion_dependency", "Information about a mechanism's dependence on an ion species."); ion_dep .def(pybind11::init<const arb::ion_dependency&>()) .def_readonly("write_int_con", &arb::ion_dependency::write_concentration_int) .def_readonly("write_ext_con", &arb::ion_dependency::write_concentration_ext) .def_readonly("write_rev_pot", &arb::ion_dependency::write_reversal_potential) .def_readonly("read_rev_pot", &arb::ion_dependency::read_reversal_potential) .def("__repr__", [](const arb::ion_dependency& dep) { auto tf = [](bool x) {return x? "True": "False";}; return util::pprintf("{write_int_con: {}, write_ext_con: {}, write_rev_pot: {}, read_rev_pot: {}}", tf(dep.write_concentration_int), tf(dep.write_concentration_ext), tf(dep.write_reversal_potential), tf(dep.read_reversal_potential)); }) .def("__str__", [](const arb::ion_dependency& dep) { auto tf = [](bool x) {return x? "True": "False";}; return util::pprintf("{write_int_con: {}, write_ext_con: {}, write_rev_pot: {}, read_rev_pot: {}}", tf(dep.write_concentration_int), tf(dep.write_concentration_ext), tf(dep.write_reversal_potential), tf(dep.read_reversal_potential)); }) ; pybind11::class_<arb::mechanism_info> mech_inf(m, "mechanism_info", "Meta data about a mechanism's fields and ion dependendencies."); mech_inf .def(pybind11::init<const arb::mechanism_info&>()) .def_readonly("globals", &arb::mechanism_info::globals, "Global fields have one value common to an instance of a mechanism, are constant in time and set at instantiation.") .def_readonly("parameters", &arb::mechanism_info::parameters, "Parameter fields may vary across the extent of a mechanism, but are constant in time and set at instantiation.") .def_readonly("state", &arb::mechanism_info::state, "State fields vary in time and across the extent of a mechanism, and potentially can be sampled at run-time.") .def_readonly("ions", &arb::mechanism_info::ions, "Ion dependencies.") .def_readonly("linear", &arb::mechanism_info::linear, "True if a synapse mechanism has linear current contributions so that multiple instances on the same compartment can be coalesed.") .def("__repr__", [](const arb::mechanism_info& inf) { return util::pprintf("(arbor.mechanism_info)"); }) .def("__str__", [](const arb::mechanism_info& inf) { return util::pprintf("(arbor.mechanism_info)"); }); pybind11::class_<arb::mechanism_catalogue> cat(m, "catalogue"); cat .def(pybind11::init<const arb::mechanism_catalogue&>()) .def("has", &arb::mechanism_catalogue::has, "name"_a, "Is 'name' in the catalogue?") .def("keys", &arb::mechanism_catalogue::mechanism_names, "Return a list of all mechanisms in this catalogues.") .def("is_derived", &arb::mechanism_catalogue::is_derived, "name"_a, "Is 'name' a derived mechanism or can it be implicitly derived?") .def("__getitem__", [](arb::mechanism_catalogue& c, const char* name) { try { return c[name]; } catch (...) { throw std::runtime_error(util::pprintf("\nKeyError: '{}'", name)); } }) .def("extend", &arb::mechanism_catalogue::import, "other"_a, "Catalogue to import into self", "prefix"_a, "Prefix for names in other", "Import another catalogue, possibly with a prefix. Will overwrite in case of name collisions.") .def("derive", &apply_derive, "name"_a, "parent"_a, "globals"_a=std::unordered_map<std::string, double>{}, "ions"_a=std::unordered_map<std::string, std::string>{}) .def("__repr__", [](const arb::mechanism_catalogue& cat) { return util::pprintf("<arbor.mechanism_catalogue>"); }) .def("__str__", [](const arb::mechanism_catalogue& cat) { return util::pprintf("<arbor.mechanism_catalogue>"); }); m.def("default_catalogue", [](){return arb::global_default_catalogue();}); m.def("allen_catalogue", [](){return arb::global_allen_catalogue();}); m.def("bbp_catalogue", [](){return arb::global_bbp_catalogue();}); // arb::mechanism_desc // For specifying a mechanism in the cable_cell interface. pybind11::class_<arb::mechanism_desc> mechanism_desc(m, "mechanism"); mechanism_desc .def(pybind11::init([](const char* n) {return arb::mechanism_desc{n};})) // allow construction of a description with parameters provided in a dictionary: // mech = arbor.mechanism('mech_name', {'param1': 1.2, 'param2': 3.14}) .def(pybind11::init( [](const char* name, std::unordered_map<std::string, double> params) { arb::mechanism_desc md(name); for (const auto& p: params) md.set(p.first, p.second); return md; }), "name"_a, "The name of the mechanism", "params"_a, "A dictionary of parameter values, with parameter name as key.", "Example usage setting parameters:\n" " m = arbor.mechanism('expsyn', {'tau': 1.4})\n" "will create parameters for the 'expsyn' mechanism, with the provided value\n" "for 'tau' overrides the default. If a parameter is not set, the default\n" "(as defined in NMODL) is used.\n\n" "Example overriding a global parameter:\n" " m = arbor.mechanism('nernst/R=8.3145,F=96485')") .def("set", [](arb::mechanism_desc& md, std::string name, double value) { md.set(name, value); }, "name"_a, "value"_a, "Set parameter value.") .def_property_readonly("name", [](const arb::mechanism_desc& md) { return md.name(); }, "The name of the mechanism.") .def_property_readonly("values", [](const arb::mechanism_desc& md) { return md.values(); }, "A dictionary of parameter values with parameter name as key.") .def("__repr__", [](const arb::mechanism_desc& md) { return util::pprintf("<arbor.mechanism: name '{}', parameters {}", md.name(), util::dictionary_csv(md.values())); }) .def("__str__", [](const arb::mechanism_desc& md) { return util::pprintf("('{}' {})", md.name(), util::dictionary_csv(md.values())); }); } } // namespace pyarb