Skip to content

Instantly share code, notes, and snippets.

@llandsmeer
Last active November 16, 2022 23:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save llandsmeer/ba41405d81bf5d5270685e12fd75895d to your computer and use it in GitHub Desktop.
Save llandsmeer/ba41405d81bf5d5270685e12fd75895d to your computer and use it in GitHub Desktop.
Class-based custom mechanisms for arbor in python and C++ (DONT TRY THIS AT HOME!)
import sys
sys.path.insert(0, '/home/llandsmeer/repos/llandsmeer/arbor/build-debug/prefix/lib/python3.10/site-packages')
sys.path.append('/home/llandsmeer/repos/llandsmeer/nmlcc-cat')
import arbor
import matplotlib.pyplot as plt
import nmlcat
cat = nmlcat.Catalogue.make()
cat.add('one')
cat.add('two')
tree = arbor.segment_tree()
tree.append(arbor.mnpos, arbor.mpoint(0, 0, 0, 1), arbor.mpoint(1, 0, 0, 1), tag=1)
labels = arbor.label_dict()
decor = arbor.decor() \
.paint('(all)', arbor.density('one')) \
.paint('(all)', arbor.density('two'))
cell = arbor.cable_cell(tree, decor, labels)
m = arbor.single_cell_model(cell)
m.properties.catalogue.extend(cat.load(), '')
m.probe('voltage', '(root)', frequency=10)
m.run(tfinal=1, dt=0.5)
print(m.traces[0].value)
plt.plot(m.traces[0].time, m.traces[0].value)
plt.show()
#include <iostream>
#include <dlfcn.h>
#include <memory>
#include <arbor/mechcat.hpp>
#include <arbor/mechanism.hpp>
#ifdef PYTHON
#include <pybind11/embed.h>
#include <pybind11/pybind11.h>
#endif
class Mechanism;
class Catalogue {
friend class Mechanism;
std::vector<std::shared_ptr<Mechanism>> mechs; /* shared doesn't make sense here but whatever */
std::vector<arb_mechanism> mechs_for_arbor;
Catalogue(int cat_id_);
int cat_id;
inline static std::vector<std::shared_ptr<Catalogue>> catalogues; /* we can never delete! */
inline static int _counter_current_mech_based_on_counter_during_init = 0;
static std::shared_ptr<Mechanism> _get_current_mech_based_on_counter_during_init() {
int idx = _counter_current_mech_based_on_counter_during_init / 2;
_counter_current_mech_based_on_counter_during_init += 1;
return current->mechs[idx];
}
inline static Catalogue * current = 0;
public:
void set_as_current_for_init() {
current = this;
_counter_current_mech_based_on_counter_during_init = 0;
}
static Catalogue * get_current_during_init() {
return current;
}
std::shared_ptr<Mechanism> add(std::string name);
static std::shared_ptr<Catalogue> make() {
int cat_id = catalogues.size();
catalogues.emplace_back(std::shared_ptr<Catalogue>(new Catalogue(cat_id)));
return catalogues[catalogues.size()-1];
}
void * get_catalogue(int * n);
#ifdef PYTHON
pybind11::object load();
#else
arb::mechanism_catalogue load();
#endif
Catalogue (const Catalogue&) = delete;
Catalogue& operator= (const Catalogue&) = delete;
};
class Mechanism {
friend class Catalogue;
arb_mechanism_interface cpu;
std::string name = "my_bytecode_mechanism";
std::vector<arb_field_info> globals;
std::vector<arb_field_info> state_vars;
std::vector<arb_field_info> parameters;
std::vector<arb_ion_info> ions;
static Mechanism * current(arb_mechanism_ppack * pp) {
/* we put a global in pp with a index to our mechanism */
int cat_id = (int)pp->globals[0];
int mech_id = (int)pp->globals[1];
return Catalogue::catalogues.at(cat_id)->mechs.at(mech_id).get();
}
/* during initilization */
static arb_mechanism_type global_make_type_callback() {
return Catalogue::_get_current_mech_based_on_counter_during_init()->type();
}
static arb_mechanism_interface * global_make_cpu_callback() {
return &Catalogue::_get_current_mech_based_on_counter_during_init()->cpu;
}
static arb_mechanism_interface * global_make_gpu_callback() { return 0; }
/* during integration: */
static void global_init_callback(arb_mechanism_ppack * pp) { current(pp)->init(pp); }
static void global_compute_currents_callback(arb_mechanism_ppack * pp) { current(pp)->compute_currents(pp); }
static void global_advance_state_callback(arb_mechanism_ppack * pp) { current(pp)->advance_state(pp); }
static void global_write_ions_callback(arb_mechanism_ppack * pp) { current(pp)->write_ions(pp); }
static void global_apply_events_callback(arb_mechanism_ppack * pp, arb_deliverable_event_stream * e) { current(pp)->apply_events(pp, e); }
static void global_post_event_callback(arb_mechanism_ppack * pp) { current(pp)->post_event(pp); }
arb_mechanism_type type() {
arb_mechanism_type type;
type.abi_version = ARB_MECH_ABI_VERSION;
type.fingerprint = "<placeholder>";
type.name = name.c_str();
type.kind = arb_mechanism_kind_density;
type.is_linear = false;
type.has_post_events = false;
type.globals = globals.data();
type.n_globals = globals.size();
type.state_vars = state_vars.data();
type.n_state_vars = state_vars.size();
type.parameters = parameters.data();
type.n_parameters = parameters.size();
type.ions = ions.data();
type.n_ions = ions.size();
type.n_random_variables = 0;
return type;
}
Mechanism(int cat_id, int mech_id, std::string _name = "") : name(_name) {
cpu.partition_width = 1;
cpu.backend = arb_backend_kind_cpu;
cpu.alignment = 8;
cpu.init_mechanism = global_init_callback;
cpu.compute_currents = global_compute_currents_callback;
cpu.apply_events = global_apply_events_callback;
cpu.advance_state = global_advance_state_callback;
cpu.write_ions = global_write_ions_callback;
cpu.post_event = global_post_event_callback;
/* do ugly thing with globals */
arb_value_type arb_cat_id = cat_id;
arb_value_type arb_mech_id = mech_id;
globals.emplace_back("cat_id", "", arb_cat_id, arb_cat_id, arb_cat_id);
globals.emplace_back("mech_id", "", arb_mech_id, arb_mech_id, arb_mech_id);
}
arb_mechanism make() {
arb_mechanism result;
result.type = global_make_type_callback;
result.i_cpu = global_make_cpu_callback;
result.i_gpu = global_make_gpu_callback;
return result;
}
public:
void init(arb_mechanism_ppack * pp) {
std::cout << name << "::init()" << std::endl;
(void)pp;
}
void compute_currents(arb_mechanism_ppack * pp) {
std::cout << name << "::compute_currents()" << std::endl;
(void)pp;
}
void advance_state(arb_mechanism_ppack * pp) {
std::cout << name << "::advance_state()" << std::endl;
(void)pp;
}
void write_ions(arb_mechanism_ppack * pp) {
std::cout << name << "::write_ions()" << std::endl;
(void)pp;
}
void apply_events(arb_mechanism_ppack * pp, arb_deliverable_event_stream * e) {
std::cout << name << "::apply_events()" << std::endl;
(void)pp;
(void)e;
}
void post_event(arb_mechanism_ppack * pp) {
std::cout << name << "::post_event()" << std::endl;
(void)pp;
}
Mechanism (const Mechanism&) = delete;
Mechanism& operator= (const Mechanism&) = delete;
};
Catalogue::Catalogue(int cat_id_) : cat_id(cat_id_) {
}
std::shared_ptr<Mechanism> Catalogue::add(std::string name) {
int mech_id = mechs.size();
auto m = std::shared_ptr<Mechanism>(new Mechanism(cat_id, mech_id, name));
mechs.push_back(m);
return m;
}
void * Catalogue::get_catalogue(int * n) {
if (mechs_for_arbor.size() == 0) {
for (auto & m : mechs) {
mechs_for_arbor.push_back(m->make());
}
}
*n = mechs_for_arbor.size();
return mechs_for_arbor.data();
}
#ifndef PYTHON
arb::mechanism_catalogue Catalogue::load() {
arb::mechanism_catalogue result;
for(auto & mech : mechs) {
auto type = mech->type();
result.add(mech->name, type);
result.register_implementation(mech->name, std::make_unique<arb::mechanism>(type, mech->cpu));
}
return result;
}
#endif
#ifdef TEST
int main() {
/*
auto x = Catalogue::make();
auto m = x->add("test");
auto res = x->build();
*/
}
#endif
extern "C" {
void * get_catalogue(int * n) {
Catalogue * c = Catalogue::get_current_during_init();
if (c) {
return c->get_catalogue(n);
} else {
/* this will crash */
*n = 0;
return 0;
}
}
}
#ifdef PYTHON
namespace py = pybind11;
py::object Catalogue::load() {
Dl_info dl_info;
dladdr((const void*)::get_catalogue, &dl_info);
//py::scoped_interpreter guard;
auto arbor = py::module::import("arbor");
set_as_current_for_init();
return arbor.attr("load_catalogue")(dl_info.dli_fname);
}
PYBIND11_MODULE(nmlcat, m) {
py::module arbor = py::module::import("arbor");
py::class_<Mechanism, std::shared_ptr<Mechanism>>(m, "Mechanism")
;
py::class_<Catalogue, std::shared_ptr<Catalogue>>(m, "Catalogue")
.def_static("make", &Catalogue::make)
.def("add", &Catalogue::add)
.def("load", &Catalogue::load)
;
}
#endif
two::init()
one::init()
two::write_ions()
one::write_ions()
two::init()
one::init()
two::apply_events()
two::compute_currents()
one::apply_events()
one::compute_currents()
two::advance_state()
one::advance_state()
two::write_ions()
one::write_ions()
two::apply_events()
two::compute_currents()
one::apply_events()
one::compute_currents()
two::advance_state()
one::advance_state()
two::write_ions()
one::write_ions()
two::apply_events()
two::compute_currents()
one::apply_events()
one::compute_currents()
two::advance_state()
one::advance_state()
two::write_ions()
one::write_ions()
[-65.0, -65.0, -65.0, -65.0, -65.0, -65.0, -65.0, -65.0, -65.0, -65.0]
PREFIX=~/repos/llandsmeer/arbor/build-debug/prefix
SRC=nmlcat.cpp
g++ ${SRC} \
-I "${PREFIX}/include/" \
-std=c++20 \
${PREFIX}/lib/libarbor*.a \
-DTEST \
-g
g++ \
-I "${PREFIX}/include/" \
-g \
-Wall \
-Wextra \
-Werror \
-shared -fPIC \
-std=c++20 \
$(python3 -m pybind11 --includes) \
${SRC} \
-o nmlcat$(python3-config --extension-suffix) \
-DPYTHON \
/home/llandsmeer/.local/lib/python3.10/site-packages/arbor/_arbor.cpython-310-x86_64-linux-gnu.so \
/home/llandsmeer/.local/lib/python3.10/site-packages/arbor.libs/libxml2-3998bec4.so.2.9.1 \
/home/llandsmeer/.local/lib/python3.10/site-packages/arbor.libs/liblzma-004595ca.so.5.2.2
./a.out
python3 main.py
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment