Skip to content
Snippets Groups Projects
  • Nora Abi Akar's avatar
    Expose global_properties in Python recipes (#1273) · 5fdfb02e
    Nora Abi Akar authored
    Extend Ptyhon recipe wrapper to support setting of global cell properties.
    
    * Rename `pyrecipe::get_probes` to `py_recipe::probes`
    * Remove `global_properties_shim` : It's easy to make mistakes with the class, as it holds a `mechanism_catalogue` and a `cable_cell_global_properties` which holds a pointer to the catalogue. This would likely have caused issues with the users.
    * Expose `py_recipe::global_properties`
    Unverified
    5fdfb02e
mechanism.cpp 9.26 KiB
#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