Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New policy 'round_robin_halt' #1868

Merged
merged 11 commits into from
Jun 14, 2022
2 changes: 2 additions & 0 deletions arbor/common_types_io.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ ARB_ARBOR_API std::ostream& operator<<(std::ostream& o, lid_selection_policy pol
switch (policy) {
case lid_selection_policy::round_robin:
return o << "round_robin";
case lid_selection_policy::round_robin_halt:
return o << "round_robin_halt";
case lid_selection_policy::assert_univalent:
return o << "univalent";
}
Expand Down
1 change: 1 addition & 0 deletions arbor/include/arbor/common_types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ struct lid_range {

enum class lid_selection_policy {
round_robin,
round_robin_halt,
assert_univalent // throw if the range of possible lids is wider than 1
};

Expand Down
51 changes: 45 additions & 6 deletions arbor/label_resolution.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,6 @@ lid_hopefully label_resolution_map::range_set::at(unsigned idx) const {
// Offset into the range containing idx.
const auto& range_part = part.at(ridx);
auto offset = idx - range_part.first;

return start + offset;
}

Expand Down Expand Up @@ -126,6 +125,19 @@ lid_hopefully round_robin_state::update(const label_resolution_map::range_set& r
return lid;
}

cell_lid_type round_robin_state::get() {
return state;
}

lid_hopefully round_robin_halt_state::update(const label_resolution_map::range_set& range_set) {
auto lid = range_set.at(state);
return lid;
}

cell_lid_type round_robin_halt_state::get() {
return state;
}

lid_hopefully assert_univalent_state::update(const label_resolution_map::range_set& range_set) {
if (range_set.size() != 1) {
return util::unexpected("range is not univalent");
Expand All @@ -134,11 +146,29 @@ lid_hopefully assert_univalent_state::update(const label_resolution_map::range_s
return range_set.at(0);
}

cell_lid_type assert_univalent_state::get() {
return 0;
}

// resolver methods
resolver::state_variant resolver::construct_state(lid_selection_policy pol) {
switch (pol) {
case lid_selection_policy::round_robin:
return round_robin_state();
case lid_selection_policy::round_robin_halt:
return round_robin_halt_state();
case lid_selection_policy::assert_univalent:
return assert_univalent_state();
default: return assert_univalent_state();
}
}

resolver::state_variant resolver::construct_state(lid_selection_policy pol, cell_lid_type state) {
switch (pol) {
case lid_selection_policy::round_robin:
return round_robin_state(state);
case lid_selection_policy::round_robin_halt:
return round_robin_halt_state(state);
case lid_selection_policy::assert_univalent:
return assert_univalent_state();
default: return assert_univalent_state();
Expand All @@ -151,15 +181,24 @@ cell_lid_type resolver::resolve(const cell_global_label_type& iden) {
}
const auto& range_set = label_map_->at(iden.gid, iden.label.tag);

// Construct state if if doesn't exist
if (!state_map_[iden.gid][iden.label.tag].count(iden.label.policy)) {
state_map_[iden.gid][iden.label.tag][iden.label.policy] = construct_state(iden.label.policy);
}

// Policy round_robin_halt: use previous state of round_robin policy, if existent
if (iden.label.policy == lid_selection_policy::round_robin_halt
&& state_map_[iden.gid][iden.label.tag].count(lid_selection_policy::round_robin)) {
cell_lid_type prev_state_rr = std::visit([range_set](auto& state) { return state.get(); }, state_map_[iden.gid][iden.label.tag][lid_selection_policy::round_robin]);
state_map_[iden.gid][iden.label.tag][lid_selection_policy::round_robin_halt] = construct_state(lid_selection_policy::round_robin_halt, prev_state_rr);
}

// Construct state if it doesn't exist
if (!state_map_[iden.gid][iden.label.tag].count(iden.label.policy)) {
state_map_[iden.gid][iden.label.tag][iden.label.policy] = construct_state(iden.label.policy);
}

// Update state
auto lid = std::visit([range_set](auto& state) { return state.update(range_set); }, state_map_[iden.gid][iden.label.tag][iden.label.policy]);
if (!lid) {
throw arb::bad_connection_label(iden.gid, iden.label.tag, lid.error());
}

return lid.value();
}

Expand Down
15 changes: 13 additions & 2 deletions arbor/label_resolution.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,13 +88,23 @@ class ARB_ARBOR_API label_resolution_map {
};

struct ARB_ARBOR_API round_robin_state {
cell_size_type state = 0;
cell_lid_type state = 0;
round_robin_state() : state(0) {};
round_robin_state(cell_lid_type state) : state(state) {};
cell_lid_type get();
lid_hopefully update(const label_resolution_map::range_set& range);
};

struct ARB_ARBOR_API round_robin_halt_state {
cell_lid_type state = 0;
round_robin_halt_state() : state(0) {};
round_robin_halt_state(cell_lid_type state) : state(state) {};
cell_lid_type get();
lid_hopefully update(const label_resolution_map::range_set& range);
};

struct ARB_ARBOR_API assert_univalent_state {
cell_lid_type get();
lid_hopefully update(const label_resolution_map::range_set& range);
};

Expand All @@ -105,9 +115,10 @@ struct ARB_ARBOR_API resolver {
cell_lid_type resolve(const cell_global_label_type& iden);

private:
using state_variant = std::variant<round_robin_state, assert_univalent_state>;
using state_variant = std::variant<round_robin_state, round_robin_halt_state, assert_univalent_state>;

state_variant construct_state(lid_selection_policy pol);
state_variant construct_state(lid_selection_policy pol, cell_lid_type state);

const label_resolution_map* label_map_;
std::unordered_map<cell_gid_type, std::unordered_map<cell_tag_type, std::unordered_map <lid_selection_policy, state_variant>>> state_map_;
Expand Down
4 changes: 4 additions & 0 deletions doc/cpp/cell.rst
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ cells and members of cell-local collections.

Iterate over the items of the group in a round-robin fashion.

.. cpp:enumerator:: round_robin_halt

Halts at the current item of the group until the round_robin policy is called (again).

.. cpp:enumerator:: assert_univalent

Assert that ony one item is available in the group. Throws an exception if the assertion
Expand Down
4 changes: 4 additions & 0 deletions doc/python/cell.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ The types defined below are used as identifiers for cells and members of cell-lo

Iterate over the items of the group in a round-robin fashion.

.. attribute:: round_robin_halt

Halts at the current item of the group until the round_robin policy is called (again).

.. attribute:: univalent

Assert that only one item is available in the group. Throws an exception if the assertion
Expand Down
2 changes: 1 addition & 1 deletion mechanisms/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ make_catalogue(
NAME default
SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/default"
OUTPUT "CAT_DEFAULT_SOURCES"
MOD exp2syn expsyn expsyn_stdp hh kamt kdrmt nax nernst pas gj
MOD exp2syn expsyn expsyn_curr expsyn_stdp hh kamt kdrmt nax nernst pas gj
CXX
PREFIX "${PROJECT_SOURCE_DIR}/mechanisms"
CXX_FLAGS_TARGET "${ARB_CXX_FLAGS_TARGET_FULL}"
Expand Down
47 changes: 47 additions & 0 deletions mechanisms/default/expsyn_curr.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
: Exponential current-based synapse

NEURON {
POINT_PROCESS expsyn_curr
RANGE w, tau, R_mem
NONSPECIFIC_CURRENT I
}

UNITS {
(ms) = (milliseconds)
(mV) = (millivolt)
(MOhm) = (megaohm)
}

PARAMETER {
R_mem = 10.0 (MOhm) : membrane resistance
tau = 5.0 (ms) : synaptic time constant
w = 4.20075 (mV) : weight
}

STATE {
g (mV) : instantaneous synaptic conductance
}

INITIAL {
g = 0
}

BREAKPOINT {
:SOLVE state METHOD cnexp
SOLVE state METHOD sparse : to match with expsyn_curr_calcium_plasticity

I = -g / R_mem
}

DERIVATIVE state {
: Exponential decay of postsynaptic potential
g' = -g / tau
}

NET_RECEIVE(weight) {
if (weight >= 0) {
: Start of postsynaptic potential
g = g + w
}
}

4 changes: 3 additions & 1 deletion python/identifiers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ void register_identifiers(py::module& m) {
py::enum_<arb::lid_selection_policy>(m, "selection_policy",
"Enumeration used to identify a selection policy, used by the model for selecting one of possibly multiple locations on the cell associated with a labeled item.")
.value("round_robin", arb::lid_selection_policy::round_robin,
"iterate round-robin over all possible locations.")
"Iterate round-robin over all possible locations.")
.value("round_robin_halt", arb::lid_selection_policy::round_robin_halt,
"Halts at the current location until the round_robin policy is called (again).")
.value("univalent", arb::lid_selection_policy::assert_univalent,
"Assert that there is only one possible location associated with a labeled item on the cell. The model throws an exception if the assertion fails.");

Expand Down
71 changes: 42 additions & 29 deletions python/test/fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,34 +140,10 @@ class empty_recipe(arbor.recipe):
"""
pass


@_fixture
def cable_cell():
# (1) Create a morphology with a single (cylindrical) segment of length=diameter=6 μm
tree = arbor.segment_tree()
tree.append(
arbor.mnpos,
arbor.mpoint(-3, 0, 0, 3),
arbor.mpoint(3, 0, 0, 3),
tag=1,
)

# (2) Define the soma and its midpoint
labels = arbor.label_dict({'soma': '(tag 1)',
'midpoint': '(location 0 0.5)'})

# (3) Create cell and set properties
decor = arbor.decor()
decor.set_property(Vm=-40)
decor.paint('"soma"', arbor.density('hh'))
decor.place('"midpoint"', arbor.iclamp( 10, 2, 0.8), "iclamp")
decor.place('"midpoint"', arbor.spike_detector(-10), "detector")
return arbor.cable_cell(tree, labels, decor)

@_fixture
class art_spiker_recipe(arbor.recipe):
"""
Recipe fixture with 3 artificial spiking cells.
Recipe fixture with 3 artificial spiking cells and one cable cell.
"""
def __init__(self):
super().__init__()
Expand Down Expand Up @@ -201,15 +177,52 @@ def probes(self, gid):
else:
return [arbor.cable_probe_membrane_voltage('"midpoint"')]

@cable_cell
def _cable_cell(self, cable_cell):
return cable_cell
def _cable_cell_elements(self):
# (1) Create a morphology with a single (cylindrical) segment of length=diameter=6 μm
tree = arbor.segment_tree()
tree.append(
arbor.mnpos,
arbor.mpoint(-3, 0, 0, 3),
arbor.mpoint(3, 0, 0, 3),
tag=1,
)

# (2) Define the soma and its midpoint
labels = arbor.label_dict({'soma': '(tag 1)',
'midpoint': '(location 0 0.5)'})

# (3) Create cell and set properties
decor = arbor.decor()
decor.set_property(Vm=-40)
decor.paint('"soma"', arbor.density('hh'))
decor.place('"midpoint"', arbor.iclamp( 10, 2, 0.8), "iclamp")
decor.place('"midpoint"', arbor.spike_detector(-10), "detector")

# return tuple of tree, labels, and decor for creating a cable cell (can still be modified before calling arbor.cable_cell())
return tree, labels, decor

def cell_description(self, gid):
if gid < 3:
return arbor.spike_source_cell("src", arbor.explicit_schedule(self.trains[gid]))
else:
return self._cable_cell()
tree, labels, decor = self._cable_cell_elements()
return arbor.cable_cell(tree, labels, decor)

@_fixture
def sum_weight_hh_spike():
"""
Fixture returning connection weight for 'expsyn_stdp' mechanism which is just enough to evoke an immediate spike
at t=1ms in the 'hh' neuron in 'art_spiker_recipe'
"""
return 0.4

@_fixture
def sum_weight_hh_spike_2():
"""
Fixture returning connection weight for 'expsyn_stdp' mechanism which is just enough to evoke an immediate spike
at t=1.8ms in the 'hh' neuron in 'art_spiker_recipe'
"""
return 0.36

@_fixture
@context
Expand Down
Loading