Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Split blocks automatically #264

Merged
merged 10 commits into from Feb 7, 2020
2 changes: 1 addition & 1 deletion .travis.yml
Expand Up @@ -90,7 +90,7 @@ before_install:
- if [[ "$TRAVIS_PYTHON_VERSION" < "3.6" ]]; then
echo "Skipping bones-check because Python $TRAVIS_PYTHON_VERSION < 3.6";
else
bones-check;
bones-check --verbose;
fi
# display environment info
- pip freeze
Expand Down
20 changes: 20 additions & 0 deletions CHANGES.rst
Expand Up @@ -35,6 +35,11 @@ Release history
See `nengo_loihi.add_params
<https://www.nengo.ai/nengo-loihi/api.html#nengo_loihi.add_params>`__
for details. (`#261 <https://github.com/nengo/nengo-loihi/pull/261>`__)
- Added the ``block_shape`` configuration option to the ``Ensemble`` config,
and added the ``nengo_loihi.BlockShape`` class to set that option.
See `nengo_loihi.add_params
<https://www.nengo.ai/nengo-loihi/api.html#nengo_loihi.add_params>`__
for details. (`#264 <https://github.com/nengo/nengo-loihi/pull/264>`__)

**Changed**

Expand All @@ -50,6 +55,18 @@ Release history
- ``Convolution`` transforms with ``channels_last=True`` now work with outputs
up to 1024 neurons.
(`#261 <https://github.com/nengo/nengo-loihi/pull/261>`__)
- The ``Probe`` has been renamed to ``LoihiProbe`` to mirror the ``LoihiBlock``
and ``LoihiInput`` classes, which are conceptually very similar.
It has also been moved from ``nengo_loihi.block`` to ``nengo_loihi.probe``.
(`#264 <https://github.com/nengo/nengo-loihi/pull/264>`__)
- We now raise a more informative error if connecting to Loihi hardware fails.
(`#264 <https://github.com/nengo/nengo-loihi/pull/264>`__)
- It is now possible to build models with larger ensembles because
the builder can now split large Loihi blocks into smaller ones.
(`#264 <https://github.com/nengo/nengo-loihi/pull/264>`__)
- Modules for discretizing and validating models have been moved to the
``builder`` directory.
(`#264 <https://github.com/nengo/nengo-loihi/pull/264>`__)

**Fixed**

Expand All @@ -63,6 +80,9 @@ Release history
- Fixed a bug preventing making sparse connections to an ensemble.
(`#245 <https://github.com/nengo/nengo-loihi/issues/245>`__,
`#246 <https://github.com/nengo/nengo-loihi/pull/246>`__)
- We now ignore TensorFlow and NengoDL if an incompatible version is installed
rather than exiting with an exception.
(`#264 <https://github.com/nengo/nengo-loihi/pull/264>`__)

0.10.0 (November 25, 2019)
==========================
Expand Down
78 changes: 55 additions & 23 deletions docs/api.rst
Expand Up @@ -11,12 +11,15 @@ Main

nengo_loihi.Simulator
nengo_loihi.add_params
nengo_loihi.BlockShape
nengo_loihi.set_defaults

.. autoclass:: nengo_loihi.Simulator

.. autofunction:: nengo_loihi.add_params

.. autoclass:: nengo_loihi.BlockShape

.. autofunction:: nengo_loihi.set_defaults

Neurons
Expand All @@ -40,13 +43,39 @@ Neurons

.. autoclass:: nengo_loihi.neurons.AlphaRCNoise

Decode neurons
==============

Decode neurons facilitate NEF-style connections on the chip.
The type of decode neurons used by a model can be set on `.builder.Model`.

.. autosummary::

nengo_loihi.decode_neurons.DecodeNeurons
nengo_loihi.decode_neurons.OnOffDecodeNeurons
nengo_loihi.decode_neurons.NoisyDecodeNeurons
nengo_loihi.decode_neurons.Preset5DecodeNeurons
nengo_loihi.decode_neurons.Preset10DecodeNeurons

.. autoclass:: nengo_loihi.decode_neurons.DecodeNeurons

.. autoclass:: nengo_loihi.decode_neurons.OnOffDecodeNeurons

.. autoclass:: nengo_loihi.decode_neurons.NoisyDecodeNeurons

.. autoclass:: nengo_loihi.decode_neurons.Preset5DecodeNeurons

.. autoclass:: nengo_loihi.decode_neurons.Preset10DecodeNeurons

Builder
=======

The builder turns a Nengo network into a `.builder.Model`
appropriate for running on Loihi.
This model is mainly composed of `.LoihiBlock` objects,
which map parts of the network to Loihi compartments, axons, and synapses.
The model also contains `.LoihiInput` objects to provide input,
and `.LoihiProbe` objects to collect output.

.. autosummary::

