from nengo.config import Config
from nengo.connection import Connection
from nengo.ensemble import Ensemble
from nengo.exceptions import ConfigError, NetworkContextError, ReadonlyError
from nengo.node import Node
from nengo.probe import Probe
from nengo.utils.threading import ThreadLocalStack
[docs]class Network(object):
"""A network contains ensembles, nodes, connections, and other networks.
A network is primarily used for grouping together related
objects and connections for visualization purposes.
However, you can also use networks as a nice way to reuse
network creation code.
To group together related objects that you do not need to reuse,
you can create a new ``Network`` and add objects in a ``with`` block.
For example::
network = nengo.Network()
with network:
with nengo.Network(label="Vision"):
v1 = nengo.Ensemble(nengo.LIF(100), dimensions=2)
with nengo.Network(label="Motor"):
sma = nengo.Ensemble(nengo.LIF(100), dimensions=2)
nengo.Connection(v1, sma)
To reuse a group of related objects, you can create a new subclass
of ``Network``, and add objects in the ``__init__`` method.
For example::
class OcularDominance(nengo.Network):
def __init__(self):
self.column = nengo.Ensemble(nengo.LIF(100), dimensions=2)
network = nengo.Network()
with network:
left_eye = OcularDominance()
right_eye = OcularDominance()
nengo.Connection(left_eye.column, right_eye.column)
Parameters
----------
label : str, optional (Default: None)
Name of the network.
seed : int, optional (Default: None)
Random number seed that will be fed to the random number generator.
Setting the seed makes the network's build process deterministic.
add_to_container : bool, optional (Default: None)
Determines if this network will be added to the current container.
If None, this network will be added to the network at the top of the
``Network.context`` stack unless the stack is empty.
Attributes
----------
connections : list
`.Connection` instances in this network.
ensembles : list
`.Ensemble` instances in this network.
label : str
Name of this network.
networks : list
`.Network` instances in this network.
nodes : list
`.Node` instances in this network.
probes : list
`.Probe` instances in this network.
seed : int
Random seed used by this network.
"""
context = ThreadLocalStack(maxsize=100) # static stack of Network objects
def __init__(self, label=None, seed=None, add_to_container=None):
self.label = label
self.seed = seed
self._config = self.default_config()
self.objects = {
Ensemble: [], Node: [], Connection: [], Network: [], Probe: [],
}
self.ensembles = self.objects[Ensemble]
self.nodes = self.objects[Node]
self.connections = self.objects[Connection]
self.networks = self.objects[Network]
self.probes = self.objects[Probe]
# By default, we want to add to the current context, unless there is
# no context; i.e., we're creating a top-level network.
if add_to_container is None:
add_to_container = len(Network.context) > 0
if add_to_container:
Network.add(self)
def __getstate__(self):
raise NotImplementedError("Nengo Networks do not support pickling")
def __setstate__(self, state):
raise NotImplementedError("Nengo Networks do not support pickling")
@staticmethod
[docs] def add(obj):
"""Add the passed object to ``Network.context``."""
if len(Network.context) == 0:
raise NetworkContextError(
"'%s' must either be created inside a ``with network:`` "
"block, or set add_to_container=False in the object's "
"constructor." % obj)
network = Network.context[-1]
if not isinstance(network, Network):
raise NetworkContextError(
"Current context (%s) is not a network" % network)
for cls in obj.__class__.__mro__:
if cls in network.objects:
network.objects[cls].append(obj)
break
else:
raise NetworkContextError("Objects of type %r cannot be added to "
"networks." % obj.__class__.__name__)
@staticmethod
[docs] def default_config():
"""Constructs a `~.Config` object for setting defaults."""
return Config(Connection, Ensemble, Node, Probe)
def _all_objects(self, object_type):
"""Returns a list of all objects of the specified type."""
# Make a copy of this network's list
objects = list(self.objects[object_type])
for subnet in self.networks:
objects.extend(subnet._all_objects(object_type))
return objects
@property
def all_objects(self):
"""(list) All objects in this network and its subnetworks."""
objects = []
for object_type in self.objects:
objects.extend(self._all_objects(object_type))
return objects
@property
def all_ensembles(self):
"""(list) All ensembles in this network and its subnetworks."""
return self._all_objects(Ensemble)
@property
def all_nodes(self):
"""(list) All nodes in this network and its subnetworks."""
return self._all_objects(Node)
@property
def all_networks(self):
"""(list) All networks in this network and its subnetworks."""
return self._all_objects(Network)
@property
def all_connections(self):
"""(list) All connections in this network and its subnetworks."""
return self._all_objects(Connection)
@property
def all_probes(self):
"""(list) All probes in this network and its subnetworks."""
return self._all_objects(Probe)
@property
def config(self):
"""(`.Config`) Configuration for this network."""
return self._config
@config.setter
def config(self, dummy):
raise ReadonlyError(attr='config', obj=self)
def __contains__(self, obj):
return type(obj) in self.objects and obj in self.objects[type(obj)]
def __enter__(self):
Network.context.append(self)
self._config.__enter__()
return self
def __exit__(self, dummy_exc_type, dummy_exc_value, dummy_tb):
if len(Network.context) == 0:
raise NetworkContextError(
"Network.context in bad state; was empty when "
"exiting from a 'with' block.")
config = Config.context[-1]
if config is not self._config:
raise ConfigError("Config.context in bad state; was expecting "
"current context to be '%s' but instead got "
"'%s'." % (self._config, config))
network = Network.context.pop()
if network is not self:
raise NetworkContextError(
"Network.context in bad state; was expecting current context "
"to be '%s' but instead got '%s'." % (self, network))
self._config.__exit__(dummy_exc_type, dummy_exc_value, dummy_tb)
def __str__(self):
return "<%s %s>" % (
self.__class__.__name__,
'"%s"' % self.label if self.label is not None else
"(unlabeled) at 0x%x" % id(self))
def __repr__(self):
return "<%s %s %s>" % (
self.__class__.__name__,
'"%s"' % self.label if self.label is not None else "(unlabeled)",
"at 0x%x" % id(self))