#! /usr/bin/env python
#
# hl_api.py
#
# 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/>.
"""
High-level API of PyNEST.
This file defines the user-level functions of NEST's Python interface
by mapping NEST/SLI commands to Python. Please try to follow these
rules:
1. SLI commands have the same name in Python. This means that most
function names are written in camel case, although the Python
guidelines suggest to use lower case for funtcion names. However,
this way, it is easier for users to migrate from SLI to Python and.
2. Nodes are identified by their global IDs (GID) by default.
3. GIDs are always written as lists, e.g. [0], [1,2]
4. Commands that return a GID must return it as list of GID(s).
5. When possible, loops over nodes should be propagated down to the
SLI level. This minimizes the number of Python<->SLI conversions
and increases performance. Loops in SLI are also faster than in
Python.
6. If you have a *very* good reason, you may deviate from these guidelines.
Authors: Jochen Eppler, Marc-Oliver Gewaltig, Moritz Helias, Eilif Mueller
"""
import string
import types
# These variables MUST be set by __init__.py right after importing.
# There is no safety net, whatsoever.
nest = sps = spp = sr = None
class NESTError(Exception):
def __init__(self, msg) :
Exception.__init__(self, msg)
# -------------------- Helper functions
def is_ndarray(seq):
try:
import numpy
return type(seq) == numpy.ndarray
except:
return False
def is_sequencetype(seq) :
"""
Return True if the given object is a sequence type, False else
"""
import sys
return (type(seq) in (types.TupleType, types.ListType)) or is_ndarray(seq)
def is_iterabletype(seq) :
"""
Return True if the given object is iterable, False else
"""
try:
i = iter(seq)
except TypeError:
return False
return True
def is_sequence_of_nonneg_ints(seq):
"""
Return True if the given object is a list or tuple of ints, False else
"""
return is_sequencetype(seq) and all([isinstance(n,int) and n >= 0 for n in seq])
def raise_if_not_list_of_gids(seq, argname):
"""
Raise a NestError if seq is not a sequence of ints, otherwise, do nothing.
The main purpose of this function is to perform a simple check that an
argument is a potentially valid list of GIDs (ints >= 0).
"""
if not is_sequence_of_nonneg_ints(seq):
raise NESTError(argname + " must be a list or tuple of GIDs")
def broadcast(val, l, allowedtypes, name="val"):
if type(val) in allowedtypes:
return l*(val,)
elif len(val)==1:
return l*val
elif len(val)!=l:
raise NESTError("'%s' must be a single value, a list with one element or a list with %i elements." % (name, l))
return val
def flatten(x):
"""
flatten(sequence) -> list
Returns a flat list with all elements from the sequence and all
contained sub-sequences (iterables).
Examples:
>>> [1, 2, [3,4], (5,6)]
[1, 2, [3, 4], (5, 6)]
>>> flatten([[[1,2,3], (42,None)], [4,5], [6], 7, MyVector(8,9,10)])
[1, 2, 3, 42, None, 4, 5, 6, 7, 8, 9, 10]
"""
result = []
for el in x:
if hasattr(el, "__iter__") and not isinstance(el, basestring) and not type(el) == types.DictType:
result.extend(flatten(el))
else:
result.append(el)
return result
# -------------------- Functions to get information on NEST
def sysinfo():
"""
Print information on the platform on which NEST was compiled.
"""
sr("sysinfo")
def version():
"""
Return the NEST version.
"""
sr("statusdict [[ /kernelname /version ]] get")
return string.join(spp())
def authors():
"""
Print the authors of NEST.
"""
sr("authors")
def helpdesk(browser="firefox"):
"""
Open the NEST helpdesk in the given browser. The default browser is firefox.
"""
sr("/helpdesk << /command (%s) >> SetOptions" % browser)
sr("helpdesk")
def help(obj=None, pager="less"):
"""
Show the help page for the given object using the given pager. The
default pager is less.
"""
if obj:
sr("/page << /command (%s) >> SetOptions" % pager)
sr("/%s help" % obj)
else:
print "Type 'nest.helpdesk()' to access the online documentation in a browser."
print "Type 'nest.help(object)' to get help on a NEST object or command."
print
print "Type 'nest.Models()' to see a list of available models in NEST."
print
print "Type 'nest.authors()' for information about the makers of NEST."
print "Type 'nest.sysinfo()' to see details on the system configuration."
print "Type 'nest.version()' for information about the NEST version."
print
print "For more information visit http://www.nest-initiative.org."
def get_verbosity():
"""
Return verbosity level of NEST's messages.
"""
sr('verbosity')
return spp()
def set_verbosity(level):
"""
Change verbosity level for NEST's messages. level is a string and
can be one of M_FATAL, M_ERROR, M_WARNING, or M_INFO.
"""
sr("%s setverbosity" % level)
def message(level,sender,text):
"""
Print a message using NEST's message system.
"""
sps(level)
sps(sender)
sps(text)
sr('message')
# -------------------- Functions for simulation control
def Simulate(t):
"""
Simulate the network for t milliseconds.
"""
sps(float(t))
sr('ms Simulate')
def ResumeSimulation():
"""
Resume an interrupted simulation.
"""
sr("ResumeSimulation")
def ResetKernel():
"""
Reset the simulation kernel. This will destroy the network as
well as all custom models created with CopyModel(). Calling this
function is equivalent to restarting NEST.
"""
sr('ResetKernel')
def ResetNetwork():
"""
Reset all nodes and connections to their original state.
"""
sr('ResetNetwork')
def SetKernelStatus(params):
"""
Set parameters for the simulation kernel.
"""
sps(0)
sps(params)
sr('SetStatus')
def GetKernelStatus(keys = None):
"""
Obtain parameters of the simulation kernel.
Returns:
- Parameter dictionary if called without argument
- Single parameter value if called with single parameter name
- List of parameter values if called with list of parameter names
"""
sr('0 GetStatus')
rootstatus = spp()
sr('/subnet GetDefaults')
subnetdefaults = spp()
subnetdefaults["frozen"] = None
subnetdefaults["global_id"] = None
subnetdefaults["local"] = None
subnetdefaults["local_id"] = None
subnetdefaults["parent"] = None
subnetdefaults["state"] = None
subnetdefaults["thread"] = None
subnetdefaults["vp"] = None
d = dict()
for k in rootstatus :
if k not in subnetdefaults :
d[k] = rootstatus[k]
if not keys:
return d
elif is_sequencetype(keys):
return [d[k] for k in keys]
else:
return d[keys]
def Install(module_name):
"""
Load a dynamically linked NEST module.
Example:
nest.Install("mymodule")
Returns:
NEST module identifier, required for unloading.
Note: Dynamically linked modules are searched in the
LD_LIBRARY_PATH (DYLD_LIBRARY_PATH under OSX).
"""
return sr("(%s) Install" % module_name)
# -------------------- Functions for parallel computing
def Rank():
"""
Return the MPI rank of the local process.
"""
sr("Rank")
return spp()
def NumProcesses():
"""
Return the overall number of MPI processes.
"""
sr("NumProcesses")
return spp()
def SetAcceptableLatency(port, latency):
"""
Set the acceptable latency (in ms) for a MUSIC port.
"""
sps(latency)
sr("/%s exch SetAcceptableLatency" % port)
# -------------------- Functions for model handling
def Models(mtype = "all", sel=None):
"""
Return a list of all available models (neurons, devices and
synapses). Use mtype='nodes' to only see neuron and device models,
mtype='synapses' to only see synapse models. sel can be a string,
used to filter the result list and only return models containing
it.
"""
if mtype not in ("all", "nodes", "synapses"):
raise NESTError("type has to be one of 'all', 'nodes' or 'synapses'.")
models = list()
if mtype in ("all", "nodes"):
sr("modeldict")
models += spp().keys()
if mtype in ("all", "synapses"):
sr("synapsedict")
models += spp().keys()
if sel != None:
models = filter(lambda x: x.find(sel) >= 0, models)
models.sort()
return models
def SetDefaults(model, params, val=None) :
"""
Set the default parameters of the given model to the values
specified in the params dictionary.
If val is given, params has to be the name of a model property.
New default values are used for all subsequently created instances
of the model.
"""
if type(params) == types.StringType :
params = {params : val}
sr('/'+model)
sps(params)
sr('SetDefaults')
def GetDefaults(model, keys=None) :
"""
Return a dictionary with the default parameters of the given
model, specified by a string.
If keys is given, it must be a string or a list of strings naming model properties.
GetDefaults then returns a single value or a list of values belonging to the keys given.
Examples:
GetDefaults('iaf_neuron','V_m') -> -70.0
GetDefaults('iaf_neuron',['V_m', 'model') -> [-70.0, 'iaf_neuron']
"""
cmd = "/%s GetDefaults" % model
if keys:
if is_sequencetype(keys):
keyss = string.join(["/%s" % x for x in keys])
cmd='/'+model+' GetDefaults [ %s ] { 1 index exch get} Map' % keyss
else:
cmd= '/'+model+' GetDefaults '+'/'+keys+' get'
sr(cmd)
return spp()
def CopyModel(existing, new, params=None):
"""
Create a new model by copying an existing one. Default parameters
can be given as params, or else are taken from existing.
"""
if params:
sps(params)
sr("/%s /%s 3 2 roll CopyModel" % (existing, new))
else:
sr("/%s /%s CopyModel" % (existing, new))
# -------------------- Functions for node handling
def Create(model, n=1, params=None):
"""
Create n instances of type model. Parameters for the new nodes can
are given as params (a single dictionary or a list of dictionaries
with size n). If omitted, the model's defaults are used.
"""
broadcast_params = False
sps(n)
cmd = "/%s exch Create" % model
if params:
if type(params) == types.DictType:
sps(params)
cmd = "/%s 3 1 roll Create" % model
elif is_sequencetype(params) and (len(params) == 1 or len(params) == n):
broadcast_params = True
else:
sr(";") # pop n from the stack
raise NESTError("params has to be a single dictionary or a list of dictionaries with size n.")
sr(cmd)
lastgid = spp()
ids = range(lastgid - n + 1, lastgid + 1)
if broadcast_params:
try:
SetStatus(ids, broadcast(params, n, (dict,)))
except:
raise NESTError("SetStatus failed, but nodes already have been created. The ids of the new nodes are: %s" % ids)
return ids
def SetStatus(nodes, params, val=None) :
"""
Set the parameters of nodes (identified by global ids) or
connections (identified by handles as returned by
GetConnections()) to params, which may be a single dictionary or a
list of dictionaries. If val is given, params has to be the name
of an attribute, which is set to val on the nodes/connections. val
can be a single value or a list of the same size as nodes.
"""
if not is_sequencetype(nodes):
raise NESTError("nodes must be a list of nodes or synapses.")
if len(nodes) == 0:
return
if type(params) == types.StringType :
if is_iterabletype(val) and not type(val) in (types.StringType, types.DictType):
params = [{params : x} for x in val]
else :
params = {params : val}
params = broadcast(params, len(nodes), (dict,), "params")
if len(nodes) != len(params) :
raise NESTError("Status dict must be a dict, or list of dicts of length 1 or len(nodes).")
if (type(nodes[0]) == types.DictType) or is_sequencetype(nodes[0]):
nest.push_connection_datums(nodes)
else:
sps(nodes)
sps(params)
sr('2 arraystore')
sr('Transpose { arrayload ; SetStatus } forall')
def GetStatus(nodes, keys=None) :
"""
Return the parameter dictionaries of the given list of nodes
(identified by global ids) or connections (identified
by handles as returned by GetConnections()). If keys is given, a
list of values is returned instead. keys may also be a list, in
which case the returned list contains lists of values.
"""
if not is_sequencetype(nodes):
raise NESTError("nodes must be a list of nodes or synapses.")
if len(nodes) == 0:
return nodes
cmd='{ GetStatus } Map'
if keys:
if is_sequencetype(keys):
keyss = string.join(["/%s" % x for x in keys])
cmd='{ GetStatus } Map { [ [ %s ] ] get } Map' % keyss
else:
cmd='{ GetStatus /%s get} Map' % keys
if (type(nodes[0]) == types.DictType) or is_sequencetype(nodes[0]):
nest.push_connection_datums(nodes)
else:
sps(nodes)
sr(cmd)
return spp()
def GetLID(gid) :
"""
Return the local id of a node with gid.
GetLID(gid) -> lid
"""
if len(gid) > 1:
raise NESTError("GetLID() expects exactly one GID.")
sps(gid[0])
sr("GetLID")
return spp()
# -------------------- Functions for connection handling
def FindConnections(source, target=None, synapse_model=None, synapse_type=None):
"""
Return an array of identifiers for connections that match the
given parameters. Only source is mandatory and must be a list of
one or more nodes. If target and/or synapse_model is/are given,
they must be single values, lists of length one or the same length
as source. Use GetStatus()/SetStatus() to inspect/modify the found
connections.
Note: FindConnections() is deprecated and will be removed in the future.
Use GetConnections() instead.
Note: synapse_type is alias for synapse_model for backward compatibility
"""
if synapse_model and synapse_type:
raise NESTError("synapse_type is alias for synapse_model, cannot be used together.")
if synapse_type:
synapse_model = synapse_type
if not target and not synapse_model:
params = [{"source": s} for s in source]
if not target and synapse_model:
synapse_model = broadcast(synapse_model, len(source), (str,), "synapse_model")
params = [{"source": s, "synapse_model": syn}
for s, syn in zip(source, synapse_model)]
if target and not synapse_model:
target = broadcast(target, len(source), (int,), "target")
params = [{"source": s, "target": t} for s, t in zip(source, target)]
if target and synapse_model:
target = broadcast(target, len(source), (int,), "target")
synapse_model = broadcast(synapse_model, len(source), (str,), "synapse_model")
params = [{"source": s, "target": t, "synapse_model": syn}
for s, t, syn in zip(source, target, synapse_model)]
sps(params)
sr("{FindConnections} Map Flatten")
result=spp()
return [ { 'source':int(r[0]), 'target_thread': int(r[2]),
'synapse_modelid': int(r[3]), 'port': int(r[4])} for r in result ]
def GetConnections(source=None, target=None, synapse_model=None) :
"""
Return an array of connection identifiers.
Parameters:
source - list of source GIDs
target - list of target GIDs
synapse_model - string with the synapse model
If GetConnections is called without parameters, all connections
in the network are returned.
If a list of source neurons is given, only connections from these
pre-synaptic neurons are returned.
If a list of target neurons is given, only connections to these
post-synaptic neurons are returned.
If a synapse model is given, only connections with this synapse
type are returned.
Any combination of source, target and synapse_model parameters
is permitted.
Each connection id is a 5-tuple or, if available, a NumPy
array with the following fived entries:
source-gid, target-gid, target-thread, synapse-id, port
Note: Only connections with targets on the MPI process executing
the command are returned.
"""
params={}
if source:
if not is_sequencetype(source):
raise NESTError("source must be a list of gids.")
params['source'] = source
if target:
if not is_sequencetype(target):
raise NESTError("target must be a list of gids.")
params['target'] = target
sps(params)
if synapse_model:
# add model to params dict as literal
sr('dup /synapse_model /{0} put_d'.format(synapse_model))
sr("GetConnections")
return spp()
def Connect(pre, post, params=None, delay=None, model="static_synapse"):
"""
Make one-to-one connections of type model between the nodes in
pre and the nodes in post. pre and post have to be lists of the
same length. If params is given (as dictionary or list of
dictionaries), they are used as parameters for the connections. If
params is given as a single float or as list of floats, it is used
as weight(s), in which case delay also has to be given as float or
as list of floats.
"""
if len(pre) != len(post):
raise NESTError("pre and post have to be the same length")
# pre post Connect
if params == None and delay == None:
for s,d in zip(pre, post):
sps(s)
sps(d)
sr('/%s Connect' % model)
# pre post params Connect
elif params != None and delay == None:
params = broadcast(params, len(pre), (dict,), "params")
if len(params) != len(pre):
raise NESTError("params must be a dict, or list of dicts of length 1 or len(pre).")
for s,d,p in zip(pre, post, params) :
sps(s)
sps(d)
sps(p)
sr('/%s Connect' % model)
# pre post w d Connect
elif params != None and delay != None:
params = broadcast(params, len(pre), (float,), "params")
if len(params) != len(pre):
raise NESTError("params must be a float, or list of floats of length 1 or len(pre) and will be used as weight(s).")
delay = broadcast(delay, len(pre), (float,), "delay")
if len(delay) != len(pre):
raise NESTError("delay must be a float, or list of floats of length 1 or len(pre).")
for s,d,w,dl in zip(pre, post, params, delay) :
sps(s)
sps(d)
sps(w)
sps(dl)
sr('/%s Connect' % model)
else:
raise NESTError("Both 'params' and 'delay' have to be given.")
def ConvergentConnect(pre, post, weight=None, delay=None, model="static_synapse"):
"""
Connect all neurons in pre to each neuron in post. pre and post
have to be lists. If weight is given (as a single float or as list
of floats), delay also has to be given as float or as list of
floats.
"""
if weight == None and delay == None:
for d in post :
sps(pre)
sps(d)
sr('/%s ConvergentConnect' % model)
elif weight != None and delay != None:
weight = broadcast(weight, len(pre), (float,), "weight")
if len(weight) != len(pre):
raise NESTError("weight must be a float, or sequence of floats of length 1 or len(pre)")
delay = broadcast(delay, len(pre), (float,), "delay")
if len(delay) != len(pre):
raise NESTError("delay must be a float, or sequence of floats of length 1 or len(pre)")
for d in post:
sps(pre)
sps(d)
sps(weight)
sps(delay)
sr('/%s ConvergentConnect' % model)
else:
raise NESTError("Both 'weight' and 'delay' have to be given.")
def RandomConvergentConnect(pre, post, n, weight=None, delay=None, model="static_synapse", options=None):
"""
Connect n randomly selected neurons from pre to each neuron in
post. pre and post have to be lists. If weight is given (as a
single float or as list of floats), delay also has to be given as
float or as list of floats. options is a dictionary specifying
options to the RandomConvergentConnect function: allow_autapses,
allow_multapses.
"""
# store current options, set desired options
old_options = None
error = False
if options:
old_options = sli_func('GetOptions', '/RandomConvergentConnect',
litconv=True)
del old_options['DefaultOptions'] # in the way when restoring
sli_func('SetOptions', '/RandomConvergentConnect', options,
litconv=True)
if weight == None and delay == None:
sli_func(
'/m Set /n Set /pre Set { pre exch n m RandomConvergentConnect } forall',
post, pre, n, '/'+model, litconv=True)
elif weight != None and delay != None:
weight = broadcast(weight, n, (float,), "weight")
if len(weight) != n:
raise NESTError("weight must be a float, or sequence of floats of length 1 or n")
delay = broadcast(delay, n, (float,), "delay")
if len(delay) != n:
raise NESTError("delay must be a float, or sequence of floats of length 1 or n")
sli_func(
'/m Set /d Set /w Set /n Set /pre Set { pre exch n w d m RandomConvergentConnect } forall',
post, pre, n, weight, delay, '/'+model, litconv=True)
else:
error = True
# restore old options
if old_options:
sli_func('SetOptions', '/RandomConvergentConnect', old_options,
litconv=True)
if error:
raise NESTError("Both 'weight' and 'delay' have to be given.")
def DivergentConnect(pre, post, weight=None, delay=None, model="static_synapse"):
"""
Connect each neuron in pre to all neurons in post. pre and post
have to be lists. If weight is given (as a single float or as list
of floats), delay also has to be given as float or as list of
floats.
"""
if weight == None and delay == None:
for s in pre :
sps(s)
sps(post)
sr('/%s DivergentConnect' % model)
elif weight != None and delay != None:
weight = broadcast(weight, len(post), (float,), "weight")
if len(weight) != len(post):
raise NESTError("weight must be a float, or sequence of floats of length 1 or len(post)")
delay = broadcast(delay, len(post), (float,), "delay")
if len(delay) != len(post):
raise NESTError("delay must be a float, or sequence of floats of length 1 or len(post)")
cmd='/%s DivergentConnect' % model
for s in pre :
sps(s)
sps(post)
sps(weight)
sps(delay)
sr(cmd)
else:
raise NESTError("Both 'weight' and 'delay' have to be given.")
def DataConnect(pre, params=None, model=None):
"""
Connect neurons from lists of connection data.
Variant 1.
pre: [gid_1, ... gid_n]
params: [ {param1}, ..., {param_n} ]
model= 'synapse_model'
Variant 2:
pre = [ {synapse_state1}, ..., {synapse_state_n}]
params=None
model=None
Variant 1 of DataConnect connects each neuron in pre to the targets given in params, using synapse type model.
The dictionary parames must contain at least the following keys:
'target'
'weight'
'delay'
each resolving to a list or numpy.ndarray of values. Depending on the synapse model, other parameters can be given
in the same format. All arrays in params must have the same length as 'target'.
Variant 2 of DataConnect will connect neurons according to a list of synapse status dictionaries,
as obtained from GetStatus.
Note: During connection, status dictionary misses will not raise errors, even if
the kernel property 'dict_miss_is_error' is True.
"""
if not is_sequencetype(pre):
raise NESTError("'pre' must be a list of nodes or connection dictionaries.")
if params and not is_sequencetype(params):
raise NESTError("'params' must be a list of dictionaries.")
if params:
if not model:
model="static_synapse"
cmd='(%s) DataConnect_i_D_s ' % model
for s,p in zip(pre,params):
sps(s)
sps(p)
sr(cmd)
else:
# Call the variant where all connections are
# given explicitly
dictmiss=GetKernelStatus('dict_miss_is_error')
# We disable dictionary checking now because most models
# cannot re-use their own status dictionary
SetKernelStatus({'dict_miss_is_error': False})
sps(pre)
sr('DataConnect_a')
SetKernelStatus({'dict_miss_is_error': dictmiss})
def RandomDivergentConnect(pre, post, n, weight=None, delay=None, model="static_synapse", options=None):
"""
Connect each neuron in pre to n randomly selected neurons from
post. pre and post have to be lists. If weight is given (as a
single float or as list of floats), delay also has to be given as
float or as list of floats. options is a dictionary specifying
options to the RandomDivergentConnect function: allow_autapses,
allow_multapses.
"""
# store current options, set desired options
old_options = None
error = False
if options:
old_options = sli_func('GetOptions', '/RandomDivergentConnect',
litconv=True)
del old_options['DefaultOptions'] # in the way when restoring
sli_func('SetOptions', '/RandomDivergentConnect', options,
litconv=True)
if weight == None and delay == None:
sli_func(
'/m Set /n Set /post Set { n post m RandomDivergentConnect } forall',
pre, post, n, '/'+model, litconv=True)
elif weight != None and delay != None:
weight = broadcast(weight, n, (float,), "weight")
if len(weight) != n:
raise NESTError("weight must be a float, or sequence of floats of length 1 or n")
delay = broadcast(delay, n, (float,), "delay")
if len(delay) != n:
raise NESTError("delay must be a float, or sequence of floats of length 1 or n")
sli_func(
'/m Set /d Set /w Set /n Set /post Set { n post w d m RandomDivergentConnect } forall',
pre, post, n, weight, delay, '/'+model, litconv=True)
else:
error = True
# restore old options
if old_options:
sli_func('SetOptions', '/RandomDivergentConnect', old_options,
litconv=True)
if error:
raise NESTError("Both 'weight' and 'delay' have to be given.")
def CGConnect(pre, post, cg, parameter_map={}, model="static_synapse"):
"""
Connect neurons from pre to neurons from post using connectivity
specified by the connection generator cg. pre and post are either
both lists containing 1 subnet, or lists of gids. parameter_map is
a dictionary mapping names of values such as weight and delay to
value set positions.
"""
if GetStatus(pre[:1], "model")[0] == "subnet":
if GetStatus(post[:1], "model")[0] != "subnet":
raise NESTError("if pre is a subnet, post also has to be a subnet")
if len(pre) > 1 or len(post) > 1:
raise NESTError("the length of pre and post has to be 1 if subnets are given")
sli_func('CGConnect', cg, pre[0], post[0], parameter_map, '/'+model, litconv=True)
else:
sli_func('CGConnect', cg, pre, post, parameter_map, '/'+model, litconv=True)
# -------------------- Functions for hierarchical networks
def PrintNetwork(depth=1, subnet=None) :
"""
Print the network tree up to depth, starting at subnet. if
subnet is omitted, the current subnet is used instead.
"""
if subnet == None:
subnet = CurrentSubnet()
elif len(subnet) > 1:
raise NESTError("PrintNetwork() expects exactly one GID.")
sps(subnet[0])
sr("%i PrintNetwork" % depth)
def CurrentSubnet() :
"""
Returns the global id of the current subnet.
"""
sr("CurrentSubnet")
return [spp()]
def ChangeSubnet(subnet) :
"""
Make subnet the current subnet.
"""
if len(subnet) > 1:
raise NESTError("ChangeSubnet() expects exactly one GID.")
sps(subnet[0])
sr("ChangeSubnet")
def GetLeaves(subnets, properties=None, local_only=False) :
"""
Return the global ids of the leaf nodes of the given subnets.
Leaf nodes are all nodes that are not subnets.
If properties is given, it must be a dictionary. Only global ids of nodes
matching the properties given in the dictionary exactly will be returned.
Matching properties with float values (e.g. the membrane potential) may
fail due to tiny numerical discrepancies and should be avoided.
If local_only is True, only global ids of nodes simulated on the local MPI
process will be returned. By default, global ids of nodes in the entire
simulation will be returned. This requires MPI communication and may
slow down the script.
See also: GetNodes, GetChildren
"""
if properties is None:
properties = {}
func = 'GetLocalLeaves' if local_only else 'GetGlobalLeaves'
return sli_func('/props Set { props %s } Map' % func, subnets, properties,
litconv=True)
def GetNodes(subnets, properties=None, local_only=False):
"""
Return the global ids of the all nodes of the given subnets.
If properties is given, it must be a dictionary. Only global ids of nodes
matching the properties given in the dictionary exactly will be returned.
Matching properties with float values (e.g. the membrane potential) may
fail due to tiny numerical discrepancies and should be avoided.
If local_only is True, only global ids of nodes simulated on the local MPI
process will be returned. By default, global ids of nodes in the entire
simulation will be returned. This requires MPI communication and may
slow down the script.
See also: GetLeaves, GetChildren
"""
if properties is None:
properties = {}
func = 'GetLocalNodes' if local_only else 'GetGlobalNodes'
return sli_func('/props Set { props %s } Map' % func, subnets, properties,
litconv=True)
def GetChildren(subnets, properties=None, local_only=False):
"""
Return the global ids of the immediate children of the given subnets.
If properties is given, it must be a dictionary. Only global ids of nodes
matching the properties given in the dictionary exactly will be returned.
Matching properties with float values (e.g. the membrane potential) may
fail due to tiny numerical discrepancies and should be avoided.
If local_only is True, only global ids of nodes simulated on the local MPI
process will be returned. By default, global ids of nodes in the entire
simulation will be returned. This requires MPI communication and may
slow down the script.
See also: GetNodes, GetLeaves
"""
if properties is None:
properties = {}
func = 'GetLocalChildren' if local_only else 'GetGlobalChildren'
return sli_func('/props Set { props %s } Map' % func, subnets, properties,
litconv=True)
def GetNetwork(gid, depth):
"""
Return a nested list with the children of subnet id at level
depth. If depth==0, the immediate children of the subnet are
returned. The returned list is depth+1 dimensional.
"""
if len(gid)>1 :
raise NESTError("GetNetwork() expects exactly one GID.")
sps(gid[0])
sps(depth)
sr("GetNetwork")
return spp()
def BeginSubnet(label=None, params=None):
"""
Create a new subnet and change into it. A string argument can be
used to name the new subnet A dictionary argument can be used to
set the subnet's custom dict.
"""
sn=Create("subnet")
if label:
SetStatus(sn, "label", label)
if params:
SetStatus(sn, "customdict", params)
ChangeSubnet(sn)
def EndSubnet():
"""
Change to the parent subnet and return the gid of the current.
"""
csn=CurrentSubnet()
parent=GetStatus(csn, "parent")
if csn != parent:
ChangeSubnet(parent)
return csn
else:
raise NESTError("Unexpected EndSubnet(). Cannot go higher than the root node.")
def LayoutNetwork(model, dim, label=None, params=None) :
"""
Create a subnetwork of dimension dim with nodes of type model and
return a list of ids.
"""
if type(model) == types.StringType:
sps(dim)
sr('/%s exch LayoutNetwork' % model)
if label:
sr("dup << /label (%s) >> SetStatus"%label)
if params:
sr("dup << /customdict")
sps(params)
sr(">> SetStatus")
return [spp()]
# If model is a function.
elif type(model) == types.FunctionType:
# The following code uses a model constructor function
# model() instead of a model name string.
BeginSubnet(label, params)
if len(dim)==1:
for i in xrange(dim[0]):
model()
else:
for i in xrange(dim[0]):
LayoutNetwork(model,dim[1:])
gid = EndSubnet()
return gid
else:
raise NESTError("model must be a string or a function.")