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

Nengo allclose #277

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .ci/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ if [[ "$COMMAND" == "install" ]]; then
pip install -e .
elif [[ "$COMMAND" == "run" ]]; then
coverage run -m pytest nengo_loihi -v --duration 20 --plots && coverage report
elif [[ "$COMMAND" == "run-nengo" ]]; then
pytest --pyargs nengo
elif [[ "$COMMAND" == "upload" ]]; then
eval "bash <(curl -s https://codecov.io/bash)"
else
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ nengo_loihi/snips/nengo_io.c
# For .ipynb examples
*.pdf
*.pkl
*.pkl.gz
docs/examples/adaptive_motor_control.py
docs/examples/communication_channel.py
docs/examples/integrator.py
Expand Down
10 changes: 7 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,16 @@ notifications:

env:
global:
- NENGO="false"
- STATIC="false"
- DOCS="false"

matrix:
include:
- env: PYTHON="3.5.5"
- env: PYTHON="3.5.5" STATIC="true"
- env: PYTHON="3.5.5" DOCS="true"
- env: PYTHON="3.5.2"
- env: PYTHON="3.5.2" NENGO="true"
- env: PYTHON="3.5.2" STATIC="true"
- env: PYTHON="3.5.2" DOCS="true"

before_install:
- source .ci/conda.sh install
Expand All @@ -34,6 +36,8 @@ script:
.ci/docs.sh run;
elif [[ "$DOCS" == "true" ]]; then
.ci/docs.sh check;
elif [[ "$NENGO" == "true" ]]; then
.ci/test.sh run-nengo;
else
.ci/test.sh run;
fi
Expand Down
3 changes: 0 additions & 3 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,7 @@ Release history
0.4.0 (unreleased)
==================

**Changed**

- An error is now raised if
a learning rule is applied to a non-decoded connection.

**Fixed**

Expand Down
55 changes: 48 additions & 7 deletions conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
import os
from functools import partial

import nengo.utils.numpy as npext
import matplotlib as mpl
import nengo.utils.numpy as npext
import numpy as np
import pytest

Expand All @@ -13,7 +13,6 @@
from nengo.utils.compat import ensure_bytes

import nengo_loihi

from nengo_loihi.loihi_cx import CxSimulator


Expand All @@ -38,8 +37,8 @@ def pytest_addoption(parser):

def pytest_report_header(config, startdir):
target = config.getoption("--target")
return "Nengo Loihi is using {}".format(
"Loihi hardware" if target == "loihi" else "the Numpy simulator")
return "Nengo Loihi is using Loihi {}".format(
"hardware" if target == "loihi" else "emulator")


def pytest_terminal_summary(terminalreporter):
Expand All @@ -58,9 +57,9 @@ def pytest_terminal_summary(terminalreporter):


def pytest_runtest_setup(item):
if (getattr(item.obj, "hang", False) and
item.config.getvalue("--target") == "loihi" and
item.config.getvalue("--no-hang")):
if (getattr(item.obj, "hang", False)
and item.config.getvalue("--target") == "loihi"
and item.config.getvalue("--no-hang")):
pytest.xfail("This test causes Loihi to hang indefinitely")


