/*
* node.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 NODE_H
#define NODE_H
#include <bitset>
#include <string>
#include <sstream>
#include <vector>
#include <deque>
#include <utility>
#include "nest.h"
#include "nest_time.h"
#include "nest_names.h"
#include "dictdatum.h"
#include "histentry.h"
#include "event.h"
/** @file node.h
* Declarations for base class Node
*/
namespace nest {
class Scheduler;
class Model;
class Subnet;
class Network;
class Archiving_Node;
class histentry;
class Connector;
class Connection;
/**
* @defgroup user_interface Model developer interface.
* Functions and classes important for implementing new Node and
* Model classes.
*/
/**
* Base class for all NEST network objects.
*
* Class Node is the top of the simulation object hierarchy. It
* defines the most general interface to a network element.
*
* Class Node provide the interface for
* - updating the dynamic state of an object
* - connecting nodes, using particular Events
* - accepting connection requests
* - handling incoming events
* A new type of Node must be derived from this base class and
* implement its interface.
* In order to keep the inheritance hierarchy flat, it is encouraged
* to direcly subclass from base class Node.
*
* @see class Event
* @see Subnet
* @ingroup user_interface
*/
/* BeginDocumentation
Name: Node - General properties of all nodes.
Parameters:
frozen booltype - Whether the node is updated during simulation
global_id integertype - The global id of the node (cf. local_id)
local booltype - Whether the node is available on the local process
local_id integertype - The id of the node in the current (cf. global_id)
model literaltype - The model type the node was created from
parent integertype - The global id of the parent subnet
state integertype - The state of the node (see the help on elementstates for details)
thread integertype - The id of the thread the node is assigned to (valid locally)
vp integertype - The id of the virtual process the node is assigned to (valid globally)
SeeAlso: GetStatus, SetStatus, elementstates
*/
class Node
{
friend class Network;
friend class Scheduler;
friend class Subnet;
friend class proxynode;
friend class Synapse;
friend class Model;
Node& operator=(const Node&); //!< not implemented
public:
/**
* Enumeration for status flags.
* @see test(flag)
* @see set(flag)
* @see unset(flag)
* @see flip(flag)
* @todo Review set of states and transition rules, as well as use.
*/
enum flag
{
valid=0, //!< default element state
busy, //!< element is not fully updated
updated, //!< update was completed successfully
suspended, //!< update was suspended
frozen, //!< element or branch is "frozen"
buffers_initialized, //!< set if buffers are initialized
err, //!< some error has occoured
n_flags
};
Node();
/**
* The copy constructor assumes that information from
* the original object is needed to complete the construction.
* Thus, we merely copy all fields and just set the state flag
* to "incomplete", to indicate that construction is not
* ready yet.
*/
Node(Node const &);
virtual ~Node();
/**
* Virtual copy constructor.
* This function should create a new object by
* calling the derived class' copy constructor and
* return its pointer.
*/
virtual Node *clone() const{ return 0;}
/**
* Returns true if the node has proxies on remote threads. This is
* used to discriminate between different types of nodes, when adding
* new nodes to the network.
*/
virtual bool has_proxies() const;
/**
* Returns true if the node only receives events from nodes/devices
* on the same thread.
*/
virtual bool local_receiver() const;
/**
* Returns true if the node exists only once per process, but does
* not have proxies on remote threads. This is used to
* discriminate between different types of nodes, when adding new
* nodes to the network.
*/
virtual bool one_node_per_process() const;
/**
* Returns true if the node if it sends/receives -grid events This is
* used to discriminate between different types of nodes, when adding
* new nodes to the network.
*/
virtual bool is_off_grid() const;
/**
* Returns true if the node is a proxy node. This is implemented because
* the use of RTTI is rather expensive.
*/
virtual bool is_proxy() const;
/**
* Return class name.
* Returns name of node model (e.g. "iaf_neuron") as string.
* This name is identical to the name that is used to identify
* the model in the interpreter's model dictionary.
*/
std::string get_name() const;
virtual
void register_connector(nest::Connector&) {}
/**
* Return global Network ID.
* Returns the global network ID of the Node.
* Each node has a unique network ID which can be used to access
* the Node comparable to a pointer. By definition, the top-level
* subnet has ID=0.
*/
index get_gid() const;
/**
* Return local node ID.
* Returns the ID of the node within the parent subject.
* Local IDs start with 0.
*/
index get_lid() const;
/**
* Return the index to the node in the node array of the parent subnet.
* @note Since subnets no longer store non-local nodes, LIDs are no
* longer identical to these indices.
*/
index get_subnet_index() const;
/**
* Return model ID of the node.
* Returns the model ID of the model for this node.
* Model IDs start with 0, Subnet always having ID 0.
* @note The model ID is not stored in the model prototype instance.
* It is only set when actual nodes are created from a prototype.
*/
int get_model_id() const;
/**
* Return pointer to parent subnet.
* Each node is member of a subnet whose pointer can be accessed
* through this function.
* This pointer must be non NULL for all Nodes which are not the
* top-level subnet. Only the top-level subnet returns NULL.
*/
Subnet* get_parent() const;
/************************************************
* Functions to modify and test state flags
*/
/**
* Return status flag of node.
* @see enum flag
*/
index get_status_flag() const;
/**
* Clear all status flags of node.
* @see enum flag
*/
void reset_status_flag();
/**
* Prints out one line of the tree view of the network.
*/
virtual
std::string print_network(int , int, std::string = "") {return std::string();}
/**
* Flip one or more state flags.
* @param flag Bit mask, representing the flags to be flipped.
*/
void flip(flag);
/**
* Set one or more state flags.
* @param flag Bit mask, representing the flags to be set.
*/
void set(flag);
/**
* Unset one or more state flags.
* @param flag Bit mask, representing the flags to be cleared.
*/
void unset(flag);
/**
* Test one or more state flags.
* @param flag Bit mask, representing the flags to be tested.
* @return Returns true, if all flags in the mask are set and false
* otherwise.
*/
bool test(flag) const;
/**
* Returns true if the node has been updated during the current time-slice.
* Each simulation step brings all nodes from state $t$ to state $t+dt$.
* At the beginning of a time slice, the system clock matches the states
* of all nodes in the network. In this state, is_updated() will return false for
* all nodes.
* Each node which is updated has advanced to state $t+dt$. Its local state is ahead of the
* ststem clock.
* is_updated() will return true if the node is in state $t+dt$.
* is_updated() will return false if the node is still in state $t$.
* @node The state for the updated_ flag is only indirectly coupled to the node's
* is_updated state.
*/
bool is_updated() const;
/**
* Returns true if frozen flag is true.
*/
bool is_frozen() const;
/**
* Return pointer to network driver class.
* @todo This member should return a reference, not a pointer.
*/
static Network* network();
/**
* Returns true if the node is allocated in the local process.
*/
bool is_local() const;
/**
* Set state variables to the default values for the model.
* Dynamic variables are all observable state variables of a node
* that change during Node::update().
* After calling init_state(), the state variables
* should have the same values that they had after the node was
* created. In practice, they will be initialized to the values
* of the prototype node (model).
* @note If the parameters of the model have been changes since the node
* was created, the node will be initialized to the present values
* set in the model.
* @note This function is the public interface to the private function
* Node::init_state_(const Node&) that must be implemented by
* derived classes.
*/
void init_state();
/**
* Initialize buffers of a node.
* This function initializes the Buffers of a Node, e.g., ring buffers
* for incoming events, buffers for logging potentials.
* This function is called before Simulate is called for the first time
* on a node, but not upon resumption of a simulation.
* This is a wrapper function, which calls the overloaded Node::init_buffers_()
* worker only if the buffers of the node have not been initialized yet. It
* then sets the buffers_initialized flag.
*/
void init_buffers();
/**
* Re-calculate dependent parameters of the node.
* This function is called each time a simulation is begun/resumed.
* It must re-calculate all internal Variables of the node required
* for spike handling or updating the node.
*
*/
virtual void calibrate()=0;
/**
* Finalize node.
* Override this function if a node needs to "wrap up" things after a simulation,
* i.e., before Scheduler::resume() returns. Typical use-cases are devices
* that need to flush buffers or disconnect from external files or pipes.
*/
virtual void finalize() {}
/**
* Bring the node from state $t$ to $t+n*dt$.
*
* n->update(T, from, to) performs the update steps beginning
* at T+from .. T+to-1, ie, emitting events with time stamps
* T+from+1 .. T+to.
*
* @param Time network time at beginning of time slice.
* @param long_t initial step inside time slice
* @param long_t post-final step inside time slice
*
*/
virtual
void update(Time const &, const long_t, const long_t)=0;
/**
* @defgroup status_interface Configuration interface.
* Functions and infrastructure, responsible for the configuration
* of Nodes from the SLI Interpreter level.
*
* Each node can be configured from the SLI level through a named
* parameter interface. In order to change parameters, the user
* can specify name value pairs for each parameter. These pairs
* are stored in a data structure which is called Dictionary.
* Likewise, the user can query the configuration of any node by
* requesting a dictionary with name value pairs.
*
* The configuration interface consists of four functions which
* implement storage and retrieval of named parameter sets.
*/
/**
* Change properties of the node according to the
* entries in the dictionary.
* @param d Dictionary with named parameter settings.
* @ingroup status_interface
*/
virtual
void set_status(const DictionaryDatum&)=0;
/**
* Export properties of the node by setting
* entries in the status dictionary.
* @param d Dictionary.
* @ingroup status_interface
*/
virtual
void get_status(DictionaryDatum&) const=0;
public:
/**
* @defgroup event_interface Communication.
* Functions and infrastructure, responsible for communication
* between Nodes.
*
* Nodes communicate by sending an receiving events. The
* communication interface consists of two parts:
* -# Functions to handle incoming events.
* -# Functions to check if a connection between nodes is possible.
*
* @see Event
*/
/**
* This function checks if the receiver accepts the connection by creating an
* instance of the event type it sends in its update() function and passing it
* to connect_sender() of the receiver. Afterwards it has to return the event
* so that the Connection object can check if it supports the type of event.
*/
virtual
port check_connection(Connection& c, port receptor);
/**
* Register a STDP connection
*
* @throws IllegalConnection
*
*/
virtual
void register_stdp_connection(double_t);
/**
* Unregister a STDP connection
*
* @throws IllegalConnection
*
*/
virtual
void unregister_stdp_connection(double_t);
/**
* Handle incoming spike events.
* @param thrd Id of the calling thread.
* @param e Event object.
*
* This handler has to be implemented if a Node should
* accept spike events.
* @see class SpikeEvent
* @ingroup event_interface
*/
virtual
void handle(SpikeEvent& e);
/**
* Request a connection with the receiver.
* @param e Fully initialised event object.
* @retval Receiver port number, or 0 if unused.
* A negative receiver port is a request to the sender
* to cancel the connection without issueing an error.
*
* @throws IllegalConnection
*
* connect_sender() function is used to verify that the receiver
* can handle the event. It can also be used by the receiver to
* store information about an event sender.
* The default definition throws an IllegalConnection
* exception. Any node class should define empty connect_sender
* functions for all those event types it can handle.
*
* connect_sender() is called by the register_connection() method
* of the respective connector object.
*
* @note The semantics of all other connect_sender() functions is identical.
* @ingroup event_interface
* @throws IllegalConnection
*/
virtual
port connect_sender(SpikeEvent& e, port receptor);
/**
* Handler for rate events.
* @see handle(SpikeEvent&)
* @ingroup event_interface
* @throws UnexpectedEvent
*/
virtual
void handle(RateEvent& e);
/**
* Establish connection for rate events (receiver).
* @see connect_sender(SpikeEvent&)
* @ingroup event_interface
*/
virtual
port connect_sender(RateEvent& e, port receptor);
/**
* Handler for universal data logging request.
* @see handle(SpikeEvent&)
* @ingroup event_interface
* @throws UnexpectedEvent
*/
virtual
void handle(DataLoggingRequest& e);
/**
* Establish connection for data logging request (receiver).
* @see connect_sender(SpikeEvent&, port)
* @ingroup event_interface
* @throws IllegalConnection
*/
virtual
port connect_sender(DataLoggingRequest&, port);
/**
* Handler for universal data logging request.
* @see handle(SpikeEvent&)
* @ingroup event_interface
* @throws UnexpectedEvent
* @note There is no connect_sender() for DataLoggingReply, since
* this event is only used as "back channel" for DataLoggingRequest.
*/
virtual
void handle(DataLoggingReply& e);
/**
* Handler for current events.
* @see handle(thread, SpikeEvent&)
* @ingroup event_interface
* @throws UnexpectedEvent
*/
virtual
void handle(CurrentEvent& e);
/**
* Establish connection for current events (receiver).
* @see connect_sender(SpikeEvent&)
* @ingroup event_interface
* @throws IllegalConnection
*/
virtual
port connect_sender(CurrentEvent& e, port receptor);
/**
* Handler for conductance events.
* @see handle(thread, SpikeEvent&)
* @ingroup event_interface
* @throws UnexpectedEvent
*/
virtual
void handle(ConductanceEvent& e);
/**
* Establish connection for conductance events (receiver).
* @see connect_sender(SpikeEvent&)
* @ingroup event_interface
* @throws IllegalConnection
*/
virtual
port connect_sender(ConductanceEvent& e, port receptor);
/**
* Handler for DoubleData events.
* @see handle(thread, SpikeEvent&)
* @ingroup event_interface
* @throws UnexpectedEvent
*/
virtual
void handle(DoubleDataEvent& e);
/**
* Establish connection for DoubleData events (receiver).
* @see connect_sender(SpikeEvent&)
* @ingroup event_interface
* @throws IllegalConnection
*/
virtual
port connect_sender(DoubleDataEvent& e, port receptor);
/**
* return the Kminus value at t (in ms).
* @throws UnexpectedEvent
*/
virtual
double_t get_K_value(double_t t);
/**
* write the Kminus and triplet_Kminus values at t (in ms) to
* the provided locations.
* @throws UnexpectedEvent
*/
virtual
void get_K_values(double_t t, double_t& Kminus, double_t& triplet_Kminus);
/**
* return the spike history for (t1,t2].
* @throws UnexpectedEvent
*/
virtual
void get_history(double_t t1, double_t t2,
std::deque<histentry>::iterator* start,
std::deque<histentry>::iterator* finish);
/**
* Modify Event object parameters during event delivery.
* 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(thread, *this)
* in its operator() function instead of calling target->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.
*/
virtual
void event_hook(DSSpikeEvent&);
virtual
void event_hook(DSCurrentEvent&);
/**
* Store the number of the thread to which the node is assigned.
* The assignment is done after node creation by the Network class.
* @see: Network::add_node().
*/
void set_thread(thread);
/**
* Retrieve the number of the thread to which the node is assigned.
*/
thread get_thread() const;
/**
* Store the number of the virtual process to which the node is assigned.
* This is assigned to the node in network::add_node().
*/
void set_vp(thread);
/**
* Retrieve the number of the virtual process to which the node is assigned.
*/
thread get_vp() const;
/** Set the model id.
* This method is called by Network::add_node() when a node is created.
* @see get_model_id()
*/
void set_model_id(int);
/**
* @returns true if node can be entered with the NestModule::ChangeSubnet()
* commands (only true for Subnets).
*/
virtual bool allow_entry() const;
/**
* Return a dictionary with the node's properties.
*
* get_status_base() first gets a dictionary with the basic
* information of an element, using get_status_dict_(). It then
* calls the custom function get_status(DictionaryDatum) with
* the created status dictionary as argument.
*/
DictionaryDatum get_status_base();
/**
* Set status dictionary of a node.
*
* set_status_base() manages the frozen flag of the node itself
* and then invokes the set_status() of the derived class.
* @internal
*/
void set_status_base(const DictionaryDatum&);
/**
* Returns true if node is model prototype.
*/
bool is_model_prototype() const;
private:
void set_lid_(index); //!< Set local id, relative to the parent subnet
void set_parent_(Subnet *); //!< Set pointer to parent subnet.
void set_gid_(index); //!< Set global node id
void set_subnet_index_(index);//!< Index into node array in subnet
/** Return a new dictionary datum .
*
* This function is called by get_status_base() and returns a new
* empty dictionary by default. Some nodes may contain a
* permanent status dictionary which is then returned by
* get_status_dict_().
*/
virtual
DictionaryDatum get_status_dict_();
protected:
/**
* Return the number of thread siblings in SiblingContainer.
*
* This method is meaningful only for SiblingContainer, for which it
* returns the number of siblings in the container.
* For all other models (including Subnet), it returns 0, which is not
* wrong. By defining the method in this way, we avoid many dynamic casts.
*/
virtual
size_t num_thread_siblings_() const { return 0;}
/**
* Return the specified member of a SiblingContainer.
*
* This method is meaningful only for SiblingContainer, for which it
* returns the pointer to the indexed node in the container.
* For all other models (including Subnet), it returns a null pointer
* and throws and assertion.By defining the method in this way, we avoid
* many dynamic casts.
*/
virtual
Node* get_thread_sibling_(index) const { assert(false); return 0; }
/**
* Return specified member of a SiblingContainer, with access control.
*/
virtual
Node* get_thread_sibling_safe_(index) const { assert(false); return 0; }
/**
* Private function to initialize the state of a node to model defaults.
* This function, which must be overloaded by all derived classes, provides
* the implementation for initializing the state of a node to the model defaults;
* the state is the set of observable dynamic variables.
* @param Reference to model prototype object.
* @see Node::init_state()
* @note To provide a reasonable behavior during the transition to the new scheme,
* init_state_() has a default implementation calling init_dynamic_state_().
*/
virtual void init_state_(Node const&) =0;
/**
* Private function to initialize the buffers of a node.
* This function, which must be overloaded by all derived classes, provides
* the implementation for initializing the buffers of a node.
* @see Node::init_buffers()
*/
virtual void init_buffers_() =0;
Model & get_model_() const;
/**
* Auxiliary function to downcast a Node to a concrete class derived from Node.
* @note This function is used to convert generic Node references to specific
* ones when intializing parameters or state from a prototype.
*/
template <typename ConcreteNode>
const ConcreteNode& downcast(const Node&);
private:
index gid_; //!< Global element id (within network).
index lid_; //!< Local element id (within parent).
index subnet_index_; //!< Index of node in parent's node array
/**
* Model ID.
* @see get_model_id(), set_model_id()
*/
int model_id_;
Subnet *parent_; //!< Pointer to parent.
std::bitset<n_flags> stat_; //!< enum flag as bit mask.
thread thread_; //!< thread node is assigned to
thread vp_; //!< virtual process node is assigned to
protected:
static Network* net_; //!< Pointer to global network driver.
};
inline
index Node::get_status_flag() const
{
return stat_.to_ulong();
}
inline
void Node::reset_status_flag()
{
stat_.reset();
}
inline
bool Node::test(enum flag f) const
{
return stat_.test(f);
}
inline
void Node::set(enum flag f)
{
stat_.set(f);
}
inline
void Node::unset(enum flag f)
{
stat_.reset(f);
}
inline
void Node::flip(enum flag f)
{
stat_[f].flip();
}
inline
bool Node::is_frozen() const
{
return stat_.test(frozen);
}
inline
bool Node::has_proxies() const
{
return true;
}
inline
bool Node::local_receiver() const
{
return false;
}
inline
bool Node::one_node_per_process() const
{
return false;
}
inline
bool Node::is_off_grid() const
{
return false;
}
inline
bool Node::is_proxy() const
{
return false;
}
inline
index Node::get_lid() const
{
return lid_;
}
inline
index Node::get_gid() const
{
return gid_;
}
inline
index Node::get_subnet_index() const
{
return subnet_index_;
}
inline
void Node::set_gid_(index i)
{
gid_=i;
}
inline
void Node::set_lid_(index i)
{
lid_=i;
}
inline
void Node::set_subnet_index_(index i)
{
subnet_index_ = i;
}
inline
int Node::get_model_id() const
{
return model_id_;
}
inline
void Node::set_model_id(int i)
{
model_id_ = i;
}
inline
bool Node::is_model_prototype() const
{
return vp_ == invalid_thread_;
}
inline
Subnet * Node::get_parent() const
{
return parent_;
}
inline
void Node::set_parent_(Subnet *c)
{
parent_=c;
}
inline
Network* Node::network()
{
return net_;
}
inline
void Node::set_thread(thread t)
{
thread_ = t;
}
inline
thread Node::get_thread() const
{
return thread_;
}
inline
void Node::set_vp(thread vp)
{
vp_ = vp;
}
inline
thread Node::get_vp() const
{
return vp_;
}
template <typename ConcreteNode>
const ConcreteNode& Node::downcast(const Node& n)
{
ConcreteNode const* tp = dynamic_cast<ConcreteNode const*>(&n);
assert(tp != 0);
return *tp;
}
} // namespace
#endif