Skip to content

Commit e479f6a

Browse files
authored
Support for FOR_NETCON (BlueBrain/CoreNeuron#355)
* Framework to support FOR_NETCONS construct with CORENEURON. * Update correct mod2c master with necessary support * Implement setup_fornetcon_info * Remove _type redefine macro to avid issues with compile like GCC v8.3 * docs target are not added if coreneuron is used as submodule * After embedded simulation, copy weights back to NEURON NetCon. Co-authored-by: Alexandru Savulescu <[email protected]> Co-authored-by: pramodk <[email protected]> CoreNEURON Repo SHA: BlueBrain/CoreNeuron@3eda428
1 parent 5ce4369 commit e479f6a

File tree

13 files changed

+320
-52
lines changed

13 files changed

+320
-52
lines changed

cmake/coreneuron/CMakeLists.txt

Lines changed: 46 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -301,54 +301,55 @@ else()
301301
set(COMPILER_FLAGS "${CMAKE_CXX_FLAGS_${BUILD_TYPE_UPPER}}")
302302
endif()
303303

304-
# =============================================================================
305-
# Setup Doxygen documentation
306-
# =============================================================================
307-
find_package(Doxygen QUIET)
308-
if(DOXYGEN_FOUND)
309-
# generate Doxyfile with correct source paths
310-
configure_file(${PROJECT_SOURCE_DIR}/docs/Doxyfile.in ${PROJECT_BINARY_DIR}/Doxyfile)
311-
add_custom_target(
312-
doxygen
313-
COMMAND ${DOXYGEN_EXECUTABLE} ${PROJECT_BINARY_DIR}/Doxyfile
314-
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
315-
COMMENT "Generating API documentation with Doxygen"
316-
VERBATIM)
317-
endif()
318-
319-
# =============================================================================
320-
# Setup Sphinx documentation
321-
# =============================================================================
322-
find_package(Sphinx QUIET)
323-
if(SPHINX_FOUND)
324-
set(SPHINX_SOURCE ${PROJECT_SOURCE_DIR}/docs)
325-
set(SPHINX_BUILD ${PROJECT_BINARY_DIR}/docs/)
304+
if(NOT CORENEURON_AS_SUBPROJECT)
305+
# =============================================================================
306+
# Setup Doxygen documentation
307+
# =============================================================================
308+
find_package(Doxygen QUIET)
309+
if(DOXYGEN_FOUND)
310+
# generate Doxyfile with correct source paths
311+
configure_file(${PROJECT_SOURCE_DIR}/docs/Doxyfile.in ${PROJECT_BINARY_DIR}/Doxyfile)
312+
add_custom_target(
313+
doxygen
314+
COMMAND ${DOXYGEN_EXECUTABLE} ${PROJECT_BINARY_DIR}/Doxyfile
315+
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
316+
COMMENT "Generating API documentation with Doxygen"
317+
VERBATIM)
318+
endif()
326319

327-
add_custom_target(
328-
sphinx
329-
COMMAND ${SPHINX_EXECUTABLE} -b html ${SPHINX_SOURCE} ${SPHINX_BUILD}
330-
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
331-
COMMENT "Generating documentation with Sphinx")
332-
endif()
320+
# =============================================================================
321+
# Setup Sphinx documentation
322+
# =============================================================================
323+
find_package(Sphinx QUIET)
324+
if(SPHINX_FOUND)
325+
set(SPHINX_SOURCE ${PROJECT_SOURCE_DIR}/docs)
326+
set(SPHINX_BUILD ${PROJECT_BINARY_DIR}/docs/)
327+
328+
add_custom_target(
329+
sphinx
330+
COMMAND ${SPHINX_EXECUTABLE} -b html ${SPHINX_SOURCE} ${SPHINX_BUILD}
331+
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
332+
COMMENT "Generating documentation with Sphinx")
333+
endif()
333334

334-
# =============================================================================
335-
# Build full docs
336-
# =============================================================================
337-
if(DOXYGEN_FOUND AND SPHINX_FOUND)
338-
add_custom_target(
339-
docs
340-
COMMAND make doxygen
341-
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
342-
COMMAND make sphinx
343-
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
344-
COMMENT "Generating full documentation")
345-
else()
346-
add_custom_target(
347-
docs VERBATIM
348-
COMMAND echo "Please install docs requirements (see docs/README.md)!"
349-
COMMENT "Documentation generation not possible!")
335+
# =============================================================================
336+
# Build full docs
337+
# =============================================================================
338+
if(DOXYGEN_FOUND AND SPHINX_FOUND)
339+
add_custom_target(
340+
docs
341+
COMMAND make doxygen
342+
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
343+
COMMAND make sphinx
344+
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
345+
COMMENT "Generating full documentation")
346+
else()
347+
add_custom_target(
348+
docs VERBATIM
349+
COMMAND echo "Please install docs requirements (see docs/README.md)!"
350+
COMMENT "Documentation generation not possible!")
351+
endif()
350352
endif()
351-
352353
# =============================================================================
353354
# Build status
354355
# =============================================================================

external/mod2c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
Subproject commit cf9f571b06ef7c44dfe267a53c5bbc3fc70ca58c
1+
Subproject commit 2b3ff22c4a785db174e915dc36a9ead5543ba673

src/coreneuron/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ if(CORENRN_ENABLE_GPU)
106106
${CMAKE_CURRENT_BINARY_DIR}/pattern.cpp
107107
${CMAKE_CURRENT_SOURCE_DIR}/utils/randoms/nrnran123.cpp
108108
${CMAKE_CURRENT_SOURCE_DIR}/io/nrn_setup.cpp
109+
${CMAKE_CURRENT_SOURCE_DIR}/io/setup_fornetcon.cpp
109110
${CMAKE_CURRENT_SOURCE_DIR}/io/global_vars.cpp)
110111

