#ifndef SLICE_RING_BUFFER_H
#define SLICE_RING_BUFFER_H
/*
* slice_ring_buffer.h
*
* This file is part of NEST.
*
* Copyright (C) 2004 The NEST Initiative
*
* NEST is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* NEST is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with NEST. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "config.h"
#include <algorithm>
#include <functional>
#include <vector>
#include "nest.h"
#include "scheduler.h"
namespace nest
{
/**
* Queue for all spikes arriving into a neuron.
* Spikes are stored unsorted on arrival, but are sorted when
* prepare_delivery() is called. They can then be retrieved
* one by one in correct temporal order. Coinciding spikes
* are combined into one, see get_next_spike().
*
* Data is organized as follows:
* - The time of the next return from refractoriness is
* stored in a separate variable and checked explicitly;
* otherwise, we'd have to re-sort data during updating.
* - We have a pseudo-ring of Nbuff=ceil((min_del+max_del)/min_del) elements.
* Each element is a vector storing incoming spikes that
* are due during a given time slice.
*
* @note The following assumptions underlie the handling of
* pseudo-events for return from refractoriness:
* - There is at most one such event per time step (value of time stamp).
*/
class SliceRingBuffer {
public:
SliceRingBuffer();
/**
* Add spike to queue.
* @param rel_delivery relative delivery time
* @param stamp Delivery time
* @param ps_offset Precise timing offset of spike time
* @param weight Weight of spike.
*/
void add_spike(const delay rel_delivery, const long_t stamp,
const double ps_offset, const double weight);
/**
* Add refractory event to queue.
* The refractory event is actually stored as a pseudo-event.
* @param stamp Delivery time
* @param ps_offset Precise timing offset of spike time
*/
void add_refractory(const long_t stamp, const double_t ps_offset);
/**
* Prepare for spike delivery in current slice by sorting.
*/
void prepare_delivery();
/**
* Discard all events in current slice.
*/
void discard_events();
/**
* Return next spike.
* @param req_stamp Request spike with this stamp. Queue
* should never contain spikes with smaller
* stamps. Spikes with larger stamps are
* left in queue.
* @param ps_offset PS-sense offset of spike time
* @param weight Spike weight
* @param end_of_refract True if spike is pseudo-spike marking
* end of refractory period
* @returns true if spike available, false otherwise
* @note If return from refractoriness coincides with
* a spike, return from refractoriness is returned.
* If several spikes coincide, the sum of their
* weights is returned a single spike.
*/
bool get_next_spike(const long_t req_stamp,
double_t& ps_offset, double_t& weight,
bool& end_of_refract);
/**
* Clear buffer
*/
void clear();
/**
* Resize the buffer according to min_delay and max_delay.
*/
void resize();
private:
/**
* Information about spike.
*/
struct SpikeInfo {
SpikeInfo(long_t stamp, double_t ps_offset, double_t weight);
bool operator< (const SpikeInfo& b) const;
bool operator<=(const SpikeInfo& b) const;
bool operator> (const SpikeInfo& b) const;
// data elements must not be const, since heap implementation
// in DEC STL uses operator=().
long_t stamp_; //<! spike's time stamp
double_t ps_offset_; //<! spike offset is PS sense
double_t weight_; //<! spike weight
};
//! entire queue, one slot per min_delay block within max_delay
std::vector<std::vector<SpikeInfo> > queue_;
//! slot to deliver from
std::vector<SpikeInfo> * deliver_;
SpikeInfo refract_; //!< pseudo-event for return from refractoriness
};
inline
void SliceRingBuffer::add_spike(const delay rel_delivery, const long_t stamp,
const double ps_offset, const double weight)
{
const delay idx = Scheduler::get_slice_modulo(rel_delivery);
assert(idx < queue_.size());
assert(ps_offset >= 0);
queue_[idx].push_back(SpikeInfo(stamp, ps_offset, weight));
}
inline
void SliceRingBuffer::add_refractory(const long_t stamp, const double_t ps_offset)
{
// We require that only one refractory-return pseudo-event is stored per
// time step. We guard against violation using assert(): refract_.stamp_ must
// be equal to the marker value for non-refractoriness. All else would mean
// that a refractory neuron fired.
assert(refract_.stamp_ == std::numeric_limits<long_t>::max());
refract_.stamp_ = stamp;
refract_.ps_offset_= ps_offset;
}
inline
bool SliceRingBuffer::get_next_spike(const long_t req_stamp,
double_t& ps_offset, double_t& weight,
bool& end_of_refract)
{
end_of_refract = false;
if ( deliver_->empty() || refract_ <= deliver_->back() )
if ( refract_.stamp_ == req_stamp )
{ // if relies on stamp_==long_t::max() if not refractory
// return from refractoriness
ps_offset = refract_.ps_offset_;
weight = 0;
end_of_refract = true;
// mark as non-refractory
refract_.stamp_ = std::numeric_limits<long_t>::max();
return true;
}
else
return false;
else if ( deliver_->back().stamp_ == req_stamp )
{
// we have an event to deliver, register its offset
ps_offset = deliver_->back().ps_offset_;
// accumulate weights of all spikes with same stamp
// AND offset
weight = 0.0; // accumulate weights of all
while ( !deliver_->empty()
&& deliver_->back().ps_offset_ == ps_offset
&& deliver_->back().stamp_ == req_stamp )
{
weight += deliver_->back().weight_;
deliver_->pop_back();
}
return true;
}
else
{
// ensure that we are not blocked by spike from the past, cf #404
assert( deliver_->back().stamp_ > req_stamp );
return false;
}
}
inline
SliceRingBuffer::SpikeInfo::SpikeInfo(long_t stamp, double_t ps_offset,
double_t weight) :
stamp_(stamp),
ps_offset_(ps_offset),
weight_(weight)
{}
inline
bool SliceRingBuffer::SpikeInfo::operator<(const SpikeInfo& b) const
{
return stamp_ == b.stamp_ ? ps_offset_ > b.ps_offset_ : stamp_ < b.stamp_;
}
inline
bool SliceRingBuffer::SpikeInfo::operator<=(const SpikeInfo& b) const
{
return !(*this > b);
}
inline
bool SliceRingBuffer::SpikeInfo::operator>(const SpikeInfo& b) const
{
return stamp_ == b.stamp_ ? ps_offset_ < b.ps_offset_ : stamp_ > b.stamp_;
}
}
#endif