-
Notifications
You must be signed in to change notification settings - Fork 1
/
lookback_connector_model_impl.h
288 lines (231 loc) · 11.3 KB
/
lookback_connector_model_impl.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
#ifndef LOOKBACK_CONNECTOR_MODEL_IMPL
#define LOOKBACK_CONNECTOR_MODEL_IMPL
#include "lookback_connector_model.h"
#include "connector_model_impl.h"
#include <vector>
namespace nest
{
///////////////////////////////////////////////////////////////////////////
// LOOK BACK CONNECTOR MODEL //////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
template <typename ConnectionT>
std::vector<ConnectionT *> LookBackConnectorModel<ConnectionT>::get_conn_ptrs(ConnectorBase *conn_base_in, index syn_id) {
// This function makes the following assumptions:
//
// 1. The conn_base_in pointer is the pointer that is passed into `this->add_connection()`
// or the one returned from `this->GenericConnectorModel<ConnectionT>::add_connection()`
//
// 2. The above basically imples that this is NOT a validated pointer and the LSB's
// still contain the information regarding has_primary and has_secondary (see the
// functions validate_pointer(), has_primary(), and has_secondary() in connector_model.h
//
// 3. Also, since the conn_base_in can be NULL as a result of there being no connection from
// the given source neuron, this case is taken care of.
//
// The following points regarding the structure of a ConnectorBase have been kept in
// mind while designing
//
// 1. ConnectorBase has a different structure depending on whether it is homogeneous or
// heterogeneous. In each case, the appropriate functions handle it.
//
// 2. In all cases. all assumptions of a called function that are stated are met.
// Extract information from 2 LSBs
conn_base_in = validate_pointer(conn_base_in);
if (conn_base_in) {
// in case there is a connector_base
if (conn_base_in->homogeneous_model()) {
// This is the case where the ConnectorBase is a homogenous connector
if (is_matching_syn_id(conn_base_in, syn_id)){
return get_conn_ptrs_hom(conn_base_in);
}
else {
return std::vector<ConnectionT *>();
}
}
else {
// This is the case where the ConnectorBase is a heterogenous connector
// Note that the synid passed to this function represents the type of
// the synapse inserted in add_connection, which is expected to refer to
// the type of LookBackConnectorModel<ConnectionT>
return get_conn_ptrs_het(conn_base_in, syn_id);
}
}
else {
// in case there is no connection of the source node
return std::vector<ConnectionT *>();
}
}
template <typename ConnectionT>
void LookBackConnectorModel<ConnectionT>::update_conn_ptrs(const std::vector<ConnectionT *> &old_conn_ptrs,
const std::vector<ConnectionT *> &new_conn_ptrs) {
// Assumptions:
//
// Regarding Connection Pointers:
//
// 1. Each pointer in `new_conn_ptrs` is a valid pointer pointing to a synapse of
// model LookBackConnectorModel<ConnectionT>. (satisfied by get_conn_ptr)
//
// 2. All pointers in `new_conn_ptrs` point to connections of the above type in a
// single connector. (satisfied by get_conn_ptr)
//
// 3. The pointers in `new_conn_ptrs` having satisfied the above two assumptions
// will basically be pointing to the elements of the vector used to store the
// connections in the connector. It is necessary that should be stored in the
// same order as the elements in the vector. In other words this will imply
// that the values of the pointers will be sequential. (satisfied by get_conn_ptr)
//
// 4. This function is designed to update pointers after the addition of a SINGLE
// connection. Furthermore we assume that there is NO DELETION OF SYNAPSES.
// Combined, this translates to the assumption that
//
// length(new_conn_ptrs) = length(old_conn_ptrs)+1
//
//
// 5. In continuation with 3. and 4. we assume in addition, that there is a one-
// to-one correspondence between old_conn_ptrs[1..end] and new_conn_ptrs[1..end-1].
// This also means that the new synapse to be inserted is at the back of
// new_conn_ptrs.
//
// 6. It is also assumed that the target neuron of the new synapse has already
// been validated to have inherited from LookBackNode<ConnectionT>.
//
assert (old_conn_ptrs.size() + 1 == new_conn_ptrs.size());
// First, we check if the two vectors are identical i.e. there were no
// reallocations. This is done by checking if old_conn_ptrs is empty or they
// have identical first elements. This is sufficient because of 3. 4. & 5.
if (!(old_conn_ptrs.empty() || old_conn_ptrs[0] == new_conn_ptrs[0])) {
// in case they are not, we need to replace the relevant synapses across all
// the neurons to whom these synapses point. As far as thread safety goes, there
// is no need to worry as by definition of connection_manager.connections_,
// all the connections on a single connector (assum 2.) point only to neurons
// that are accessed on the same thread as the current one.
for (auto old_iter=old_conn_ptrs.begin(), new_iter=new_conn_ptrs.begin();
old_iter != old_conn_ptrs.end();
++old_iter, ++new_iter) {
// It is assumed that this will work. assump 1. implies that all pointers here
// are connected using LookBackConnectorModel. This means that their target is
// already validated to have inherited from LookBackNode<ConnectionT>.
auto target_node = dynamic_cast< LookBackNode<ConnectionT>* >(
// Get the Target (Node*) of the connection that is pointed to by the (element
// that is pointed to by new_iter)
(*new_iter)->get_target(kernel().vp_manager.get_thread_id())
);
target_node->replace_inc_synapse(*old_iter, *new_iter);
}
}
// Now, having assumed that the last element of new_conn_ptrs is the new connection, we
// will perform the relevant addition to the set (note assump 6. for reinterpret_cast)
auto val_target_node = dynamic_cast< LookBackNode<ConnectionT>* >(
// Get the Target of the connection that is pointed to by the last
// element of new_conn_ptrs
new_conn_ptrs.back()->get_target(kernel().vp_manager.get_thread_id())
);
val_target_node->add_inc_synapse(new_conn_ptrs.back());
}
template <typename ConnectionT>
LookBackNode<ConnectionT>* LookBackConnectorModel<ConnectionT>::get_validated_neuron(Node *node_in) {
auto val_target_node = dynamic_cast<LookBackNode<ConnectionT>*>(node_in);
if (!val_target_node) {
std::ostringstream msg;
msg << "The node being connected to by a LookBackConnectorModel is not itself derived "
<< "from the appropriate LookBackNode";
throw IllegalConnection(msg.str());
}
return val_target_node;
}
template <typename ConnectionT>
ConnectorBase* LookBackConnectorModel<ConnectionT>::add_connection(Node &src, Node &tgt, ConnectorBase *conn, synindex syn_id,
DictionaryDatum &d, double weight, double delay) {
// Assumptions:
//
// 1. Adding a synapse to a connector will only result in the re-allocation of the
// hom-connector to which the synapse is allocated.
//
// Ensured:
//
// 1. A synapse is connected if and only if the target neuron inherits from LookBackNode<ConnectionT>.
// 2. At each step, all tarrget neurons that have ever been connected to via
// add_connection, possesses valid pointers to the incoming synapses
// First we perform target neuron validation
get_validated_neuron(&tgt);
// Then we read out the vector of old pointers to the connections
std::vector<ConnectionT*> old_conn_ptrs = get_conn_ptrs(conn, syn_id);
// Then we add synapse by calling super function from GenericConnectorModel
ConnectorBase* new_conn_base_ptr = GenericConnectorModel<ConnectionT>::add_connection(src, tgt, conn, syn_id, d, weight, delay);
// Then we read out the vector of new pointers to the connections
// This includes te newly added synapse
std::vector<ConnectionT*> new_conn_ptrs = get_conn_ptrs(new_conn_base_ptr, syn_id);
// Now we update the connection pointers on all the relevant target neurons
update_conn_ptrs(old_conn_ptrs, new_conn_ptrs);
return new_conn_base_ptr;
}
template <typename ConnectionT>
ConnectorBase* LookBackConnectorModel<ConnectionT>::add_connection(Node &src, Node &tgt, ConnectorBase *conn, synindex syn_id,
double weight, double delay) {
// First we perform target neuron validation
get_validated_neuron(&tgt);
// Then we read out the vector of old pointers to the connections
std::vector<ConnectionT*> old_conn_ptrs = get_conn_ptrs(conn, syn_id);
// Then we add synapse by calling super function from GenericConnectorModel
ConnectorBase* new_conn_base_ptr = GenericConnectorModel<ConnectionT>::add_connection(src, tgt, conn, syn_id, weight, delay);
// Then we read out the vector of new pointers to the connections
// This includes te newly added synapse
std::vector<ConnectionT*> new_conn_ptrs = get_conn_ptrs(new_conn_base_ptr, syn_id);
// Now we update the connection pointers on all the relevant target neurons
update_conn_ptrs(old_conn_ptrs, new_conn_ptrs);
return new_conn_base_ptr;
}
template <typename ConnectionT>
bool LookBackConnectorModel<ConnectionT>::is_matching_syn_id(ConnectorBase *conn_base_in, index syn_id) {
// Assumptions:
//
// 1. conn_base_in is a validated pointer (i.e. with 2 LSB's 0)
return conn_base_in->homogeneous_model() && conn_base_in->get_syn_id() == syn_id;
}
template <typename ConnectionT>
std::vector<ConnectionT *> LookBackConnectorModel<ConnectionT>::get_conn_ptrs_hom(ConnectorBase *conn_base_in) {
// Assumptions:
//
// 1. conn_base_in is a validated pointer (i.e. with 2 LSB's 0)
// 2. conn_base_in represents a homogeneous connector
// 3. conn_base_in contains synapses of type ConnectionT
//
std::vector<ConnectionT *> conn_ptrs_out;
// Cast connector to vector_like<ConnectionT>. Guaranteed by assumptions 1, 2
auto hom_conn_base = static_cast<vector_like<ConnectionT>* >(conn_base_in);
for(size_t i=0; i< hom_conn_base->size(); ++i) {
conn_ptrs_out.push_back( &(hom_conn_base->at(i)) );
}
return conn_ptrs_out;
}
template <typename ConnectionT>
std::vector<ConnectionT *> LookBackConnectorModel<ConnectionT>::get_conn_ptrs_het(ConnectorBase *conn_base_in, index syn_id) {
// Assumptions:
//
// 1. conn_base_in is a validated pointer (i.e. with 2 LSB's 0)
// 2. conn_base_in represents a heterogeneous connector
// Casting conn_base_in to HetConnector. Safe thanksto assumption 1, 2
auto het_conn = static_cast<HetConnector*>(conn_base_in);
// Finding the Homogenous connector with matching type
vector_like<ConnectionT>* matching_hom_conn = NULL;
for(size_t i=0; i < het_conn->size(); ++i) {
if (is_matching_syn_id(het_conn->at(i), syn_id)) {
matching_hom_conn = static_cast< vector_like<ConnectionT>* >(het_conn->at(i));
break;
}
}
// If there is a homogenous connector of the right type, then return pointers from it
// else return empty vector
if(matching_hom_conn) {
return get_conn_ptrs_hom(matching_hom_conn);
}
else {
return std::vector<ConnectionT *>();
}
}
template <typename ConnectionT>
ConnectorModel *LookBackConnectorModel<ConnectionT>::clone(std::string name) const {
return new LookBackConnectorModel<ConnectionT>( *this, name );
}
}
#endif