Expand Down Expand Up @@ -190,3 +189,45 @@ def _allclose(a, b, rtol=1e-5, atol=1e-8, xtol=0,
return result

return _allclose


def pytest_collection_modifyitems(session, config, items):
target = config.getoption("--target")
if target != "loihi":
return

hanging_nengo_tests = [
"nengo/tests/test_connection.py::test_slicing[LIF]",
"nengo/tests/test_connection.py::test_slicing[SpikingRectifiedLinear]",
"nengo/tests/test_connection.py::test_slicing_function",
"nengo/tests/test_learning_rules.py::test_slicing",
"nengo/tests/test_ensemble.py::test_vector[LIF]",
"nengo/tests/test_ensemble.py::test_vector[SpikingRectifiedLinear]",
"nengo/tests/test_neurons.py::test_direct_mode_nonfinite_value",
"nengo/tests/test_neurons.py::test_lif_min_voltage[-inf]",
"nengo/tests/test_neurons.py::test_lif_min_voltage[-1]",
"nengo/tests/test_neurons.py::test_lif_min_voltage[0]",
"nengo/tests/test_neurons.py::test_lif_zero_tau_ref",
"nengo/tests/test_node.py::test_none",
"nengo/tests/test_node.py::test_invalid_values[inf]",
"nengo/tests/test_node.py::test_invalid_values[nan]",
"nengo/tests/test_node.py::test_invalid_values[string]",
"nengo/utils/tests/test_network.py::" # no comma
"test_activate_direct_mode_learning[learning_rule0-False]",
"nengo/utils/tests/test_network.py::" # no comma
"test_activate_direct_mode_learning[learning_rule1-True]",
"nengo/utils/tests/test_network.py::" # no comma
"test_activate_direct_mode_learning[learning_rule2-True]",
"nengo/utils/tests/test_network.py::" # no comma
"test_activate_direct_mode_learning[learning_rule3-False]",
]
deselected = [
item for item in items if item.nodeid in hanging_nengo_tests
]
config.hook.pytest_deselected(items=deselected)
for item in deselected:
items.remove(item)


nengo.conftest.RefSimulator = Simulator
nengo.conftest.Simulator = Simulator
22 changes: 12 additions & 10 deletions nengo_loihi/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import nengo
from nengo import Network, Ensemble, Connection, Node, Probe
from nengo.builder.connection import BuiltConnection
from nengo.dists import Distribution, get_samples
from nengo.connection import LearningRule
from nengo.ensemble import Neurons
Expand Down Expand Up @@ -135,6 +136,12 @@ def inter_scale(self):
"""
return 1. / (self.dt * self.inter_rate * self.inter_n)

def __getstate__(self):
raise NotImplementedError("Can't pickle nengo_loihi.builder.Model")

def __setstate__(self, state):
raise NotImplementedError("Can't pickle nengo_loihi.builder.Model")

def __str__(self):
return "Model: %s" % self.label

Expand Down Expand Up @@ -326,12 +333,12 @@ def build_ensemble(model, ens):
gain, bias, max_rates, intercepts = get_gain_bias(
ens, rng, model.intercept_limit)

if isinstance(ens.neuron_type, nengo.Direct):
raise NotImplementedError()
else:
if isinstance(ens.neuron_type, (nengo.LIF, nengo.SpikingRectifiedLinear)):
group = CxGroup(ens.n_neurons, label='%s' % ens)
group.bias[:] = bias
model.build(ens.neuron_type, ens.neurons, group)
else:
raise NotImplementedError()

# set default filter just in case no other filter gets set
group.configure_default_filter(model.inter_tau, dt=model.dt)
Expand Down Expand Up @@ -402,11 +409,6 @@ def build_node(model, node):
raise NotImplementedError()


BuiltConnection = collections.namedtuple(
'BuiltConnection',
('eval_points', 'solver_info', 'weights', 'transform'))


def get_eval_points(model, conn, rng):
if conn.eval_points is None:
view = model.params[conn.pre_obj].eval_points.view()
Expand Down Expand Up @@ -534,7 +536,7 @@ def build_connection(model, conn):
# TODO: this identity transform may be avoidable
transform = np.eye(conn.pre.size_out)
else:
assert transform.ndim == 2
assert transform.ndim == 2, "transform shape not handled yet"
assert transform.shape[1] == conn.pre.size_out

assert transform.shape[1] == conn.pre.size_out
Expand Down Expand Up @@ -565,7 +567,7 @@ def build_connection(model, conn):
needs_interneurons = True
elif isinstance(conn.pre_obj, Neurons):
assert conn.pre_slice == slice(None)
assert transform.ndim == 2
assert transform.ndim == 2, "transform shape not handled yet"
weights = transform / model.dt
neuron_type = conn.pre_obj.ensemble.neuron_type
else:
Expand Down
5 changes: 3 additions & 2 deletions nengo_loihi/loihi_cx.py
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,8 @@ def set_full_weights(self, weights):
assert weights.shape[0] == self.n_axons

idxBits = int(np.ceil(np.log2(self.max_ind() + 1)))
assert idxBits <= SynapseFmt.INDEX_BITS_MAP[-1]
assert idxBits <= SynapseFmt.INDEX_BITS_MAP[-1], (
"idxBits out of range, ensemble too large?")
idxBits = next(i for i, v in enumerate(SynapseFmt.INDEX_BITS_MAP)
if v >= idxBits)
self.format(compression=3, idxBits=idxBits, fanoutType=1,
Expand Down Expand Up @@ -904,7 +905,7 @@ def step(self): # noqa: C901
for probe in group.probes:
x_slice = self.group_cxs[probe.target]
p_slice = probe.slice
assert hasattr(self, probe.key)
assert hasattr(self, probe.key), "probe key not found"
x = getattr(self, probe.key)[x_slice][p_slice].copy()
self.probe_outputs[probe].append(x)

Expand Down
4 changes: 2 additions & 2 deletions nengo_loihi/loihi_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -358,7 +358,7 @@ def build_axons(n2core, core, group, axons, cx_ids):


def build_probe(n2core, core, group, probe, cx_idxs):
assert probe.key in ('u', 'v', 's')
assert probe.key in ('u', 'v', 's'), "probe key not found"
key_map = {'s': 'spike'}
key = key_map.get(probe.key, probe.key)

Expand Down Expand Up @@ -696,7 +696,7 @@ def get_probe_output(self, probe):

def create_io_snip(self):
# snips must be created before connecting
assert not self.is_connected()
assert not self.is_connected(), "still connected"

snips_dir = os.path.join(os.path.dirname(__file__), "snips")
env = jinja2.Environment(
Expand Down
14 changes: 10 additions & 4 deletions nengo_loihi/simulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def __getitem__(self, key):
if key in fallback:
target = fallback.raw
break
assert key in target
assert key in target, "probed object not found"

if (key not in self._cache
or len(self._cache[key]) != len(target[key])):
Expand Down Expand Up @@ -144,13 +144,18 @@ class Simulator(object):
unsupported = []

def __init__(self, network, dt=0.001, seed=None, model=None, # noqa: C901
precompute=False, target=None):
precompute=False, target=None, progress_bar=None):
self.closed = True # Start closed in case constructor raises exception
self._time = 0
if progress_bar is not None:
raise NotImplementedError("progress bars not implemented")

if model is None:
# Call the builder to make a model
self.model = Model(dt=float(dt), label="%s, dt=%f" % (network, dt))
else:
assert isinstance(model, Model), (
"model is not type 'nengo_loihi.builder.Model'")
self.model = model
assert self.model.dt == dt

Expand Down Expand Up @@ -295,7 +300,8 @@ def _probe(self):
for probe in self.model.probes:
if probe in self.model.chip2host_params:
continue
assert probe.sample_every is None
assert probe.sample_every is None, (
"probe.sample_every not implemented")
assert ("loihi" not in self.sims
or "emulator" not in self.sims)
if "loihi" in self.sims:
Expand Down Expand Up @@ -510,7 +516,7 @@ def run_steps(self, steps):
logger.info("Finished running for %d steps", steps)
self._probe()

def trange(self, sample_every=None):
def trange(self, sample_every=None, dt=None):
"""Create a vector of times matching probed data.

Note that the range does not start at 0 as one might expect, but at
Expand Down
52 changes: 33 additions & 19 deletions nengo_loihi/splitter.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,8 +154,9 @@ def place_ensembles(networks):
# User-specified config takes precedence
if config[ens].on_chip is not None:
networks.move(ens, "chip" if config[ens].on_chip else "host")
# Direct mode ensembles must be off chip
elif isinstance(ens.neuron_type, nengo.Direct):
# Unsupported neuron types must be off-chip
elif not isinstance(ens.neuron_type,
(nengo.LIF, nengo.SpikingRectifiedLinear)):
networks.move(ens, "host")

for conn in networks.original.all_connections:
Expand Down Expand Up @@ -323,27 +324,40 @@ def split_chip_to_host(networks, conn):
d=conn.size_mid,
rng=np.random.RandomState(seed=seed))