Expand All @@ -56,7 +85,9 @@ which map parts of the network to Loihi compartments, axons, and synapses.
nengo_loihi.block.Compartment
nengo_loihi.block.Axon
nengo_loihi.block.Synapse
nengo_loihi.block.Probe
nengo_loihi.inputs.LoihiInput
nengo_loihi.inputs.SpikeInput
nengo_loihi.probe.LoihiProbe

.. autoclass:: nengo_loihi.builder.Model

Expand All @@ -70,42 +101,43 @@ which map parts of the network to Loihi compartments, axons, and synapses.

.. autoclass:: nengo_loihi.block.Synapse

.. autoclass:: nengo_loihi.block.Probe
.. autoclass:: nengo_loihi.inputs.LoihiInput

Decode neurons
==============
.. autoclass:: nengo_loihi.inputs.SpikeInput

Decode neurons facilitate NEF-style connections on the chip.
The type of decode neurons used by a model can be set on `.builder.Model`.
.. autoclass:: nengo_loihi.probe.LoihiProbe

.. autosummary::
Discretization
--------------

nengo_loihi.decode_neurons.DecodeNeurons
nengo_loihi.decode_neurons.OnOffDecodeNeurons
nengo_loihi.decode_neurons.NoisyDecodeNeurons
nengo_loihi.decode_neurons.Preset5DecodeNeurons
nengo_loihi.decode_neurons.Preset10DecodeNeurons
.. autofunction:: nengo_loihi.builder.discretize.discretize_model

.. autoclass:: nengo_loihi.decode_neurons.DecodeNeurons
.. autofunction:: nengo_loihi.builder.discretize.discretize_block

.. autoclass:: nengo_loihi.decode_neurons.OnOffDecodeNeurons
.. autofunction:: nengo_loihi.builder.discretize.discretize_compartment

.. autoclass:: nengo_loihi.decode_neurons.NoisyDecodeNeurons
.. autofunction:: nengo_loihi.builder.discretize.discretize_synapse

.. autoclass:: nengo_loihi.decode_neurons.Preset5DecodeNeurons
.. autofunction:: nengo_loihi.builder.discretize.discretize_weights

.. autoclass:: nengo_loihi.decode_neurons.Preset10DecodeNeurons
.. autofunction:: nengo_loihi.builder.discretize.discretize_probe

Discretization
==============
Validation
----------

.. autofunction:: nengo_loihi.builder.validate.validate_model

.. autofunction:: nengo_loihi.builder.validate.validate_block

.. autofunction:: nengo_loihi.builder.validate.validate_compartment

.. autofunction:: nengo_loihi.discretize.discretize_model
.. autofunction:: nengo_loihi.builder.validate.validate_axon

.. autofunction:: nengo_loihi.discretize.discretize_block
.. autofunction:: nengo_loihi.builder.validate.validate_synapse

.. autofunction:: nengo_loihi.discretize.discretize_compartment
.. autofunction:: nengo_loihi.builder.validate.validate_synapse_cfg

.. autofunction:: nengo_loihi.discretize.discretize_synapse
.. autofunction:: nengo_loihi.builder.validate.validate_probe

Emulator
========
Expand Down
2 changes: 1 addition & 1 deletion nengo_loihi/__init__.py
Expand Up @@ -6,7 +6,7 @@
del check_nengo_version

from .simulator import Simulator
from .config import add_params, set_defaults
from .config import add_params, BlockShape, set_defaults

# Import builders so they are registered
from . import builder
Expand Down
100 changes: 36 additions & 64 deletions nengo_loihi/block.py
Expand Up @@ -8,14 +8,20 @@
from nengo_loihi.nxsdk_obfuscation import d


MAX_COMPARTMENTS = d(b"MTAyNA==", int)
MAX_IN_AXONS = d(b"NDA5Ng==", int)
MAX_OUT_AXONS = d(b"NDA5Ng==", int)
MAX_SYNAPSE_BITS = d(b"MTA0ODU3Ng==", int)


class LoihiBlock:
"""Class holding Loihi objects that can be placed on the chip.

This class can be thought of as a block of the Loihi board, and is how
Nengo Loihi keeps track of how Loihi Neuron cores will be configured.
Generally, the job of the build process is to convert Nengo objects
(ensembles, connections, nodes, and probes) to LoihiBlocks, which
will then be used by the `.EmulatorInterface` or `.HardwareInterface`.
(ensembles, connections, and nodes) to LoihiBlocks, which will then
be used by the `.EmulatorInterface` or `.HardwareInterface`.

Initially, parameters in a LoihiBlock are floating point values.
The `.discretize_block` function converts them to integer values inplace
Expand All @@ -33,8 +39,6 @@ class LoihiBlock:
Mapping from a name to a Synapse object.
label : string
A label for the block (for debugging purposes).
probes : list of Probe
Probes recording information from objects in the block.
synapses : list of Synapse
Synapse objects projecting to compartments in the block.
"""
Expand All @@ -47,7 +51,6 @@ def __init__(self, n_neurons, label=None):
self.axons = []
self.synapses = []
self.named_synapses = {}
self.probes = []

