1 /*
2 * source_table.h
3 *
4 * This file is part of NEST.
5 *
6 * Copyright (C) 2004 The NEST Initiative
7 *
8 * NEST is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation, either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * NEST is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with NEST. If not, see <http://www.gnu.org/licenses/>.
20 *
21 */
22
23 #ifndef SOURCE_TABLE_H
24 #define SOURCE_TABLE_H
25
26 // C++ includes:
27 #include <algorithm>
28 #include <cassert>
29 #include <iostream>
30 #include <map>
31 #include <set>
32 #include <vector>
33
34 // Includes from nestkernel:
35 #include "mpi_manager.h"
36 #include "nest_types.h"
37 #include "per_thread_bool_indicator.h"
38 #include "source.h"
39 #include "source_table_position.h"
40 #include "spike_data.h"
41
42 // Includes from libnestutil
43 #include "block_vector.h"
44 #include "vector_util.h"
45
46 namespace nest
47 {
48
49 class TargetData;
50
51 /**
52 * This data structure stores the node IDs of presynaptic neurons
53 * during postsynaptic connection creation, before the connection
54 * information has been transferred to the presynaptic side. The core
55 * structure is the three dimensional sources vector, which is
56 * arranged as follows:
57 * 1st dimension: threads
58 * 2nd dimension: synapse types
59 * 3rd dimension: node IDs
60 * After all connections have been created, the information stored in
61 * this structure is transferred to the presynaptic side and the
62 * sources vector can be cleared.
63 */
64 class SourceTable
65 {
66 private:
67 /**
68 * 3D structure storing node IDs of presynaptic neurons.
69 */
70 std::vector< std::vector< BlockVector< Source > > > sources_;
71
72 /**
73 * Whether the 3D structure has been deleted.
74 */
75 PerThreadBoolIndicator is_cleared_;
76
77 //! Needed during readout of sources_.
78 std::vector< SourceTablePosition > current_positions_;
79 //! Needed during readout of sources_.
80 std::vector< SourceTablePosition > saved_positions_;
81
82 /**
83 * If we detect an overflow in one of the MPI buffer parts, we save
84 * our current position in sources_ to continue at that point in
85 * the next communication round, while filling up (possible)
86 * remaining parts of the MPI buffer.
87 */
88 PerThreadBoolIndicator saved_entry_point_;
89
90 /**
91 * Minimal number of sources that need to be deleted per synapse
92 * type and thread before a reallocation of the respective vector
93 * is performed. Balances number of reallocations and memory usage.
94 *
95 * @see SourceTable::clean()
96 */
97 static const size_t min_deleted_elements_ = 1000000;
98
99
100 /**
101 * Returns whether this Source object should be considered when
102 * constructing MPI buffers for communicating connections. Returns
103 * false if i) this entry was already processed, or ii) this entry
104 * is disabled (e.g., by structural plastcity) or iii) the reading
105 * thread is not responsible for the particular part of the MPI
106 * buffer where this entry would be written.
107 */
108 bool source_should_be_processed_( const thread rank_start, const thread rank_end, const Source& source ) const;
109
110 /**
111 * Returns true if the following entry in the SourceTable has the
112 * same source gid.
113 */
114 bool next_entry_has_same_source_( const SourceTablePosition& current_position, const Source& current_source ) const;
115
116 /**
117 * Returns true if the previous entry in the SourceTable has the
118 * same source gid.
119 */
120 bool previous_entry_has_same_source_( const SourceTablePosition& current_position,
121 const Source& current_source ) const;
122
123 /**
124 * Fills the fields of a TargetData during construction of *
125 * presynaptic connection infrastructure.
126 */
127 bool populate_target_data_fields_( const SourceTablePosition& current_position,
128 const Source& current_source,
129 const thread source_rank,
130 TargetData& next_target_data ) const;
131
132 /**
133 * A structure to temporarily hold information about all process
134 * local targets will be addressed by incoming spikes. Data from
135 * this structure is transferred to the compressed_spike_data_
136 * structure of ConnectionManager during construction of the
137 * postsynaptic connection infrastructure. Arranged as a two
138 * dimensional vector (thread|synapse) with an inner map (source
139 * node id -> spike data).
140 */
141 std::vector< std::vector< std::map< index, SpikeData > > > compressible_sources_;
142
143 /**
144 * A structure to temporarily store locations of "unpacked spikes"
145 * in the compressed_spike_data_ structure of
146 * ConnectionManager. Data from this structure is transferred to the
147 * presynaptic side during construction of the presynaptic
148 * connection infrastructure. Arranged as a two dimensional vector
149 * (thread|synapse) with an inner map (source node id -> index).
150 */
151 std::vector< std::vector< std::map< index, size_t > > > compressed_spike_data_map_;
152
153 public:
154 SourceTable();
155 ~SourceTable();
156
157 /**
158 * Initialize data structure.
159 */
160 void initialize();
161
162 /**
163 * Delete data structures.
164 */
165 void finalize();
166
167 /**
168 * Adds a source to sources_.
169 */
170 void add_source( const thread tid, const synindex syn_id, const index node_id, const bool is_primary );
171
172 /**
173 * Clears sources_.
174 */
175 void clear( const thread tid );
176
177 /**
178 * Returns true if sources_ has been cleared.
179 */
180 bool is_cleared() const;
181
182 /**
183 * Returns the next target data, according to the current_positions_.
184 */
185 bool get_next_target_data( const thread tid,
186 const thread rank_start,
187 const thread rank_end,
188 thread& source_rank,
189 TargetData& next_target_data );
190
191 /**
192 * Rejects the last target data, and resets the current_positions_
193 * accordingly.
194 */
195 void reject_last_target_data( const thread tid );
196
197 /**
198 * Stores current_positions_ in saved_positions_.
199 */
200 void save_entry_point( const thread tid );
201
202 /**
203 * Restores current_positions_ from saved_positions_.
204 */
205 void restore_entry_point( const thread tid );
206
207 /**
208 * Resets saved_positions_ to end of sources_.
209 */
210 void reset_entry_point( const thread tid );
211
212 /**
213 * Returns the node ID of the source at tid|syn_id|lcid.
214 */
215 index get_node_id( const thread tid, const synindex syn_id, const index lcid ) const;
216
217 /**
218 * Returns a reference to all sources local on thread; necessary
219 * for sorting.
220 */
221 std::vector< BlockVector< Source > >& get_thread_local_sources( const thread tid );
222
223 /**
224 * Determines maximal saved_positions_ after which it is safe to
225 * delete sources during clean().
226 */
227 SourceTablePosition find_maximal_position() const;
228
229 /**
230 * Resets all processed flags. Needed for restructuring connection
231 * tables, e.g., during structural plasticity update.
232 */
233 void reset_processed_flags( const thread tid );
234
235 /**
236 * Removes all entries marked as processed.
237 */
238 void clean( const thread tid );
239
240 /**
241 * Sets current_positions_ for this thread to minimal values so that
242 * these are not considered in find_maximal_position().
243 */
244 void no_targets_to_process( const thread tid );
245
246 /**
247 * Computes MPI buffer positions for unique combination of source
248 * node ID and synapse type across all threads for all secondary
249 * connections.
250 */
251 void compute_buffer_pos_for_unique_secondary_sources( const thread tid,
252 std::map< index, size_t >& buffer_pos_of_source_node_id_syn_id_ );
253
254 /**
255 * Finds the first entry in sources_ at the given thread id and
256 * synapse type that is equal to snode_id.
257 */
258 index find_first_source( const thread tid, const synindex syn_id, const index snode_id ) const;
259
260 /**
261 * Marks entry in sources_ at given position as disabled.
262 */
263 void disable_connection( const thread tid, const synindex syn_id, const index lcid );
264
265 /**
266 * Removes all entries from sources_ that are marked as disabled.
267 */
268 index remove_disabled_sources( const thread tid, const synindex syn_id );
269
270 /**
271 * Returns node IDs for entries in sources_ for the given thread
272 * id, synapse type and local connections ids.
273 */
274 void get_source_node_ids( const thread tid,
275 const synindex syn_id,
276 const std::vector< index >& source_lcids,
277 std::vector< index >& sources );
278
279 /**
280 * Returns the number of unique node IDs for given thread id and
281 * synapse type in sources_. This number corresponds to the number
282 * of targets that need to be communicated during construction of
283 * the presynaptic connection infrastructure.
284 */
285 size_t num_unique_sources( const thread tid, const synindex syn_id ) const;
286
287 /**
288 * Resizes sources_ according to total number of threads and
289 * synapse types.
290 */
291 void resize_sources( const thread tid );
292
293 /**
294 * Encodes combination of node ID and synapse types as single
295 * long number.
296 */
297 index pack_source_node_id_and_syn_id( const index source_node_id, const synindex syn_id ) const;
298
299 void resize_compressible_sources();
300
301 // creates maps of sources with more than one thread-local target
302 void collect_compressible_sources( const thread tid );
303 // fills the compressed_spike_data structure in ConnectionManager
304 void fill_compressed_spike_data( std::vector< std::vector< std::vector< SpikeData > > >& compressed_spike_data );
305
306 void clear_compressed_spike_data_map( const thread tid );
307 };
308
309 inline void
add_source(const thread tid,const synindex syn_id,const index node_id,const bool is_primary)310 SourceTable::add_source( const thread tid, const synindex syn_id, const index node_id, const bool is_primary )
311 {
312 const Source src( node_id, is_primary );
313 sources_[ tid ][ syn_id ].push_back( src );
314 }
315
316 inline void
clear(const thread tid)317 SourceTable::clear( const thread tid )
318 {
319 for ( std::vector< BlockVector< Source > >::iterator it = sources_[ tid ].begin(); it != sources_[ tid ].end(); ++it )
320 {
321 it->clear();
322 }
323 sources_[ tid ].clear();
324 is_cleared_[ tid ].set_true();
325 }
326
327 inline void
reject_last_target_data(const thread tid)328 SourceTable::reject_last_target_data( const thread tid )
329 {
330 // The last target data returned by get_next_target_data() could not
331 // be inserted into MPI buffer due to overflow. We hence need to
332 // correct the processed flag of the last entry (see
333 // source_table.cpp)
334 assert( current_positions_[ tid ].lcid + 1
335 < static_cast< long >( sources_[ current_positions_[ tid ].tid ][ current_positions_[ tid ].syn_id ].size() ) );
336
337 sources_[ current_positions_[ tid ].tid ][ current_positions_[ tid ].syn_id ][ current_positions_[ tid ].lcid + 1 ]
338 .set_processed( false );
339 }
340
341 inline void
save_entry_point(const thread tid)342 SourceTable::save_entry_point( const thread tid )
343 {
344 if ( saved_entry_point_[ tid ].is_false() )
345 {
346 saved_positions_[ tid ].tid = current_positions_[ tid ].tid;
347 saved_positions_[ tid ].syn_id = current_positions_[ tid ].syn_id;
348
349 // if tid and syn_id are valid entries, also store valid entry for lcid
350 if ( current_positions_[ tid ].tid > -1 and current_positions_[ tid ].syn_id > -1 )
351 {
352 // either store current_position.lcid + 1, since this can
353 // contain non-processed entry (see reject_last_target_data()) or
354 // store maximal value for lcid.
355 saved_positions_[ tid ].lcid = std::min( current_positions_[ tid ].lcid + 1,
356 static_cast< long >( sources_[ current_positions_[ tid ].tid ][ current_positions_[ tid ].syn_id ].size()
357 - 1 ) );
358 }
359 else
360 {
361 assert( current_positions_[ tid ].lcid == -1 );
362 saved_positions_[ tid ].lcid = -1;
363 }
364 saved_entry_point_[ tid ].set_true();
365 }
366 }
367
368 inline void
restore_entry_point(const thread tid)369 SourceTable::restore_entry_point( const thread tid )
370 {
371 current_positions_[ tid ] = saved_positions_[ tid ];
372 saved_entry_point_[ tid ].set_false();
373 }
374
375 inline void
reset_entry_point(const thread tid)376 SourceTable::reset_entry_point( const thread tid )
377 {
378 // Since we read the source table backwards, we need to set saved
379 // values to the biggest possible value. These will be used to
380 // initialize current_positions_ correctly upon calling
381 // restore_entry_point. However, this can only be done if other
382 // values have valid values.
383 saved_positions_[ tid ].tid = sources_.size() - 1;
384 if ( saved_positions_[ tid ].tid > -1 )
385 {
386 saved_positions_[ tid ].syn_id = sources_[ saved_positions_[ tid ].tid ].size() - 1;
387 }
388 else
389 {
390 saved_positions_[ tid ].syn_id = -1;
391 }
392 if ( saved_positions_[ tid ].syn_id > -1 )
393 {
394 saved_positions_[ tid ].lcid = sources_[ saved_positions_[ tid ].tid ][ saved_positions_[ tid ].syn_id ].size() - 1;
395 }
396 else
397 {
398 saved_positions_[ tid ].lcid = -1;
399 }
400 }
401
402 inline void
reset_processed_flags(const thread tid)403 SourceTable::reset_processed_flags( const thread tid )
404 {
405 for ( std::vector< BlockVector< Source > >::iterator it = sources_[ tid ].begin(); it != sources_[ tid ].end(); ++it )
406 {
407 for ( BlockVector< Source >::iterator iit = it->begin(); iit != it->end(); ++iit )
408 {
409 iit->set_processed( false );
410 }
411 }
412 }
413
414 inline void
no_targets_to_process(const thread tid)415 SourceTable::no_targets_to_process( const thread tid )
416 {
417 current_positions_[ tid ].tid = -1;
418 current_positions_[ tid ].syn_id = -1;
419 current_positions_[ tid ].lcid = -1;
420 }
421
422 inline index
find_first_source(const thread tid,const synindex syn_id,const index snode_id)423 SourceTable::find_first_source( const thread tid, const synindex syn_id, const index snode_id ) const
424 {
425 // binary search in sorted sources
426 const BlockVector< Source >::const_iterator begin = sources_[ tid ][ syn_id ].begin();
427 const BlockVector< Source >::const_iterator end = sources_[ tid ][ syn_id ].end();
428 BlockVector< Source >::const_iterator it = std::lower_bound( begin, end, Source( snode_id, true ) );
429
430 // source found by binary search could be disabled, iterate through
431 // sources until a valid one is found
432 while ( it != end )
433 {
434 if ( it->get_node_id() == snode_id and not it->is_disabled() )
435 {
436 const index lcid = it - begin;
437 return lcid;
438 }
439 ++it;
440 }
441
442 // no enabled entry with this snode ID found
443 return invalid_index;
444 }
445
446 inline void
disable_connection(const thread tid,const synindex syn_id,const index lcid)447 SourceTable::disable_connection( const thread tid, const synindex syn_id, const index lcid )
448 {
449 // disabling a source changes its node ID to 2^62 -1
450 // source here
451 assert( not sources_[ tid ][ syn_id ][ lcid ].is_disabled() );
452 sources_[ tid ][ syn_id ][ lcid ].disable();
453 }
454
455 inline void
get_source_node_ids(const thread tid,const synindex syn_id,const std::vector<index> & source_lcids,std::vector<index> & sources)456 SourceTable::get_source_node_ids( const thread tid,
457 const synindex syn_id,
458 const std::vector< index >& source_lcids,
459 std::vector< index >& sources )
460 {
461 for ( std::vector< index >::const_iterator cit = source_lcids.begin(); cit != source_lcids.end(); ++cit )
462 {
463 sources.push_back( sources_[ tid ][ syn_id ][ *cit ].get_node_id() );
464 }
465 }
466
467 inline size_t
num_unique_sources(const thread tid,const synindex syn_id)468 SourceTable::num_unique_sources( const thread tid, const synindex syn_id ) const
469 {
470 size_t n = 0;
471 index last_source = 0;
472 for ( BlockVector< Source >::const_iterator cit = sources_[ tid ][ syn_id ].begin();
473 cit != sources_[ tid ][ syn_id ].end();
474 ++cit )
475 {
476 if ( last_source != ( *cit ).get_node_id() )
477 {
478 last_source = ( *cit ).get_node_id();
479 ++n;
480 }
481 }
482 return n;
483 }
484
485 inline index
pack_source_node_id_and_syn_id(const index source_node_id,const synindex syn_id)486 SourceTable::pack_source_node_id_and_syn_id( const index source_node_id, const synindex syn_id ) const
487 {
488 assert( source_node_id < 72057594037927936 );
489 assert( syn_id < invalid_synindex );
490 // syn_id is maximally 256, so shifting node ID by 8 bits and storing
491 // syn_id in the lowest 8 leads to a unique number
492 return ( source_node_id << 8 ) + syn_id;
493 }
494
495 inline void
clear_compressed_spike_data_map(const thread tid)496 SourceTable::clear_compressed_spike_data_map( const thread tid )
497 {
498 for ( synindex syn_id = 0; syn_id < compressed_spike_data_map_[ tid ].size(); ++syn_id )
499 {
500 compressed_spike_data_map_[ tid ][ syn_id ].clear();
501 }
502 }
503
504 } // namespace nest
505
506 #endif // SOURCE_TABLE_H
507