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