111112
set_source_files_properties(${DIMPLIC_CODE_FILE} ${NMODL_INBUILT_MOD_OUTPUTS} PROPERTIES

src/coreneuron/apps/main1.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -593,6 +593,16 @@ extern "C" int run_solve_core(int argc, char** argv) {
593593
output_spikes(output_dir.c_str(), spikes_population_name);
594594
}
595595

596+
// copy weights back to NEURON NetCon
597+
if (nrn2core_all_weights_return_) {
598+
std::vector<double*> weights(nrn_nthread, NULL);
599+
// could be one thread more (empty) than in NEURON but does not matter
600+
for (int i=0; i < nrn_nthread; ++i) {
601+
weights[i] = nrn_threads[i].weights;
602+
}
603+
(*nrn2core_all_weights_return_)(weights);
604+
}
605+
596606
{
597607
Instrumentor::phase p("checkpoint");
598608
write_checkpoint(nrn_threads, nrn_nthread, corenrn_param.checkpointpath.c_str());

src/coreneuron/io/nrn2core_direct.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,10 @@ extern void (*nrn2core_trajectory_return_)(int tid, int n_pr, int vecsz, void**
109109

110110
/* send all spikes vectors to NEURON */
111111
extern int (*nrn2core_all_spike_vectors_return_)(std::vector<double>& spikevec, std::vector<int>& gidvec);
112-
}
112+
113+
/* send all weights to NEURON */
114+
extern void (*nrn2core_all_weights_return_)(std::vector<double*>& weights);
115+
116+
} // extern "C"
113117

114118
#endif /* nrn2core_direct_h */

src/coreneuron/io/nrn_setup.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ void (*nrn2core_trajectory_return_)(int tid, int n_pr, int vecsz, void** vpr, do
9191

9292
int (*nrn2core_all_spike_vectors_return_)(std::vector<double>& spikevec, std::vector<int>& gidvec);
9393

94+
void (*nrn2core_all_weights_return_)(std::vector<double*>& weights);
95+
9496
// file format defined in cooperation with nrncore/src/nrniv/nrnbbcore_write.cpp
9597
// single integers are ascii one per line. arrays are binary int or double
9698
// Note that regardless of the gid contents of a group, since all gids are

src/coreneuron/io/phase2.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "coreneuron/permute/node_permute.h"
99
#include "coreneuron/utils/vrecitem.h"
1010
#include "coreneuron/io/mem_layout_util.hpp"
11+
#include "coreneuron/io/setup_fornetcon.hpp"
1112

1213
int (*nrn2core_get_dat2_1_)(int tid,
1314
int& ngid,
@@ -708,6 +709,10 @@ void Phase2::handle_weights(NrnThread& nt, int n_netcon) {
708709
}
709710
assert(iw == nt.n_weight);
710711

712+
// Nontrivial if FOR_NETCON in use by some mechanisms
713+
setup_fornetcon_info(nt);
714+
715+
711716
#if CHKPNTDEBUG
712717
ntc.delay = new double[n_netcon];
713718
memcpy(ntc.delay, delay, n_netcon * sizeof(double));

src/coreneuron/io/setup_fornetcon.cpp

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
/*
2+
Copyright (c) 2020, Blue Brain Project
3+
All rights reserved.
4+
5+
Redistribution and use in source and binary forms, with or without modification,
6+
are permitted provided that the following conditions are met:
7+
1. Redistributions of source code must retain the above copyright notice,
8+
this list of conditions and the following disclaimer.
9+
2. Redistributions in binary form must reproduce the above copyright notice,
10+
this list of conditions and the following disclaimer in the documentation
11+
and/or other materials provided with the distribution.
12+
3. Neither the name of the copyright holder nor the names of its contributors
13+
may be used to endorse or promote products derived from this software
14+
without specific prior written permission.
15+
16+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19+
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20+
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21+
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22+
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23+
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24+
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25+
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
26+
THE POSSIBILITY OF SUCH DAMAGE.
27+
*/
28+
29+
#include "coreneuron/coreneuron.hpp"
30+
#include "coreneuron/io/setup_fornetcon.hpp"
31+
#include "coreneuron/network/netcon.hpp"
32+
#include "coreneuron/nrniv/nrniv_decl.h"
33+
#include <map>
34+
35+
namespace coreneuron {
36+
37+
/**
38+
If FOR_NETCON in use, setup NrnThread fornetcon related info.
39+
40+
i.e NrnThread._fornetcon_perm_indices, NrnThread._fornetcon_weight_perm,
41+
and the relevant dparam element of each mechanism instance that uses
42+
a FOR_NETCONS statement.
43+
44+
Makes use of nrn_fornetcon_cnt_, nrn_fornetcon_type_,
45+
and nrn_fornetcon_index_ that were specified during registration of
46+
mechanisms that use FOR_NETCONS.
47+
48+
nrn_fornetcon_cnt_ is the number of mechanisms that use FOR_NETCONS,
49+
nrn_fornetcon_type_ is an int array of size nrn_fornetcon_cnt, that specifies
50+
the mechanism type.
51+
nrn_fornetcon_index_ is an int array of size nrn_fornetcon_cnt, that
52+
specifies the index into an instance's dparam int array having the
53+
fornetcon semantics.
54+
55+
FOR_NETCONS (args) means to loop over all NetCon connecting to this
56+
target instance and args are the names of the items of each NetCon's
57+
weight vector (same as the enclosing NET_RECEIVE but possible different
58+
local names).
59+
60+
NrnThread._weights is a vector of weight groups where the number of groups
61+
is the number of NetCon in this thread and each group has a size
62+
equal to the number of args in the target NET_RECEIVE block. The order
63+
of these groups is the NetCon Object order in HOC (the construction order).
64+
So the weight vector indices for the NetCons in the FOR_NETCONS loop
65+
are not adjacent.
66+
67+
NrnThread._fornetcon_weight_perm is an index vector into the
68+
NrnThread._weight vector such that the list of indices that targets a
69+
mechanism instance are adjacent.
70+
NrnThread._fornetcon_perm_indices is an index vector into the
71+
NrnThread._fornetcon_weight_perm to the first of the list of NetCon weights
72+
that target the instance. The index of _fornetcon_perm_indices
73+
containing this first in the list is stored in the mechanism instances
74+
dparam at the dparam's semantic fornetcon slot. (Note that the next index
75+
points to the first index of the next target instance.)
76+
77+
**/
78+
79+
static int* fornetcon_slot(const int mtype, const int instance,
80+
const int fnslot, const NrnThread& nt)
81+
{
82+
int layout = corenrn.get_mech_data_layout()[mtype];
83+
int sz = corenrn.get_prop_dparam_size()[mtype];
84+
Memb_list* ml = nt._ml_list[mtype];
85+
int* fn = nullptr;
86+
if (layout == 1) { /* AoS */
87+
fn = ml->pdata + (instance*sz + fnslot);
88+
}else if (layout == 0) { /* SoA */
89+
int padded_cnt = nrn_soa_padded_size(ml->nodecount, layout);
90+
fn = ml->pdata + (fnslot*padded_cnt + instance);
91+
}
92+
return fn;
93+
}
94+
95+
void setup_fornetcon_info(NrnThread& nt) {
96+
97+
if (nrn_fornetcon_cnt_ == 0) { return; }
98+
99+
// Mechanism types in use that have FOR_NETCONS statements
100+
// Nice to have the dparam fornetcon slot as well so use map
101+
// instead of set
102+
std::map<int, int> type_to_slot;
103+
for (int i = 0; i < nrn_fornetcon_cnt_; ++i) {
104+
int type = nrn_fornetcon_type_[i];
105+
Memb_list* ml = nt._ml_list[type];
106+
if (ml && ml->nodecount) {
107+
type_to_slot[type] = nrn_fornetcon_index_[i];
108+
}
109+
}
110+
if (type_to_slot.empty()) {
111+
return;
112+
}
113+
114+
// How many NetCons (weight groups) are involved.
115+
// Also count how many weight groups for each target instance.
116+
// For the latter we can count in the dparam fornetcon slot.
117+
118+
// zero the dparam fornetcon slot for counting and count number of slots.
119+
size_t n_perm_indices = 0;
120+
for (const auto& kv: type_to_slot) {
121+
int mtype = kv.first;
122+
int fnslot = kv.second;
123+
int nodecount = nt._ml_list[mtype]->nodecount;
124+
for (int i=0; i < nodecount; ++i) {
125+
int* fn = fornetcon_slot(mtype, i, fnslot, nt);
126+
*fn = 0;
127+
n_perm_indices += 1;
128+
}
129+
}
130+
131+
// Count how many weight groups for each slot and total number of weight groups
132+
size_t n_weight_perm = 0;
133+
for (int i = 0; i < nt.n_netcon; ++i) {
134+
NetCon& nc = nt.netcons[i];
135+
int mtype = nc.target_->_type;
136+
auto search = type_to_slot.find(mtype);
137+
if (search != type_to_slot.end()) {
138+
int i_instance = nc.target_->_i_instance;
139+
int* fn = fornetcon_slot(mtype, i_instance, search->second, nt);
140+
*fn += 1;
141+
n_weight_perm += 1;
142+
}
143+
}
144+
145+
// Displacement vector has an extra element since the number for last item
146+
// at n-1 is x[n] - x[n-1] and number for first is x[0] = 0.
147+
nt._fornetcon_perm_indices.resize(n_perm_indices + 1);
148+
nt._fornetcon_weight_perm.resize(n_weight_perm);
149+
150+
// From dparam fornetcon slots, compute displacement vector, and
151+
// set the dparam fornetcon slot to the index of the displacement vector
152+
// to allow later filling the _fornetcon_weight_perm.
153+
size_t i_perm_indices = 0;
154+
nt._fornetcon_perm_indices[0] = 0;
155+
for (const auto& kv: type_to_slot) {
156+
int mtype = kv.first;
157+
int fnslot = kv.second;
158+
int nodecount = nt._ml_list[mtype]->nodecount;
159+
for (int i=0; i < nodecount; ++i) {
160+
int* fn = fornetcon_slot(mtype, i, fnslot, nt);
161+
nt._fornetcon_perm_indices[i_perm_indices + 1] =
162+
nt._fornetcon_perm_indices[i_perm_indices] + size_t(*fn);
163+
*fn = int(nt._fornetcon_perm_indices[i_perm_indices]);
164+
i_perm_indices += 1;
165+
}
166+
}
167+
168+
// One more iteration over NetCon to fill in weight index for
169+
// nt._fornetcon_weight_perm. To help with this we increment the
170+
// dparam fornetcon slot on each use.
171+
for (int i = 0; i < nt.n_netcon; ++i) {
172+
NetCon& nc = nt.netcons[i];
173+
int mtype = nc.target_->_type;
174+
auto search = type_to_slot.find(mtype);
175+
if (search != type_to_slot.end()) {
176+
int i_instance = nc.target_->_i_instance;
177+
int* fn = fornetcon_slot(mtype, i_instance, search->second, nt);
178+
size_t nc_w_index = size_t(nc.u.weight_index_);
179+
nt._fornetcon_weight_perm[size_t(*fn)] = nc_w_index;
180+
*fn += 1; // next item conceptually adjacent
181+
}
182+
}
183+
184+
// Put back the proper values into the dparam fornetcon slot
185+
i_perm_indices = 0;
186+
for (const auto& kv: type_to_slot) {
187+
int mtype = kv.first;
188+
int fnslot = kv.second;
189+
int nodecount = nt._ml_list[mtype]->nodecount;
190+
for (int i=0; i < nodecount; ++i) {
191+
int* fn = fornetcon_slot(mtype, i, fnslot, nt);
192+
*fn = int(i_perm_indices);
193+
i_perm_indices += 1;
194+
}
195+
}
196+
}
197+
198+
} // namespace coreneuron

0 commit comments

Comments
 (0)