/*
* event.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/>.
*
*/
#ifndef EVENT_H
#define EVENT_H
#include "nest.h"
#include "nest_time.h"
#include "lockptr.h"
#include "exceptions.h"
#include "name.h"
namespace nest{
class Node;
/**
* Encapsulates information which is sent between Nodes.
*
* For each type of information there has to be a specialized event
* class.
*
* Events are used for two tasks. During connection, they are used as
* polymorphic connect objects. During simulation they are used to
* transport basic event information from one node to the other.
*
* A connection between two elements is physically established in two
* steps: First, create an event with the two envolved elements.
* Second, call the connect method of the event.
*
* An event object contains only administrative information which is
* needed to successfully deliver the event. Thus, event objects
* cannot direcly contain custom data: events are not messages. If a
* node receives an event, arbitrary abounts of data may be exchanged
* between the participating nodes.
* With this restriction it is possible to implement a comparatively
* efficient event handling scheme. 5-6 function calls per event may
* seem a long time, but this is cheap if we consider that event
* handling makes update and communication succeptible to parallel
* execution.
*
* @see Node
* @see SpikeEvent
* @see RateEvent
* @see CurrentEvent
* @see CurrentEvent
* @see ConductanceEvent
* @ingroup event_interface
*/
class Event {
public:
Event();
virtual
~Event(){}
/**
* Virtual copy constructor.
*/
virtual
Event* clone() const=0;
/**
* Deliver the event to receiver.
*
* This operator calls the handler for the specific event type at
* the receiver.
*/
virtual
void operator()() = 0;
/**
* Change pointer to receiving Node.
*/
void set_receiver(Node &);
/**
* Return reference to receiving Node.
*/
Node & get_receiver() const;
/**
* Return reference to sending Node.
*/
Node & get_sender() const;
/**
* Change pointer to sending Node.
*/
void set_sender(Node &);
/**
* Return GID of sending Node.
*/
index get_sender_gid() const;
/**
* Change GID of sending Node.
*/
void set_sender_gid(index);
/**
* Return time stamp of the event.
* The stamp denotes the time when the event was created.
* The resolution of Stamp is limited by the time base of the
* simulation kernel (@see class nest::Time).
* If this resolution is not fine enough, the creation time
* can be corrected by using the time attribute.
*/
Time const& get_stamp() const;
/**
* Set the transmission delay of the event.
* The delay refers to the time until the event is
* expected to arrive at the receiver.
* @param t delay.
*/
void set_delay(delay);
/**
* Return transmission delay of the event.
* The delay refers to the time until the event is
* expected to arrive at the receiver.
*/
delay get_delay() const;
delay get_max_delay() const;
/**
* Relative spike delivery time in steps.
* Returns the delivery time of the spike relative to a given
* time in steps. Causality commands that the result should
* not be negative.
*
* @returns stamp + delay - 1 - t in steps
* @param Time reference time
*
* @see NEST Time Memo, Rule 3
*/
long_t get_rel_delivery_steps(const Time& t) const;
/**
* Return the sender port number of the event.
* This function returns the number of the port over which the
* Event was sent.
* @retval A negative return value indicates that no port number
* was available.
*/
port get_port() const;
/**
* Return the receiver port number of the event.
* This function returns the number of the r-port over which the
* Event was sent.
* @note A return value of 0 indicates that the r-port is not used.
*/
rport get_rport() const;
/**
* Set the port number.
* Each event carries the number of the port over which the event
* is sent. When a connection is established, it receives a unique
* ID from the sender. This number has to be stored in each Event
* object.
* @param p Port number of the connection, or -1 if unknown.
*/
void set_port(port p);
/**
* Set the receiver port number (r-port).
* When a connection is established, the receiving Node may issue
* a port number (r-port) to distinguish the incomin
* connection. By the default, the r-port is not used and its port
* number defaults to zero.
* @param p Receiver port number of the connection, or 0 if unused.
*/
void set_rport(rport p);
/**
* Return the creation time offset of the Event.
* Each Event carries the exact time of creation. This
* time need not coincide with an integral multiple of the
* temporal resolution. Rather, Events may be created at any point
* in time.
*/
double_t get_offset() const;
/**
* Set the creation time of the Event.
* Each Event carries the exact time of creation in realtime. This
* time need not coincide with an integral multiple of the
* temporal resolution. Rather, Events may be created at any point
* in time.
* @param t Creation time in realtime. t has to be in [0, h).
*/
void set_offset(double_t t);
/**
* Return the weight.
*/
weight get_weight() const;
/**
* Set weight of the event.
*/
void set_weight(weight t);
/**
* Check integrity of the event.
* This function returns true, if all data, in particular sender
* and receiver pointers are correctly set.
*/
bool is_valid() const;
/**
* Set the time stamp of the event.
* The time stamp refers to the time when the event
* was created.
*/
void set_stamp(Time const &);
protected:
index sender_gid_; //!< GID of sender or -1.
/*
* The original formulation used references to Nodes as
* members, however, in order to avoid the reference of reference
* problem, we store sender and receiver as pointers and use
* references in the interface.
* Thus, we can still ensure that the pointers are never NULL.
*/
Node * sender_; //!< Pointer to sender or NULL.
Node * receiver_; //!< Pointer to receiver or NULL.
/**
* Sender port number.
* The sender port is used as a unique identifier for the
* connection. The receiver of an event can use the port number
* to obtain data from the sender. The sender uses this number to
* locate target-specific information. @note A negative port
* number indicates an unknown port.
*/
port p_;
/**
* Receiver port number (r-port).
* The receiver port (r-port) can be used by the receiving Node to
* distinguish incoming connections. E.g. the r-port number can be
* used by Events to access specific parts of a Node. In most
* cases, however, this port will no be used.
* @note The use of this port number is optional.
* @note An r-port number of 0 indicates that the port is not used.
*/
rport rp_;
/**
* Transmission delay.
* Number of simulations steps that pass before the event is
* delivered at the receiver.
* The delay must be at least 1.
*/
delay d_;
/**
* Time stamp.
* The time stamp specifies the absolute time
* when the event shall arrive at the target.
*/
Time stamp_;
/**
* Offset for precise spike times.
* offset_ specifies a correction to the creation time.
* If the resolution of stamp is not sufficiently precise,
* this attribute can be used to correct the creation time.
* offset_ has to be in [0, h).
*/
double offset_;
/**
* Weight of the connection.
*/
weight w_;
};
// Built-in event types
/**
* Event for spike information.
* Used to send a spike from one node to the next.
*/
class SpikeEvent: public Event
{
public:
SpikeEvent();
void operator()();
SpikeEvent* clone() const;
void set_multiplicity(int_t);
int_t get_multiplicity() const;
protected:
int_t multiplicity_;
};
inline
SpikeEvent::SpikeEvent()
: multiplicity_(1)
{}
inline
SpikeEvent* SpikeEvent::clone() const
{
return new SpikeEvent(*this);
}
inline
void SpikeEvent::set_multiplicity(int_t multiplicity)
{
multiplicity_=multiplicity;
}
inline
int_t SpikeEvent::get_multiplicity() const
{
return multiplicity_;
}
/**
* "Callback request event" for use in Device.
*
* Some Nodes want to perform a function on an event for each
* of their targets. An example is the poisson_generator which
* needs to draw a random number for each target. The DSSpikeEvent,
* DirectSendingSpikeEvent, calls sender->event_hook(*this)
* in its operator() function instead of calling receiver->handle().
* The default implementation of Node::event_hook() just calls
* target->handle(DSSpikeEvent&). Any reimplementation must also
* execute this call. Otherwise the event will not be delivered.
* If needed, target->handle(DSSpikeEvent&) may be called more than
* once.
*
* @note Callback events must only be sent via static_synapse
*/
class DSSpikeEvent : public SpikeEvent
{
public:
void operator()();
};
/**
* Event for firing rate information.
* Used to send firing rate from one node to the next.
* The rate information is not contained in the event
* object. Rather, the receiver has to poll this information
* from the sender.
*/
class RateEvent: public Event
{
double_t r_;
public:
void operator()();
RateEvent* clone() const;
void set_rate(double_t);
double_t get_rate() const;
};
inline
RateEvent* RateEvent::clone() const
{
return new RateEvent(*this);
}
inline
void RateEvent::set_rate(double_t r)
{
r_=r;
}
inline
double_t RateEvent::get_rate() const
{
return r_;
}
/**
* Event for electrical currents.
* Used to send currents from one node to the next.
*/
class CurrentEvent: public Event
{
double_t c_;
public:
void operator()();
CurrentEvent* clone() const;
void set_current(double_t);
double_t get_current() const;
};
inline
CurrentEvent* CurrentEvent::clone() const
{
return new CurrentEvent(*this);
}
inline
void CurrentEvent::set_current(double_t c)
{
c_ = c;
}
inline
double_t CurrentEvent::get_current() const
{
return c_;
}
/**
* "Callback request event" for use in Device.
*
* Some Nodes want to perform a function on an event for each
* of their targets. An example is the noise_generator which
* needs to draw a random number for each target. The DSCurrentEvent,
* DirectSendingCurrentEvent, calls sender->event_hook(*this)
* in its operator() function instead of calling receiver->handle().
* The default implementation of Node::event_hook() just calls
* target->handle(DSCurrentEvent&). Any reimplementation must also
* execute this call. Otherwise the event will not be delivered.
* If needed, target->handle(DSCurrentEvent&) may be called more than
* once.
*
* @note Callback events must only be sent via static_synapse.
*/
class DSCurrentEvent : public CurrentEvent
{
public:
void operator()();
};
/**
* @defgroup DataLoggingEvents Event types for analog logging devices.
*
* Events for flexible data recording.
*
* @addtogroup Devices
* @ingroup eventinterfaces
*/
/**
* Request data to be logged/logged data to be sent.
*
* @see DataLoggingReply
* @ingroup DataLoggingEvents
*/
class DataLoggingRequest : public Event
{
public:
/** Create empty request for use during simulation. */
DataLoggingRequest();
/** Create event for given time stamp and vector of recordables. */
DataLoggingRequest(const Time&, const std::vector<Name>&);
DataLoggingRequest* clone() const;
void operator()();
/** Access to stored time interval.*/
const Time& get_recording_interval() const;
/** Access to vector of recordables. */
const std::vector<Name>& record_from() const;
private:
//! Interval between two recordings, first is step 1
Time recording_interval_;
/**
* Names of properties to record from.
* @note This pointer shall be NULL unless the event is sent by a connection routine.
*/
std::vector<Name> const * const record_from_;
};
inline
DataLoggingRequest::DataLoggingRequest()
: Event(),
recording_interval_(Time::neg_inf()),
record_from_(0)
{}
inline
DataLoggingRequest::DataLoggingRequest(const Time& rec_int,
const std::vector<Name>& recs)
: Event(),
recording_interval_(rec_int),
record_from_(&recs)
{}
inline
DataLoggingRequest* DataLoggingRequest::clone() const
{
return new DataLoggingRequest(*this);
}
inline
const Time& DataLoggingRequest::get_recording_interval() const
{
// During simulation, events are created without recording interval
// information. On these, get_recording_interval() must not be called.
assert(recording_interval_.is_finite());
return recording_interval_;
}
inline
const std::vector<Name>& DataLoggingRequest::record_from() const
{
// During simulation, events are created without recordables
// information. On these, record_from() must not be called.
assert(record_from_ != 0);
return *record_from_;
}
/**
* Provide logged data through request transmitting reference.
* @see DataLoggingRequest
* @ingroup DataLoggingEvents
*/
class DataLoggingReply : public Event
{
public:
//! Data type data at single recording time
typedef std::vector<double_t> DataItem;
/** Data item with pertaining time stamp.
* Items are initialized with time stamp -inf to mark them as invalid.
* Data is initialized to <double_t>::max() as a highly implausible value.
* Ideally, we should initialized to a NaN, but since the C++-standard does
* not require NaN, that would result in unportable code. max() should draw
* the users att
*/
struct Item {
Item(size_t n) : data(n, std::numeric_limits<double_t>::max()),
timestamp(Time::neg_inf()) {}
DataItem data;
Time timestamp;
};
//! Container for entries
typedef std::vector<Item> Container;
//! Construct with reference to data and time stamps to transmit
DataLoggingReply(const Container&);
void operator()();
//! Access referenced data
const Container& get_info() const { return info_; }
private:
//! Prohibit copying
DataLoggingReply(const DataLoggingReply&);
//! Prohibit cloning
DataLoggingReply* clone() const { assert(false); return 0; }
//! data to be transmitted, with time stamps
const Container& info_;
};
inline
DataLoggingReply::DataLoggingReply(const Container& d)
: Event(),
info_(d)
{}
/**
* Event for electrical conductances.
* Used to send conductance from one node to the next.
* The conductance is contained in the event object.
*/
class ConductanceEvent: public Event
{
double_t g_;
public:
void operator()();
ConductanceEvent* clone() const;
void set_conductance(double_t);
double_t get_conductance() const;
};
inline
ConductanceEvent* ConductanceEvent::clone() const
{
return new ConductanceEvent(*this);
}
inline
void ConductanceEvent::set_conductance(double_t g)
{
g_=g;
}
inline
double_t ConductanceEvent::get_conductance() const
{
return g_;
}
/**
* Event for transmitting arbitrary data.
* This event type may be used for transmitting arbitrary
* data between events, e.g., images or their FFTs.
* A lockptr to the data is transmitted. The date type
* is given as a template parameter.
* @note: Data is passed via a lockptr.
* The receiver should copy the data at once, otherwise
* it may be modified by the sender.
* I hope this scheme is thread-safe, as long as the
* receiver copies the data at once. HEP.
* @note: This is a base class. Actual event types had to
* be derived, since operator() cannot be instantiated
* otherwise.
*/
template<typename D>
class DataEvent: public Event
{
lockPTR<D> data_;
public:
void set_pointer(D &data);
lockPTR<D> get_pointer() const;
};
template<typename D>
inline
void DataEvent<D>::set_pointer(D &data)
{
data_ = data;
}
template<typename D>
inline
lockPTR<D> DataEvent<D>::get_pointer() const
{
return data_;
}
class DoubleDataEvent: public DataEvent<double>
{
public:
void operator()();
DoubleDataEvent* clone() const;
};
inline
DoubleDataEvent* DoubleDataEvent::clone() const
{
return new DoubleDataEvent(*this);
}
//*************************************************************
// Inline implementations.
inline
bool Event::is_valid() const
{
return ( (sender_ != NULL) && (receiver_ != NULL) && (d_ > 0));
}
inline
void Event::set_receiver(Node &r)
{
receiver_= &r;
}
inline
void Event::set_sender(Node &s)
{
sender_= &s;
}
inline
void Event::set_sender_gid(index gid)
{
sender_gid_ = gid;
}
inline
Node & Event::get_receiver(void) const
{
return *receiver_;
}
inline
Node & Event::get_sender(void) const
{
return *sender_;
}
inline
index Event::get_sender_gid(void) const
{
return sender_gid_;
}
inline
weight Event::get_weight() const
{
return w_;
}
inline
void Event::set_weight(weight w)
{
w_=w;
}
inline
Time const & Event::get_stamp() const
{
return stamp_;
}
inline
void Event::set_stamp(Time const & s)
{
stamp_ = s;
}
inline
delay Event::get_delay() const
{
return d_;
}
inline
long_t Event::get_rel_delivery_steps(const Time& t) const
{
return stamp_.get_steps() + d_ - 1 - t.get_steps();
}
inline
void Event::set_delay(delay d)
{
d_=d;
}
inline
double_t Event::get_offset() const
{
return offset_;
}
inline
void Event::set_offset(double_t t)
{
offset_=t;
}
inline
port Event::get_port() const
{
return p_;
}
inline
rport Event::get_rport() const
{
return rp_;
}
inline
void Event::set_port(port p)
{
p_=p;
}
inline
void Event::set_rport(rport rp)
{
rp_=rp;
}
}
#endif //EVENT_H