def __str__(self):
return "%s(%s)" % (type(self).__name__, self.label if self.label else "")
Expand All @@ -61,10 +64,6 @@ def add_synapse(self, synapse, name=None):
def add_axon(self, axon):
self.axons.append(axon)

def add_probe(self, probe):
assert probe.target is self
self.probes.append(probe)


class Config:
def __eq__(self, obj):
Expand Down Expand Up @@ -634,11 +633,14 @@ def format(self, **kwargs):
def idx_bits(self):
"""The number of index bits required for each weight entry."""
bits = int(np.ceil(np.log2(self.max_ind() + 1)))
assert (
bits <= SynapseConfig.INDEX_BITS_MAP[-1]
), "bits out of range, ensemble too large?"
bits = next(i for i, v in enumerate(SynapseConfig.INDEX_BITS_MAP) if v >= bits)
return bits
if bits <= SynapseConfig.INDEX_BITS_MAP[-1]:
return next(
i for i, v in enumerate(SynapseConfig.INDEX_BITS_MAP) if v >= bits
)
else: # pragma: no cover
# number of bits is actually out of range, we need to split this ensemble
# before it goes on the chip. Use -1 as a placeholder.
return -1

def idxs_per_synapse(self):
"""The number of axon indices (slots) required for each incoming axon."""
Expand All @@ -655,8 +657,16 @@ def max_ind(self):
"""
return max(i.max() if i.size > 0 else -1 for i in self.indices)

def _set_weights_indices(self, weights, indices=None):
weights = [np.array(w, copy=False, dtype=np.float32, ndmin=2) for w in weights]
def _set_weights_indices(
self,
weights,
indices=None,
weight_dtype=np.float32,
compression=d(b"MA==", int),
):
weights = [
np.array(w, copy=False, dtype=weight_dtype, ndmin=2) for w in weights
]
assert all(
w.ndim == 2 for w in weights
), "Weights must be shape (n_axons,) (n_populations, n_compartments)"
Expand All @@ -681,6 +691,14 @@ def _set_weights_indices(self, weights, indices=None):
assert len(weights) == len(indices)
self.indices = indices

self.format(
compression=compression,
idx_bits=self.idx_bits(),
fanout_type=d(b"MQ==", int),
n_synapses=d(b"NjM=", int),
weight_bits=d(b"Nw==", int),
)

def set_weights(self, weights):
"""Set dense or sparse weights on this Synapse."""
if isinstance(weights, scipy.sparse.spmatrix):
Expand All @@ -699,15 +717,7 @@ def set_weights(self, weights):
indices = None

assert len(weights) == self.n_axons, "Must have different weights for each axon"
self._set_weights_indices(weights, indices=indices)

self.format(
compression=d(b"Mw==", int),
idx_bits=self.idx_bits(),
fanout_type=d(b"MQ==", int),
n_synapses=d(b"NjM=", int),
weight_bits=d(b"Nw==", int),
)
self._set_weights_indices(weights, indices=indices, compression=d(b"Mw==", int))

def set_learning(
self, learning_rate=1.0, tracing_tau=2, tracing_mag=1.0, wgt_exp=4
Expand All @@ -732,49 +742,11 @@ def set_population_weights(
self, weights, indices, axon_to_weight_map, compartment_bases, pop_type=None
):
"""Set population weights on this Synapse."""
self._set_weights_indices(weights, indices)
self.axon_to_weight_map = axon_to_weight_map
self.axon_compartment_bases = compartment_bases
self.pop_type = 16 if pop_type is None else pop_type

idx_bits = self.idx_bits()
self.format(
compression=d(b"MA==", int),
idx_bits=idx_bits,
fanout_type=d(b"MQ==", int),
n_synapses=d(b"NjM=", int),
weight_bits=d(b"Nw==", int),
)
self._set_weights_indices(weights, indices=indices, compression=d(b"MA==", int))

def size(self):
return sum(w.size for w in self.weights)


class Probe:
"""Record data from compartment states of a LoihiBlock.

Parameters
----------
target : LoihiBlock
The block to record values from. Use ``slice`` to record from a subset
of compartments.
key : string ('current', 'voltage', 'spiked')
The compartment attribute to probe.
slice : slice or list
Select a subset of the compartments in the block to record from.
synapse : nengo.synapses.Synapse
A synapse to use for filtering the probe.
weights : np.ndarray
A linear transformation to apply to the outputs.
"""

_slice = slice

def __init__(self, target=None, key=None, slice=None, weights=None, synapse=None):
self.target = target
self.key = key
self.slice = slice if slice is not None else self._slice(None)
self.weights = weights
self.synapse = synapse
self.use_snip = False
self.snip_info = None
2 changes: 1 addition & 1 deletion nengo_loihi/builder/__init__.py
@@ -1,2 +1,2 @@
from .builder import Builder, Model
from . import connection, ensemble, node, probe
from . import connection, ensemble, network, node, probe