Skip to content
Snippets Groups Projects
Select Git revision
  • d8c21852d1ef4291126fa08e8ac7941e5083f286
  • main default protected
2 results

reorient_allen_volume.py

Blame
  • cells.cpp 10.71 KiB
    #include <pybind11/pybind11.h>
    #include <pybind11/stl.h>
    
    #include <arbor/benchmark_cell.hpp>
    #include <arbor/cable_cell.hpp>
    #include <arbor/lif_cell.hpp>
    #include <arbor/schedule.hpp>
    #include <arbor/spike_source_cell.hpp>
    #include <arbor/util/unique_any.hpp>
    
    #include "error.hpp"
    #include "schedule.hpp"
    #include "strprintf.hpp"
    
    namespace pyarb {
    
    // Convert a cell description inside a Python object to a cell
    // description in a unique_any, as required by the recipe interface.
    //
    // Warning: requires that the GIL has been acquired before calling,
    // if there is a segmentation fault in the cast or isinstance calls,
    // check that the caller has the GIL.
    arb::util::unique_any convert_cell(pybind11::object o) {
        using pybind11::isinstance;
        using pybind11::cast;
    
        pybind11::gil_scoped_acquire guard;
        if (isinstance<arb::spike_source_cell>(o)) {
            return arb::util::unique_any(cast<arb::spike_source_cell>(o));
        }
        if (isinstance<arb::benchmark_cell>(o)) {
            return arb::util::unique_any(cast<arb::benchmark_cell>(o));
        }
        if (isinstance<arb::lif_cell>(o)) {
            return arb::util::unique_any(cast<arb::lif_cell>(o));
        }
        if (isinstance<arb::cable_cell>(o)) {
            return arb::util::unique_any(cast<arb::cable_cell>(o));
        }
    
        throw pyarb_error("recipe.cell_description returned \""
                          + std::string(pybind11::str(o))
                          + "\" which does not describe a known Arbor cell type");
    }
    
    //
    // Somewhat hacky bit of code for generating cells with random morphologies.
    //
    
    // Parameters used to generate the random cell morphologies.
    struct cell_parameters {
        cell_parameters() = default;
    
        //  Maximum number of levels in the cell (not including the soma)
        unsigned max_depth = 5;
    
        // The following parameters are described as ranges.
        // The first value is at the soma, and the last value is used on the last level.
        // Values at levels in between are found by linear interpolation.
        std::array<double,2> branch_probs = {1.0, 0.5}; //  Probability of a branch occuring.
        std::array<unsigned,2> compartments = {20, 2};  //  Compartment count on a branch.
        std::array<double,2> lengths = {200, 20};       //  Length of branch in μm.
    
        // The number of synapses per cell.
        unsigned synapses = 1;
    
        friend std::ostream& operator<<(std::ostream& o, const cell_parameters& p) {
            return
                o << "<cell_parameters: depth " << p.max_depth
                  << ", synapses " << p.synapses
                  << ", branch_probs [" << p.branch_probs[0] << ":" << p.branch_probs[1] << "]"
                  << ", compartments [" << p.compartments[0] << ":" << p.compartments[1] << "]"
                  << ", lengths ["      << p.lengths[0]      << ":" << p.lengths[1] << "]>";
        }
    };
    
    // Helper used to interpolate in branch_cell.
    template <typename T>
    double interp(const std::array<T,2>& r, unsigned i, unsigned n) {
        double p = i * 1./(n-1);
        double r0 = r[0];
        double r1 = r[1];
        return r[0] + p*(r1-r0);
    }
    
    arb::cable_cell make_cable_cell(arb::cell_gid_type gid, const cell_parameters& params) {
        arb::cable_cell cell;
    
        cell.default_parameters.axial_resistivity = 100;
    
        // Add soma.
        auto soma = cell.add_soma(12.6157/2.0); // For area of 500 μm².
        soma->add_mechanism("hh");
    
        std::vector<std::vector<unsigned>> levels;
        levels.push_back({0});
    
        // Standard mersenne_twister_engine seeded with gid.
        std::mt19937 gen(gid);
        std::uniform_real_distribution<double> dis(0, 1);
    
        double dend_radius = 0.5; // Diameter of 1 μm for each cable.
    
        unsigned nsec = 1;
        for (unsigned i=0; i<params.max_depth; ++i) {
            // Branch prob at this level.
            double bp = interp(params.branch_probs, i, params.max_depth);
            // Length at this level.
            double l = interp(params.lengths, i, params.max_depth);
            // Number of compartments at this level.
            unsigned nc = std::round(interp(params.compartments, i, params.max_depth));
    
            std::vector<unsigned> sec_ids;
            for (unsigned sec: levels[i]) {
                for (unsigned j=0; j<2; ++j) {
                    if (dis(gen)<bp) {
                        sec_ids.push_back(nsec++);
                        auto dend = cell.add_cable(sec, arb::make_segment<arb::cable_segment>(arb::section_kind::dendrite, dend_radius, dend_radius, l));
                        dend->set_compartments(nc);
                        dend->add_mechanism("pas");
                    }
                }
            }
            if (sec_ids.empty()) {
                break;
            }
            levels.push_back(sec_ids);
        }
    
        // Add spike threshold detector at the soma.
        cell.place(arb::mlocation{0,0}, arb::threshold_detector{10});
    
        // Add a synapse to the mid point of the first dendrite.
        cell.place(arb::mlocation{1, 0.5}, "expsyn");
    
        // Add additional synapses.
        for (unsigned i=1u; i<params.synapses; ++i) {
            cell.place(arb::mlocation{1, 0.5}, "expsyn");
        }
    
        return cell;
    }
    
    //
    // string printers
    //
    
    std::string lif_str(const arb::lif_cell& c){
        return util::pprintf(
                    "<arbor.lif_cell: tau_m {}, V_th {}, C_m {}, E_L {}, V_m {}, t_ref {}, V_reset {}>",
                    c.tau_m, c.V_th, c.C_m, c.E_L, c.V_m, c.t_ref, c.V_reset);
    }
    
    void register_cells(pybind11::module& m) {
        using namespace pybind11::literals;
    
        pybind11::class_<arb::spike_source_cell> spike_source_cell(m, "spike_source_cell",
            "A spike source cell, that generates a user-defined sequence of spikes that act as inputs for other cells in the network.");
    
        spike_source_cell
            .def(pybind11::init<>(
                [](const regular_schedule_shim& sched){
                    return arb::spike_source_cell{sched.schedule()};}),
                "schedule"_a, "Construct a spike source cell that generates spikes at regular intervals.")
            .def(pybind11::init<>(
                [](const explicit_schedule_shim& sched){
                    return arb::spike_source_cell{sched.schedule()};}),
                "schedule"_a, "Construct a spike source cell that generates spikes at a sequence of user-defined times.")
            .def(pybind11::init<>(
                [](const poisson_schedule_shim& sched){
                    return arb::spike_source_cell{sched.schedule()};}),
                "schedule"_a, "Construct a spike source cell that generates spikes at times defined by a Poisson sequence.")
            .def("__repr__", [](const arb::spike_source_cell&){return "<arbor.spike_source_cell>";})
            .def("__str__",  [](const arb::spike_source_cell&){return "<arbor.spike_source_cell>";});
    
        pybind11::class_<arb::benchmark_cell> benchmark_cell(m, "benchmark_cell",
            "A benchmarking cell, used by Arbor developers to test communication performance.\n"
            "A benchmark cell generates spikes at a user-defined sequence of time points, and\n"
            "the time taken to integrate a cell can be tuned by setting the realtime_ratio,\n"
            "for example if realtime_ratio=2, a cell will take 2 seconds of CPU time to\n"
            "simulate 1 second.\n");
    
        benchmark_cell
            .def(pybind11::init<>(
                [](const regular_schedule_shim& sched, double ratio){
                    return arb::benchmark_cell{sched.schedule(), ratio};}),
                "schedule"_a, "realtime_ratio"_a=1.0,
                "Construct a benchmark cell that generates spikes at regular intervals.")
            .def(pybind11::init<>(
                [](const explicit_schedule_shim& sched, double ratio){
                    return arb::benchmark_cell{sched.schedule(), ratio};}),
                "schedule"_a, "realtime_ratio"_a=1.0,
                "Construct a benchmark cell that generates spikes at a sequence of user-defined times.")
            .def(pybind11::init<>(
                [](const poisson_schedule_shim& sched, double ratio){
                    return arb::benchmark_cell{sched.schedule(), ratio};}),
                "schedule"_a, "realtime_ratio"_a=1.0,
                "Construct a benchmark cell that generates spikes at times defined by a Poisson sequence.")
            .def("__repr__", [](const arb::benchmark_cell&){return "<arbor.benchmark_cell>";})
            .def("__str__",  [](const arb::benchmark_cell&){return "<arbor.benchmark_cell>";});
    
        pybind11::class_<arb::lif_cell> lif_cell(m, "lif_cell",
            "A benchmarking cell, used by Arbor developers to test communication performance.");
    
        lif_cell
            .def(pybind11::init<>())
            .def_readwrite("tau_m",     &arb::lif_cell::tau_m,      "Membrane potential decaying constant [ms].")
            .def_readwrite("V_th",      &arb::lif_cell::V_th,       "Firing threshold [mV].")
            .def_readwrite("C_m",       &arb::lif_cell::C_m,        "Membrane capacitance [pF].")
            .def_readwrite("E_L",       &arb::lif_cell::E_L,        "Resting potential [mV].")
            .def_readwrite("V_m",       &arb::lif_cell::V_m,        "Initial value of the Membrane potential [mV].")
            .def_readwrite("t_ref",     &arb::lif_cell::t_ref,      "Refractory period [ms].")
            .def_readwrite("V_reset",   &arb::lif_cell::V_reset,    "Reset potential [mV].")
            .def("__repr__", &lif_str)
            .def("__str__",  &lif_str);
    
        pybind11::class_<cell_parameters> cell_params(m, "cell_parameters", "Parameters used to generate the random cell morphologies.");
        cell_params
            .def(pybind11::init<>())
            .def_readwrite("depth",        &cell_parameters::max_depth,     "The maximum depth of the branch structure.")
            .def_readwrite("lengths",      &cell_parameters::lengths,       "Length of branch [μm], given as range.")
            .def_readwrite("synapses",     &cell_parameters::synapses,      "The number of randomly generated synapses on the cell.")
            .def_readwrite("branch_probs", &cell_parameters::branch_probs,  "Probability of a branch occuring, given as range.")
            .def_readwrite("compartments", &cell_parameters::compartments,  "Compartment count on a branch, given as range.")
            .def("__repr__", util::to_string<cell_parameters>)
            .def("__str__",  util::to_string<cell_parameters>);
    
        // Wrap cable cell description type.
        // Cable cells are going to be replaced with a saner API, so we don't go
        // adding much in the way of interface. Instead we just provide a helper
        // that will generate random cell morphologies for benchmarking.
        pybind11::class_<arb::cable_cell> cable_cell(m, "cable_cell");
    
        cable_cell
            .def("__repr__", [](const arb::cable_cell&){return "<arbor.cable_cell>";})
            .def("__str__",  [](const arb::cable_cell&){return "<arbor.cable_cell>";});
    
        m.def("make_cable_cell", &make_cable_cell,
            "Construct a branching cell with a random morphology and synapse end points locations described by params.\n"
            "seed is an integral value used to seed the random number generator, for which the gid of the cell is a good default.",
            "seed"_a,
            "params"_a=cell_parameters());
    }
    
    }