probe = nengo.Probe(
conn.pre,
synapse=None,
solver=conn.solver,
add_to_container=False,
)
networks.chip2host_params[probe] = dict(
learning_rule_type=conn.learning_rule_type,
function=conn.function,
eval_points=conn.eval_points,
scale_eval_points=conn.scale_eval_points,
transform=transform
)
if (isinstance(conn.pre, nengo.ensemble.Neurons)
and transform.ndim == 2):
# decoders manually specified in the transform
# should be handled like a normal decoder
probe = nengo.Probe(
conn.pre.ensemble,
synapse=None,
solver=nengo.solvers.NoSolver(transform.T),
add_to_container=False,
)
dims = transform.shape[0]
networks.chip2host_params[probe] = dict(
learning_rule_type=conn.learning_rule_type,
function=lambda x, dims=dims: np.zeros(dims),
transform=np.array(1),
)
else:
probe = nengo.Probe(
conn.pre,
synapse=None,
solver=conn.solver,
add_to_container=False,
)
networks.chip2host_params[probe] = dict(
learning_rule_type=conn.learning_rule_type,
function=conn.function,
eval_points=conn.eval_points,
scale_eval_points=conn.scale_eval_points,
transform=transform
)
networks.add(probe, "chip")
networks.chip2host_receivers[probe] = receive

if conn.learning_rule_type is not None:
if not isinstance(conn.pre_obj, nengo.Ensemble):
raise NotImplementedError(
"Learning rule presynaptic object must be an Ensemble "
"(got %r)" % type(conn.pre_obj).__name__)
networks.needs_sender[conn.learning_rule] = PESModulatoryTarget(probe)
networks.remove(conn)

Expand Down
4 changes: 2 additions & 2 deletions nengo_loihi/tests/test_precompute.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,10 @@ def test_precompute(allclose, Simulator, seed, plt):
assert allclose(sim1.data[p_out], sim2.data[p_out], atol=0.2)


@pytest.mark.xfail(pytest.config.getoption("--target") == "loihi",
reason="Fails allclose check")
@pytest.mark.skipif(pytest.config.getoption("--target") != "loihi",
reason="Loihi only test")
@pytest.mark.xfail(pytest.config.getoption("--target") == "loihi",
reason="Fails allclose check")
def test_input_node_precompute(allclose, Simulator, plt):
input_fn = lambda t: np.sin(2 * np.pi * t)
targets = ["sim", "loihi"]
Expand Down
Loading