/** * Create the masks for sources and targets based on the contiguous * ranges given in sources and targets. We need to do some index * translation here, as the CG expects indices from 0..n for both * source and target populations, while the RangeSets sources and * targets contain NEST global indices (gids). * * The masks for the sources must contain all nodes (local+remote). * The skip of the mask was set to 1 in cg_set_masks(). The same * source mask is stored n_proc times on each process. * * The masks for the targets must only contain local nodes. This is * achieved by first setting skip to num_processes upon creation of * the mask in cg_set_masks(), and second by the fact that for each * contiguous range of nodes in a mask, each of them contains the * index-translated id of the first local neuron as the first * entry. If this renders the range empty (i.e. because the first * local id is beyond the last element of the range), the range is * not added to the mask. * * \param masks The std::vector of Masks to populate * \param sources The source ranges to create the source masks from * \param targets The target ranges to create the target masks from * * \note Each process computes the full set of source and target * masks, i.e. one mask per rank will be created on each rank. * * \note Setting the masks for all processes on each process might * become a memory bottleneck when going to very large numbers of * processes. Especially so for the source masks, which are all the * same. This could be solved by making the ConnectionGenerator * interface MPI aware and communicating the masks during connection * setup. */ void cg_create_masks( std::vector< ConnectionGenerator::Mask >* masks, RangeSet& sources, RangeSet& targets ) { // The index of the left border of the currently looked at range // (counting from 0). This is used for index translation. size_t cg_idx_left = 0; // For sources, we only need to translate from NEST to CG indices. for ( RangeSet::iterator source = sources.begin(); source != sources.end(); ++source ) { size_t num_elements = source->last - source->first; size_t right = cg_idx_left + num_elements; for ( size_t proc = 0; proc < static_cast< size_t >( Communicator::get_num_processes() ); ++proc ) ( *masks )[ proc ].sources.insert( cg_idx_left, right ); cg_idx_left += num_elements + 1; } // Reset the index of the left border of the range for index // translation for the targets. cg_idx_left = 0; for ( RangeSet::iterator target = targets.begin(); target != targets.end(); ++target ) { size_t num_elements = target->last - target->first; for ( size_t proc = 0; proc < static_cast< size_t >( Communicator::get_num_processes() ); ++proc ) { // Make sure that the range is only added on as many ranks as // there are elements in the range, or exactly on every rank, // if there are more elements in the range. if ( proc <= num_elements ) { // For the different ranks, left will take on the CG indices // of all first local nodes that are contained in the range. // The rank, where this mask is to be used is determined // below when inserting the mask. size_t left = cg_idx_left + proc; // right is set to the CG index of the right border of the // range. This is the same for all ranks. size_t right = cg_idx_left + num_elements; // We index the masks according to the modulo distribution // of neurons in NEST. This ensures that the mask is set for // the rank where left acutally is the first neuron fromt // the currently looked at range. ( *masks )[ ( proc + target->first ) % Communicator::get_num_processes() ].targets.insert( left, right ); } } // Update the CG index of the left border of the next range to // be one after the current range. cg_idx_left += num_elements + 1; } }
void cg_create_masks(std::vector<ConnectionGenerator::Mask>* masks, RangeSet& sources, RangeSet& targets) { // We need to do some index translation here as the CG expects // indices from 0..n for both source and target populations. size_t length = 0; for (RangeSet::iterator source = sources.begin(); source != sources.end(); ++source) { for (size_t proc = 0; proc < static_cast<size_t>(Communicator::get_num_processes()); ++proc) { size_t last = source->last - source->first; if (proc <= last) { size_t left = proc + length; size_t right = last + length; (*masks)[(proc + source->first) % Communicator::get_num_processes()].sources.insert(left, right); } } length += source->last - source->first + 1; } length = 0; for (RangeSet::iterator target = targets.begin(); target != targets.end(); ++target) { for (size_t proc = 0; proc < static_cast<size_t>(Communicator::get_num_processes()); ++proc) { size_t last = target->last - target->first; if (proc <= last) { size_t left = proc + length; size_t right = last + length; (*masks)[(proc + target->first) % Communicator::get_num_processes()].targets.insert(left, right); } } length += target->last - target->first + 1; } }
/** * Splits the selected segments by inserting new nodes in the middle. The * selected segments are defined by each pair of consecutive \a indexRanges. * * This method can deal with both polygons as well as polylines. For polygons, * pass <code>true</code> for \a closed. */ static QPolygonF splitPolygonSegments(const QPolygonF &polygon, const RangeSet<int> &indexRanges, bool closed) { if (indexRanges.isEmpty()) return polygon; const int n = polygon.size(); QPolygonF result = polygon; RangeSet<int>::Range firstRange = indexRanges.begin(); RangeSet<int>::Range it = indexRanges.end(); // assert: firstRange != it if (closed) { RangeSet<int>::Range lastRange = it; --lastRange; // We know there is at least one range // Handle the case where the first and last nodes are selected if (firstRange.first() == 0 && lastRange.last() == n - 1) { const QPointF splitPoint = (result.first() + result.last()) / 2; result.append(splitPoint); } } do { --it; for (int i = it.last(); i > it.first(); --i) { const QPointF splitPoint = (result.at(i) + result.at(i - 1)) / 2; result.insert(i, splitPoint); } } while (it != firstRange); return result; }
/** * Joins the nodes at the given \a indexRanges. Each consecutive sequence * of nodes will be joined into a single node at the average location. * * This method can deal with both polygons as well as polylines. For polygons, * pass <code>true</code> for \a closed. */ static QPolygonF joinPolygonNodes(const QPolygonF &polygon, const RangeSet<int> &indexRanges, bool closed) { if (indexRanges.isEmpty()) return polygon; // Do nothing when dealing with a polygon with less than 3 points // (we'd no longer have a polygon) const int n = polygon.size(); if (n < 3) return polygon; RangeSet<int>::Range firstRange = indexRanges.begin(); RangeSet<int>::Range it = indexRanges.end(); RangeSet<int>::Range lastRange = it; --lastRange; // We know there is at least one range QPolygonF result = polygon; // Indexes need to be offset when first and last range are joined. int indexOffset = 0; // Check whether the first and last ranges connect if (firstRange.first() == 0 && lastRange.last() == n - 1) { // Do nothing when the selection spans the whole polygon if (firstRange == lastRange) return polygon; // Join points of the first and last range when the polygon is closed if (closed) { QPointF averagePoint; for (int i = firstRange.first(); i <= firstRange.last(); i++) averagePoint += polygon.at(i); for (int i = lastRange.first(); i <= lastRange.last(); i++) averagePoint += polygon.at(i); averagePoint /= firstRange.length() + lastRange.length(); result.remove(lastRange.first(), lastRange.length()); result.remove(1, firstRange.length() - 1); result.replace(0, averagePoint); indexOffset = firstRange.length() - 1; // We have dealt with these ranges now // assert: firstRange != lastRange ++firstRange; --it; } } while (it != firstRange) { --it; // Merge the consecutive nodes into a single average point QPointF averagePoint; for (int i = it.first(); i <= it.last(); i++) averagePoint += polygon.at(i - indexOffset); averagePoint /= it.length(); result.remove(it.first() + 1 - indexOffset, it.length() - 1); result.replace(it.first() - indexOffset, averagePoint); } return result; }