Beispiel #1
0
def test_solvers():
    check_init_args(Lstsq, ["weights", "rcond"])
    check_repr(Lstsq(weights=True, rcond=0.1))
    assert repr(Lstsq(weights=True,
                      rcond=0.1)) == "Lstsq(weights=True, rcond=0.1)"

    check_init_args(LstsqNoise, ["weights", "noise", "solver"])
    check_repr(LstsqNoise(weights=True, noise=0.2))
    assert (repr(LstsqNoise(
        weights=True, noise=0.2)) == "LstsqNoise(weights=True, noise=0.2)")

    check_init_args(LstsqL2, ["weights", "reg", "solver"])
    check_repr(LstsqL2(weights=True, reg=0.2))
    assert repr(LstsqL2(weights=True,
                        reg=0.2)) == "LstsqL2(weights=True, reg=0.2)"

    check_init_args(LstsqL2nz, ["weights", "reg", "solver"])
    check_repr(LstsqL2nz(weights=True, reg=0.2))
    assert repr(LstsqL2nz(weights=True,
                          reg=0.2)) == "LstsqL2nz(weights=True, reg=0.2)"

    check_init_args(NoSolver, ["values", "weights"])
    check_repr(NoSolver(values=np.array([[1.2, 3.4, 5.6, 7.8]]), weights=True))
    assert (repr(NoSolver([[1.2, 3.4, 5.6, 7.8]], weights=True)) ==
            "NoSolver(values=array([[1.2, 3.4, 5.6, 7.8]]), weights=True)")
Beispiel #2
0
def test_set_weight_solver():
    with nengo.Network():
        a = nengo.Ensemble(10, 2)
        b = nengo.Ensemble(10, 2)
        nengo.Connection(a, b, solver=LstsqL2(weights=True))
        with pytest.raises(ValidationError):
            nengo.Connection(a.neurons, b, solver=LstsqL2(weights=True))
        with pytest.raises(ValidationError):
            nengo.Connection(a, b.neurons, solver=LstsqL2(weights=True))
        with pytest.raises(ValidationError):
            nengo.Connection(a.neurons, b.neurons, solver=LstsqL2(weights=True))
Beispiel #3
0
def divider(neuronCount=1000, tau=0.002):
    # Set up neurons
    model = nengo.Network()

    with model:
        dividend = nengo.Node(size_in=1, size_out=1)
        divisor = nengo.Node(size_in=1, size_out=1)
        combine = nengo.Ensemble(neuronCount,
                                 dimensions=2,
                                 radius=200,
                                 label='combine')
        quotient = nengo.Ensemble(neuronCount,
                                  dimensions=1,
                                  radius=10,
                                  label='quotient')

        nengo.Connection(dividend, combine, synapse=tau, transform=[[1], [0]])
        nengo.Connection(divisor, combine, synapse=tau, transform=[[0], [1]])
        nengo.Connection(combine,
                         quotient,
                         synapse=tau,
                         solver=LstsqL2(weights=True),
                         function=lambda x: x[1] / (x[0] + 0.001))

    return model
Beispiel #4
0
 def objective(hyperparams):
     tauRise = hyperparams['tauRise']
     tauFall = hyperparams['tauFall']
     dt = hyperparams['dt']
     dtSample = hyperparams['dtSample']
     f = DoubleExp(tauRise, tauFall)
     spikes = np.load('data/%s_spikes.npz' % hyperparams['name'])['spikes']
     targets = np.load('data/%s_target.npz' % hyperparams['name'])['target']
     A = np.zeros((0, spikes.shape[2]))
     Y = np.zeros((0, targets.shape[2]))
     for n in range(hyperparams['nTrain']):
         A = np.append(A, f.filt(spikes[n], dt=dt), axis=0)
         Y = np.append(Y, targets[n], axis=0)
     if dt != dtSample:
         A = A[::int(dtSample / dt)]
         Y = Y[::int(dtSample / dt)]
     d, _ = LstsqL2(reg=hyperparams['reg'])(A, Y)
     X = np.dot(A, d)
     loss = rmse(X, Y)
     loss += penalty * (10 * tauRise + tauFall)
     return {
         'loss': loss,
         'd': d,
         'tauRise': tauRise,
         'tauFall': tauFall,
         'status': STATUS_OK
     }
Beispiel #5
0
def test_weights(Simulator, nl, plt, seed):
    n1, n2 = 100, 50

    def func(t):
        return [np.sin(4 * t), np.cos(12 * t)]

    transform = np.array([[0.6, -0.4]])

    m = nengo.Network(label='test_weights', seed=seed)
    with m:
        m.config[nengo.Ensemble].neuron_type = nl()
        u = nengo.Node(output=func)
        a = nengo.Ensemble(n1, dimensions=2, radius=1.5)
        b = nengo.Ensemble(n2, dimensions=1)
        bp = nengo.Probe(b)

        nengo.Connection(u, a)
        nengo.Connection(a,
                         b,
                         transform=transform,
                         solver=LstsqL2(weights=True))

    with Simulator(m) as sim:
        sim.run(1.)

    t = sim.trange()
    x = np.array(func(t)).T
    y = np.dot(x, transform.T)
    z = nengo.Lowpass(0.005).filtfilt(sim.data[bp], dt=sim.dt)
    assert allclose(t, y, z, atol=0.1, buf=0.1, delay=0.01, plt=plt)
Beispiel #6
0
def test_compare_solvers(Simulator, plt, seed, allclose):
    pytest.importorskip("sklearn")

    N = 70
    decoder_solvers = [
        Lstsq(),
        LstsqNoise(),
        LstsqL2(),
        LstsqL2nz(),
        LstsqL1(max_iter=5000),
    ]
    weight_solvers = [LstsqL1(weights=True, max_iter=5000), LstsqDrop(weights=True)]

    tfinal = 4

    def input_function(t):
        return np.interp(t, [1, 3], [-1, 1], left=-1, right=1)

    model = nengo.Network(seed=seed)
    with model:
        u = nengo.Node(output=input_function)
        a = nengo.Ensemble(N, dimensions=1)
        nengo.Connection(u, a)
        ap = nengo.Probe(a)

        probes = []
        names = []
        for solver in decoder_solvers + weight_solvers:
            b = nengo.Ensemble(N, dimensions=1, seed=seed + 1)
            nengo.Connection(a, b, solver=solver)
            probes.append(nengo.Probe(b))
            names.append(
                "%s(%s)" % (type(solver).__name__, "w" if solver.weights else "d")
            )

    with Simulator(model) as sim:
        sim.run(tfinal)
    t = sim.trange()

    # ref = sim.data[up]
    ref = nengo.Lowpass(0.02).filtfilt(sim.data[ap], dt=sim.dt)
    outputs = np.array([sim.data[probe][:, 0] for probe in probes]).T
    outputs_f = nengo.Lowpass(0.02).filtfilt(outputs, dt=sim.dt)

    close = signals_allclose(
        t,
        ref,
        outputs_f,
        atol=0.07,
        rtol=0,
        buf=0.1,
        delay=0.007,
        plt=plt,
        labels=names,
        individual_results=True,
        allclose=allclose,
    )

    for name, c in zip(names, close):
        assert c, "Solver '%s' does not meet tolerances" % name
Beispiel #7
0
    def __init__(self, pre, post, solver=LstsqL2(), **kwargs):
        if isinstance(pre, nengo.Ensemble):
            solver = BiasedSolver(solver)
            bias = nengo.Node(output=solver.bias_function(post.size_in),
                              label="Bias")
            nengo.Connection(bias, post, synapse=None)

        super(Connection, self).__init__(pre, post, solver=solver, **kwargs)
Beispiel #8
0
def test_set_weight_solver():
    with nengo.Network() as net:
        # set default Gaussian transform to avoid transform errors
        net.config[nengo.Connection].transform = nengo.dists.Gaussian(0, 1)

        ens = nengo.Ensemble(10, 2)
        node = nengo.Node(lambda t, x: x, size_in=2, size_out=2)

        nengo.Connection(ens, ens, solver=LstsqL2(weights=True))

        with pytest.warns(
            UserWarning, match="For connections from.*setting the solver has no effect"
        ):
            nengo.Connection(node, ens, solver=LstsqL2(weights=True))

        with pytest.warns(
            UserWarning, match="For connections to.*setting `weights=True`.*no effect"
        ):
            nengo.Connection(ens, node, solver=LstsqL2(weights=True))
Beispiel #9
0
 def __init__(self, solver=LstsqL2()):
     self.solver = solver
     self.bias = None
     try:
         # parent class changed in Nengo 2.1.1
         # need to do this because self.weights is read-only
         super(BiasedSolver, self).__init__(weights=solver.weights)
     except TypeError:  # pragma: no cover
         super(BiasedSolver, self).__init__()
         self.weights = solver.weights
Beispiel #10
0
def test_solvers(Simulator, nl_nodirect):

    N = 100
    decoder_solvers = [
        Lstsq(), LstsqNoise(),
        LstsqL2(), LstsqL2nz(),
        LstsqL1()
    ]
    weight_solvers = [LstsqL1(weights=True), LstsqDrop(weights=True)]

    dt = 1e-3
    tfinal = 4

    def input_function(t):
        return np.interp(t, [1, 3], [-1, 1], left=-1, right=1)

    model = nengo.Network('test_solvers', seed=290)
    with model:
        u = nengo.Node(output=input_function)
        a = nengo.Ensemble(nl_nodirect(N), dimensions=1)
        nengo.Connection(u, a)
        ap = nengo.Probe(a)

        probes = []
        names = []
        for solver in decoder_solvers + weight_solvers:
            b = nengo.Ensemble(nl_nodirect(N), dimensions=1, seed=99)
            nengo.Connection(a, b, solver=solver)
            probes.append(nengo.Probe(b))
            names.append(
                "%s(%s)" %
                (solver.__class__.__name__, 'w' if solver.weights else 'd'))

    sim = Simulator(model, dt=dt)
    sim.run(tfinal)
    t = sim.trange()

    # ref = sim.data[up]
    ref = filtfilt(sim.data[ap], 20)
    outputs = np.array([sim.data[probe] for probe in probes])
    outputs_f = filtfilt(outputs, 20, axis=1)

    close = allclose(t,
                     ref,
                     outputs_f,
                     plotter=Plotter(Simulator, nl_nodirect),
                     filename='test_decoders.test_solvers.pdf',
                     labels=names,
                     atol=0.05,
                     rtol=0,
                     buf=100,
                     delay=7)
    for name, c in zip(names, close):
        assert c, "Solver '%s' does not meet tolerances" % name
Beispiel #11
0
def test_compare_solvers(Simulator, plt, seed):

    N = 70
    decoder_solvers = [
        Lstsq(), LstsqNoise(),
        LstsqL2(), LstsqL2nz(),
        LstsqL1()
    ]
    weight_solvers = [LstsqL1(weights=True), LstsqDrop(weights=True)]

    tfinal = 4

    def input_function(t):
        return np.interp(t, [1, 3], [-1, 1], left=-1, right=1)

    model = nengo.Network(seed=seed)
    with model:
        u = nengo.Node(output=input_function)
        a = nengo.Ensemble(N, dimensions=1)
        nengo.Connection(u, a)
        ap = nengo.Probe(a)

        probes = []
        names = []
        for solver in decoder_solvers + weight_solvers:
            b = nengo.Ensemble(N, dimensions=1, seed=seed + 1)
            nengo.Connection(a, b, solver=solver)
            probes.append(nengo.Probe(b))
            names.append(
                "%s(%s)" %
                (solver.__class__.__name__, 'w' if solver.weights else 'd'))

    sim = Simulator(model)
    sim.run(tfinal)
    t = sim.trange()

    # ref = sim.data[up]
    ref = nengo.synapses.filtfilt(sim.data[ap], 0.02, dt=sim.dt)
    outputs = np.array([sim.data[probe][:, 0] for probe in probes]).T
    outputs_f = nengo.synapses.filtfilt(outputs, 0.02, dt=sim.dt)

    close = allclose(t,
                     ref,
                     outputs_f,
                     atol=0.07,
                     rtol=0,
                     buf=0.1,
                     delay=0.007,
                     plt=plt,
                     labels=names,
                     individual_results=True)

    for name, c in zip(names, close):
        assert c, "Solver '%s' does not meet tolerances" % name
Beispiel #12
0
def test_solverparam():
    """SolverParam must be a solver."""
    class Test:
        sp = ConnectionSolverParam("sp", default=None)

    inst = Test()
    assert inst.sp is None
    inst.sp = LstsqL2()
    assert isinstance(inst.sp, LstsqL2)
    assert not inst.sp.weights
    # Non-solver not OK
    with pytest.raises(ValidationError):
        inst.sp = "a"
Beispiel #13
0
def test_set_learning_rule():
    with nengo.Network():
        a = nengo.Ensemble(10, 2)
        b = nengo.Ensemble(10, 2)
        nengo.Connection(a, b, learning_rule_type=nengo.PES())
        nengo.Connection(a, b, learning_rule_type=nengo.PES(),
                         solver=LstsqL2(weights=True))
        nengo.Connection(a.neurons, b.neurons, learning_rule_type=nengo.PES())
        nengo.Connection(a.neurons, b.neurons, learning_rule_type=nengo.Oja())

        n = nengo.Node(output=lambda t, x: t * x, size_in=2)
        with pytest.raises(ValueError):
            nengo.Connection(n, a, learning_rule_type=nengo.PES())
Beispiel #14
0
def discrete_example(seed, dt):
    n_neurons = 1000
    theta = 0.1
    freq = 50
    q = 27
    radii = 1.0
    sys = PadeDelay(theta, q)

    T = 5000*(dt+0.001)
    rms = 1.0
    signal = WhiteSignal(T, high=freq, rms=rms, y0=0)

    tau = 0.1
    tau_probe = 0.02
    reg = 0.1

    # Determine radii using direct mode
    with LinearNetwork(
            sys, n_neurons_per_ensemble=1, input_synapse=tau, synapse=tau,
            dt=dt, neuron_type=Direct(),
            realizer=Balanced()) as model:
        Connection(Node(output=signal), model.input, synapse=None)
        p_x = Probe(model.state.input, synapse=None)

    with Simulator(model, dt=dt, seed=seed+1) as sim:
        sim.run(T)

    radii *= np.max(abs(sim.data[p_x]), axis=0)
    logging.info("Radii: %s", radii)

    with Network(seed=seed) as model:
        u = Node(output=signal)

        kwargs = dict(
            n_neurons_per_ensemble=n_neurons / len(sys),
            input_synapse=tau, synapse=tau, radii=radii,
            solver=LstsqL2(reg=reg), realizer=Balanced())
        delay_disc = LinearNetwork(sys, dt=dt, **kwargs)
        delay_cont = LinearNetwork(sys, dt=None, **kwargs)
        Connection(u, delay_disc.input, synapse=None)
        Connection(u, delay_cont.input, synapse=None)

        p_u = Probe(u, synapse=tau_probe)
        p_y_disc = Probe(delay_disc.output, synapse=tau_probe)
        p_y_cont = Probe(delay_cont.output, synapse=tau_probe)

    with Simulator(model, dt=dt, seed=seed) as sim:
        sim.run(T)

    return (theta, dt, sim.trange(), sim.data[p_u],
            sim.data[p_y_disc], sim.data[p_y_cont])
Beispiel #15
0
 def objective(hyperparams):
     taus_ens = [hyperparams['tau_rise'], hyperparams['tau_fall']]
     h_ens = DoubleExp(taus_ens[0], taus_ens[1])
     A = h_ens.filt(np.load('data/%s_ens.npz'%hyperparams['name'])['ens'], dt=hyperparams['dt'])
     x = np.load('data/%s_x.npz'%hyperparams['name'])['x']
     if dt != dt_sample:
         A = A[::int(dt_sample/dt)]
         x = x[::int(dt_sample/dt)]
     if hyperparams['reg']:
         d_ens = LstsqL2(reg=hyperparams['reg'])(A, x)[0]
     else:
         d_ens = Lstsq()(A, x)[0]
     xhat = np.dot(A, d_ens)
     loss = rmse(xhat, x)
     loss += penalty * (10*taus_ens[0] + taus_ens[1])
     return {'loss': loss, 'taus_ens': taus_ens, 'd_ens': d_ens, 'status': STATUS_OK}
Beispiel #16
0
def test_weights(Simulator, nl):
    name = 'test_weights'
    n1, n2 = 100, 50

    def func(t):
        return np.array([np.sin(4 * t), np.cos(12 * t)])

    transform = np.array([[0.6, -0.4]])

    m = nengo.Network(label=name, seed=3902)
    with m:
        m.config[nengo.Ensemble].neuron_type = nl()
        u = nengo.Node(output=func)
        a = nengo.Ensemble(n1, dimensions=2, radius=1.5)
        b = nengo.Ensemble(n2, dimensions=1)
        bp = nengo.Probe(b)

        nengo.Connection(u, a)
        nengo.Connection(a,
                         b,
                         transform=transform,
                         solver=LstsqL2(weights=True))

    sim = Simulator(m)
    sim.run(2.)

    t = sim.trange()
    x = func(t).T
    y = np.dot(x, transform.T)
    z = filtfilt(sim.data[bp], 10, axis=0)
    assert allclose(t,
                    y.flatten(),
                    z.flatten(),
                    plotter=Plotter(Simulator, nl),
                    filename='test_connection.' + name + '.pdf',
                    atol=0.1,
                    rtol=0,
                    buf=100,
                    delay=10)
Beispiel #17
0
def test_weights(Simulator, AnyNeuronType, plt, seed, allclose):
    """Tests connections using a solver with weights"""
    n1, n2 = 100, 50

    def func(t):
        return [np.sin(4 * t), np.cos(12 * t)]

    transform = np.array([[0.6, -0.4]])

    m = nengo.Network(label="test_weights", seed=seed)
    with m:
        m.config[nengo.Ensemble].neuron_type = AnyNeuronType()
        u = nengo.Node(output=func)
        a = nengo.Ensemble(n1, dimensions=2, radius=1.4)
        b = nengo.Ensemble(n2, dimensions=1)
        bp = nengo.Probe(b)

        nengo.Connection(u, a)
        nengo.Connection(a,
                         b,
                         synapse=0.01,
                         transform=transform,
                         solver=LstsqL2(weights=True))

    with Simulator(m) as sim:
        sim.run(1.0)

    t = sim.trange()
    x = np.array(func(t)).T
    y = np.dot(x, transform.T)
    z = nengo.Lowpass(0.01).filt(sim.data[bp], dt=sim.dt)
    assert signals_allclose(t,
                            y,
                            z,
                            atol=0.15,
                            buf=0.1,
                            delay=0.025,
                            plt=plt,
                            allclose=allclose)
Beispiel #18
0
def test_set_learning_rule():
    with nengo.Network():
        a = nengo.Ensemble(10, 2)
        b = nengo.Ensemble(10, 2)
        nengo.Connection(a, b, learning_rule_type=nengo.PES())
        nengo.Connection(
            a, b, learning_rule_type=nengo.PES(), solver=LstsqL2(weights=True)
        )
        nengo.Connection(
            a.neurons, b.neurons, learning_rule_type=nengo.PES(), transform=1
        )
        nengo.Connection(
            a.neurons,
            b.neurons,
            learning_rule_type=nengo.Oja(),
            transform=np.ones((10, 10)),
        )

        n = nengo.Node(output=lambda t, x: t * x, size_in=2)
        with pytest.raises(
            ValidationError, match="'pre' must be of type 'Ensemble'.*PES"
        ):
            nengo.Connection(n, a, learning_rule_type=nengo.PES())
Beispiel #19
0
def tune_ens_parameters(ens, function=None, solver=None, rng=None, n=1000):
    """Find good ensemble parameters for decoding a particular function.

    Randomly generate many sets of parameters and determine the decoding error
    for each. Then set the ensemble parameters to those with the lowest
    decoding error. The "ensemble parameters" are the encoders, gains, biases,
    and evaluation points.

    Parameters
    ----------
    ens : Ensemble
        The ensemble to optimize.
    function : callable, optional
        The target function to optimize for. Defaults to the identity function.
    solver : nengo.solvers.Solver, optional
        The solver to use for finding the decoders. Default: ``LstsqL2()``
    rng : numpy.random.RandomState, optional
        The random number generator to use. Default: ``np.random``
    n : int, optional
        The number of random combinations to test. Default: 1000
    """
    from nengo.dists import Distribution
    from nengo.neurons import Direct
    from nengo.solvers import LstsqL2
    from nengo.builder.connection import solve_for_decoders
    from nengo.builder.ensemble import gen_eval_points

    if solver is None:
        solver = LstsqL2()
    if rng is None:
        rng = np.random
    if isinstance(ens.neuron_type, Direct):
        raise ValueError("Parameters do not apply to Direct mode ensembles")

    sample = lambda dist, n, d=None: (dist.sample(n, d=d, rng=rng)
                                      if isinstance(dist, Distribution) else np
                                      .asarray(dist))

    # use the same evaluation points for all trials
    eval_points = gen_eval_points(ens, ens.eval_points, rng=rng)
    targets = (np.array([function(ep) for ep in eval_points])
               if function is not None else eval_points)

    # --- try random parameters and record error
    errors = []
    for i in range(n):
        # --- generate random parameters
        if ens.gain is None and ens.bias is None:
            max_rates = sample(ens.max_rates, ens.n_neurons)
            intercepts = sample(ens.intercepts, ens.n_neurons)
            gain, bias = ens.neuron_type.gain_bias(max_rates, intercepts)
        elif ens.gain is not None and ens.bias is not None:
            gain = sample(ens.gain, ens.n_neurons)
            bias = sample(ens.bias, ens.n_neurons)
        else:
            raise NotImplementedError("Mixed gain/bias and rates/ints")

        encoders = sample(ens.encoders, ens.n_neurons, ens.dimensions)

        # --- determine residual
        x = np.dot(eval_points, encoders.T / ens.radius)
        decoders, info = solve_for_decoders(solver, ens.neuron_type, gain,
                                            bias, x, targets, rng)
        error = info['rmses'].mean()

        errors.append((error, encoders, gain, bias, eval_points))

    # --- set parameters to those with the lowest error
    errors.sort(key=lambda x: x[0])
    ens.encoders, ens.gain, ens.bias, ens.eval_points = errors[0][1:]
Beispiel #20
0
class Connection(NengoObject):
    """Connects two objects together.

    The connection between the two object is unidirectional,
    transmitting information from the first argument, ``pre``,
    to the second argument, ``post``.

    Almost any Nengo object can act as the pre or post side of a connection.
    Additionally, you can use Python slice syntax to access only some of the
    dimensions of the pre or post object.

    For example, if ``node`` has ``size_out=2`` and ``ensemble`` has
    ``size_in=1``, we could not create the following connection::

        nengo.Connection(node, ensemble)

    But, we could create either of these two connections::

        nengo.Connection(node[0], ensemble)
        nengo.Connection(node[1], ensemble)

    Parameters
    ----------
    pre : Ensemble or Neurons or Node
        The source Nengo object for the connection.
    post : Ensemble or Neurons or Node or Probe
        The destination object for the connection.

    synapse : Synapse, optional \
              (Default: ``nengo.synapses.Lowpass(tau=0.005)``)
        Synapse model to use for filtering (see `~nengo.synapses.Synapse`).
    function : callable, optional (Default: None)
        Function to compute across the connection. Note that ``pre`` must be
        an ensemble to apply a function across the connection.
    transform : (post.size_in, pre.size_out) array_like, optional \
                (Default: ``np.array(1.0)``)
        Linear transform mapping the pre output to the post input.
        This transform is in terms of the sliced size; if either pre
        or post is a slice, the transform must be shaped according to
        the sliced dimensionality. Additionally, the function is applied
        before the transform, so if a function is computed across the
        connection, the transform must be of shape
        ``(len(function(np.zeros(post.size_in))), pre.size_out)``.
    solver : Solver, optional (Default: ``nengo.solvers.LstsqL2()``)
        Solver instance to compute decoders or weights
        (see `~nengo.solvers.Solver`). If ``solver.weights`` is True, a full
        connection weight matrix is computed instead of decoders.
    learning_rule_type : LearningRuleType or iterable of LearningRuleType, \
                         optional (Default: None)
        Modifies the decoders or connection weights during simulation.
    eval_points : (n_eval_points, pre.size_out) array_like or int, optional \
                  (Default: None)
        Points at which to evaluate ``function`` when computing decoders,
        spanning the interval (-pre.radius, pre.radius) in each dimension.
        If None, will use the eval_points associated with ``pre``.
    scale_eval_points : bool, optional (Default: True)
        Indicates whether the evaluation points should be scaled
        by the radius of the pre Ensemble.
    label : str, optional (Default: None)
        A descriptive label for the connection.
    seed : int, optional (Default: None)
        The seed used for random number generation.

    Attributes
    ----------
    is_decoded : bool
        True if and only if the connection is decoded. This will not occur
        when ``solver.weights`` is True or both pre and post are
        `~nengo.ensemble.Neurons`.
    function : callable
        The given function.
    function_size : int
        The output dimensionality of the given function. If no function is
        specified, function_size will be 0.
    label : str
        A human-readable connection label for debugging and visualization.
        If not overridden, incorporates the labels of the pre and post objects.
    learning_rule_type : instance or list or dict of LearningRuleType, optional
        The learning rule types.
    post : Ensemble or Neurons or Node or Probe or ObjView
        The given post object.
    post_obj : Ensemble or Neurons or Node or Probe
        The underlying post object, even if ``post`` is an ``ObjView``.
    post_slice : slice or list or None
        The slice associated with ``post`` if it is an ObjView, or None.
    pre : Ensemble or Neurons or Node or ObjView
        The given pre object.
    pre_obj : Ensemble or Neurons or Node
        The underlying pre object, even if ``post`` is an ``ObjView``.
    pre_slice : slice or list or None
        The slice associated with ``pre`` if it is an ObjView, or None.
    seed : int
        The seed used for random number generation.
    solver : Solver
        The Solver instance that will be used to compute decoders or weights
        (see ``nengo.solvers``).
    synapse : Synapse
        The Synapse model used for filtering across the connection
        (see ``nengo.synapses``).
    transform : (size_mid, size_out) array_like
        Linear transform mapping the pre function output to the post input.
    """

    probeable = ('output', 'input', 'weights')

    pre = PrePostParam('pre', nonzero_size_out=True)
    post = PrePostParam('post', nonzero_size_in=True)
    synapse = SynapseParam('synapse', default=Lowpass(tau=0.005))
    function_info = ConnectionFunctionParam('function',
                                            default=None,
                                            optional=True)
    transform = TransformParam('transform', default=np.array(1.0))
    solver = ConnectionSolverParam('solver', default=LstsqL2())
    learning_rule_type = ConnectionLearningRuleTypeParam('learning_rule_type',
                                                         default=None,
                                                         optional=True)
    eval_points = EvalPointsParam('eval_points',
                                  default=None,
                                  optional=True,
                                  sample_shape=('*', 'size_in'))
    scale_eval_points = BoolParam('scale_eval_points', default=True)
    modulatory = ObsoleteParam(
        'modulatory', "Modulatory connections have been removed. "
        "Connect to a learning rule instead.",
        since="v2.1.0",
        url="https://github.com/nengo/nengo/issues/632#issuecomment-71663849")

    def __init__(self,
                 pre,
                 post,
                 synapse=Default,
                 function=Default,
                 transform=Default,
                 solver=Default,
                 learning_rule_type=Default,
                 eval_points=Default,
                 scale_eval_points=Default,
                 label=Default,
                 seed=Default,
                 modulatory=Unconfigurable):
        super(Connection, self).__init__(label=label, seed=seed)

        self.pre = pre
        self.post = post

        self.synapse = synapse
        self.transform = transform
        self.scale_eval_points = scale_eval_points
        self.eval_points = eval_points  # Must be set before function
        self.function_info = function  # Must be set after transform
        self.solver = solver  # Must be set before learning rule
        self.learning_rule_type = learning_rule_type  # set after transform
        self.modulatory = modulatory

    def __str__(self):
        return "<Connection %s>" % self._str

    def __repr__(self):
        return "<Connection at 0x%x %s>" % (id(self), self._str)

    @property
    def _str(self):
        if self.label is not None:
            return self.label

        desc = "" if self.function is None else " computing '%s'" % (getattr(
            self.function, '__name__', str(self.function)))
        return "from %s to %s%s" % (self.pre, self.post, desc)

    @property
    def function(self):
        return self.function_info.function

    @function.setter
    def function(self, function):
        self.function_info = function

    @property
    def is_decoded(self):
        return not (self.solver.weights or
                    (isinstance(self.pre_obj, Neurons)
                     and isinstance(self.post_obj, Neurons)))

    @property
    def _label(self):
        if self.label is not None:
            return self.label

        return "from %s to %s%s" % (self.pre, self.post,
                                    " computing '%s'" % self.function.__name__
                                    if self.function is not None else "")

    @property
    def learning_rule(self):
        """(LearningRule or iterable) Connectable learning rule object(s)."""
        if self.learning_rule_type is not None and self._learning_rule is None:
            types = self.learning_rule_type
            if isinstance(types, dict):
                self._learning_rule = types.__class__()  # dict of same type
                for k, v in iteritems(types):
                    self._learning_rule[k] = LearningRule(self, v)
            elif is_iterable(types):
                self._learning_rule = [LearningRule(self, v) for v in types]
            elif isinstance(types, LearningRuleType):
                self._learning_rule = LearningRule(self, types)
            else:
                raise ValidationError("Invalid type %r" %
                                      types.__class__.__name__,
                                      attr='learning_rule_type',
                                      obj=self)

        return self._learning_rule

    @property
    def post_obj(self):
        return self.post.obj if isinstance(self.post, ObjView) else self.post

    @property
    def post_slice(self):
        return (self.post.slice
                if isinstance(self.post, ObjView) else slice(None))

    @property
    def pre_obj(self):
        return self.pre.obj if isinstance(self.pre, ObjView) else self.pre

    @property
    def pre_slice(self):
        return self.pre.slice if isinstance(self.pre, ObjView) else slice(None)

    @property
    def size_in(self):
        """(int) The number of output dimensions of the pre object.

        Also the input size of the function, if one is specified.
        """
        return self.pre.size_out

    @property
    def size_mid(self):
        """(int) The number of output dimensions of the function, if specified.

        If the function is not specified, then ``size_in == size_mid``.
        """
        size = self.function_info.size
        return self.size_in if size is None else size

    @property
    def size_out(self):
        """(int) The number of input dimensions of the post object.

        Also the number of output dimensions of the transform.
        """
        return self.post.size_in
Beispiel #21
0
def test_eval_points_static(plt, rng):
    solver = LstsqL2()

    n = 100
    d = 5

    eval_points = np.logspace(np.log10(300), np.log10(5000), 21)
    eval_points = np.round(eval_points).astype('int')
    max_points = eval_points.max()
    n_trials = 25
    # n_trials = 100

    rmses = np.nan * np.zeros((len(eval_points), n_trials))

    for trial in range(n_trials):
        # make a population for generating LIF tuning curves
        a = nengo.LIF(n)
        gain, bias = a.gain_bias(
            # rng.uniform(50, 100, n), rng.uniform(-1, 1, n))
            rng.uniform(50, 100, n),
            rng.uniform(-0.9, 0.9, n))

        e = get_encoders(n, d, rng=rng)

        # make one activity matrix with the max number of eval points
        train = get_eval_points(max_points, d, rng=rng)
        test = get_eval_points(max_points, d, rng=rng)
        Atrain = a.rates(np.dot(train, e), gain, bias)
        Atest = a.rates(np.dot(test, e), gain, bias)

        for i, n_points in enumerate(eval_points):
            Di, _ = solver(Atrain[:n_points], train[:n_points], rng=rng)
            rmses[i, trial] = rms(np.dot(Atest, Di) - test)

    rmses_norm1 = rmses - rmses.mean(0, keepdims=True)
    rmses_norm2 = (rmses - rmses.mean(0, keepdims=True)) / rmses.std(
        0, keepdims=True)

    def make_plot(rmses):
        mean = rmses.mean(1)
        low = rmses.min(1)
        high = rmses.max(1)
        std = rmses.std(1)
        plt.semilogx(eval_points, mean, 'k-', label='mean')
        plt.semilogx(eval_points, mean - std, 'k--', label='+/- std')
        plt.semilogx(eval_points, mean + std, 'k--')
        plt.semilogx(eval_points, high, 'r-', label='high')
        plt.semilogx(eval_points, low, 'b-', label='low')
        plt.xlim([eval_points[0], eval_points[-1]])
        # plt.xticks(eval_points, eval_points)
        plt.legend(fontsize=8, loc=1)

    plt.figure(figsize=(12, 8))
    plt.subplot(3, 1, 1)
    make_plot(rmses)
    plt.ylabel('rmse')
    plt.subplot(3, 1, 2)
    make_plot(rmses_norm1)
    plt.ylabel('rmse - mean')
    plt.subplot(3, 1, 3)
    make_plot(rmses_norm2)
    plt.ylabel('(rmse - mean) / std')
Beispiel #22
0
class Connection(NengoObject):
    """Connects two objects together.

    The connection between the two object is unidirectional,
    transmitting information from the first argument, ``pre``,
    to the second argument, ``post``.

    Almost any Nengo object can act as the pre or post side of a connection.
    Additionally, you can use Python slice syntax to access only some of the
    dimensions of the pre or post object.

    For example, if ``node`` has ``size_out=2`` and ``ensemble`` has
    ``size_in=1``, we could not create the following connection::

        nengo.Connection(node, ensemble)

    But, we could create either of these two connections::

        nengo.Connection(node[0], ensemble)
        nengo.Connection(node[1], ensemble)

    Parameters
    ----------
    pre : Ensemble or Neurons or Node
        The source Nengo object for the connection.
    post : Ensemble or Neurons or Node or Probe
        The destination object for the connection.
    synapse : Synapse or None, optional
        Synapse model to use for filtering (see `~nengo.synapses.Synapse`).
        If *None*, no synapse will be used and information will be transmitted
        without any delay (if supported by the backend---some backends may
        introduce a single time step delay).

        Note that at least one connection must have a synapse that is not
        *None* if components are connected in a cycle. Furthermore, a synaptic
        filter with a zero time constant is different from a *None* synapse
        as a synaptic filter will always add a delay of at least one time step.
    function : callable or (n_eval_points, size_mid) array_like, optional
        Function to compute across the connection. Note that ``pre`` must be
        an ensemble to apply a function across the connection.
        If an array is passed, the function is implicitly defined by the
        points in the array and the provided ``eval_points``, which have a
        one-to-one correspondence.
    transform : (size_out, size_mid) array_like, optional
        Linear transform mapping the pre output to the post input.
        This transform is in terms of the sliced size; if either pre
        or post is a slice, the transform must be shaped according to
        the sliced dimensionality. Additionally, the function is applied
        before the transform, so if a function is computed across the
        connection, the transform must be of shape ``(size_out, size_mid)``.
    solver : Solver, optional
        Solver instance to compute decoders or weights
        (see `~nengo.solvers.Solver`). If ``solver.weights`` is True, a full
        connection weight matrix is computed instead of decoders.
    learning_rule_type : LearningRuleType or iterable of LearningRuleType, optional
        Modifies the decoders or connection weights during simulation.
    eval_points : (n_eval_points, size_in) array_like or int, optional
        Points at which to evaluate ``function`` when computing decoders,
        spanning the interval (-pre.radius, pre.radius) in each dimension.
        If None, will use the eval_points associated with ``pre``.
    scale_eval_points : bool, optional
        Indicates whether the evaluation points should be scaled
        by the radius of the pre Ensemble.
    label : str, optional
        A descriptive label for the connection.
    seed : int, optional
        The seed used for random number generation.

    Attributes
    ----------
    is_decoded : bool
        True if and only if the connection is decoded. This will not occur
        when ``solver.weights`` is True or both pre and post are
        `~nengo.ensemble.Neurons`.
    function : callable
        The given function.
    function_size : int
        The output dimensionality of the given function. If no function is
        specified, function_size will be 0.
    label : str
        A human-readable connection label for debugging and visualization.
        If not overridden, incorporates the labels of the pre and post objects.
    learning_rule_type : instance or list or dict of LearningRuleType, optional
        The learning rule types.
    post : Ensemble or Neurons or Node or Probe or ObjView
        The given post object.
    post_obj : Ensemble or Neurons or Node or Probe
        The underlying post object, even if ``post`` is an ``ObjView``.
    post_slice : slice or list or None
        The slice associated with ``post`` if it is an ObjView, or None.
    pre : Ensemble or Neurons or Node or ObjView
        The given pre object.
    pre_obj : Ensemble or Neurons or Node
        The underlying pre object, even if ``post`` is an ``ObjView``.
    pre_slice : slice or list or None
        The slice associated with ``pre`` if it is an ObjView, or None.
    seed : int
        The seed used for random number generation.
    solver : Solver
        The Solver instance that will be used to compute decoders or weights
        (see ``nengo.solvers``).
    synapse : Synapse
        The Synapse model used for filtering across the connection
        (see ``nengo.synapses``).
    transform : (size_out, size_mid) array_like
        Linear transform mapping the pre function output to the post input.

    Properties
    ----------
    size_in : int
        The number of output dimensions of the pre object.
        Also the input size of the function, if one is specified.
    size_mid : int
        The number of output dimensions of the function, if specified.
        If the function is not specified, then ``size_in == size_mid``.
    size_out : int
        The number of input dimensions of the post object.
        Also the number of output dimensions of the transform.
    """

    probeable = ("output", "input", "weights")

    pre = PrePostParam("pre", nonzero_size_out=True)
    post = PrePostParam("post", nonzero_size_in=True)
    synapse = SynapseParam("synapse", default=Lowpass(tau=0.005))
    function_info = ConnectionFunctionParam("function",
                                            default=None,
                                            optional=True)
    transform = ConnectionTransformParam("transform", default=1.0)
    solver = ConnectionSolverParam("solver", default=LstsqL2())
    learning_rule_type = ConnectionLearningRuleTypeParam("learning_rule_type",
                                                         default=None,
                                                         optional=True)
    eval_points = EvalPointsParam("eval_points",
                                  default=None,
                                  optional=True,
                                  sample_shape=("*", "size_in"))
    scale_eval_points = BoolParam("scale_eval_points", default=True)
    modulatory = ObsoleteParam(
        "modulatory",
        "Modulatory connections have been removed. "
        "Connect to a learning rule instead.",
        since="v2.1.0",
        url="https://github.com/nengo/nengo/issues/632#issuecomment-71663849",
    )

    _param_init_order = [
        "pre",
        "post",
        "synapse",
        "eval_points",
        "function_info",
        "transform",
        "solver",
        "learning_rule_type",
    ]

    def __init__(
        self,
        pre,
        post,
        synapse=Default,
        function=Default,
        transform=Default,
        solver=Default,
        learning_rule_type=Default,
        eval_points=Default,
        scale_eval_points=Default,
        label=Default,
        seed=Default,
        modulatory=Unconfigurable,
    ):
        super().__init__(label=label, seed=seed)

        self.pre = pre
        self.post = post

        self.synapse = synapse
        self.eval_points = eval_points  # Must be set before function
        self.scale_eval_points = scale_eval_points
        self.function_info = function
        self.transform = transform  # Must be set after function
        self.solver = solver  # Must be set before learning rule
        self.learning_rule_type = learning_rule_type  # set after transform
        self.modulatory = modulatory

    def __str__(self):
        return self._str(include_id=False)

    def __repr__(self):
        return self._str(include_id=True)

    def _str(self, include_id):
        desc = "<Connection "
        if include_id:
            desc += "at 0x%x " % id(self)

        if self.label is None:
            desc += "from %s to %s%s" % (
                self.pre,
                self.post,
                ("" if self.function is None else " computing '%s'" %
                 (function_name(self.function))),
            )
        else:
            desc += self.label

        desc += ">"

        return desc

    @property
    def function(self):
        return self.function_info.function

    @function.setter
    def function(self, function):
        self.function_info = function

    @property
    def is_decoded(self):
        return not (self.solver.weights or
                    (isinstance(self.pre_obj, Neurons)
                     and isinstance(self.post_obj, Neurons)))

    @property
    def _label(self):
        if self.label is not None:
            return self.label

        return "from %s to %s%s" % (
            self.pre,
            self.post,
            " computing '%s'" %
            function_name(self.function) if self.function is not None else "",
        )

    @property
    def learning_rule(self):
        """(LearningRule or iterable) Connectable learning rule object(s)."""
        if self.learning_rule_type is None:
            return None

        types = self.learning_rule_type
        if isinstance(types, dict):
            learning_rule = type(types)()  # dict of same type
            for k, v in types.items():
                learning_rule[k] = LearningRule(self, v)
        elif is_iterable(types):
            learning_rule = [LearningRule(self, v) for v in types]
        elif isinstance(types, LearningRuleType):
            learning_rule = LearningRule(self, types)
        else:
            raise ValidationError(
                "Invalid type %r" % type(types).__name__,
                attr="learning_rule_type",
                obj=self,
            )

        return learning_rule

    @property
    def post_obj(self):
        return self.post.obj if isinstance(self.post, ObjView) else self.post

    @property
    def post_slice(self):
        return self.post.slice if isinstance(self.post,
                                             ObjView) else slice(None)

    @property
    def pre_obj(self):
        return self.pre.obj if isinstance(self.pre, ObjView) else self.pre

    @property
    def pre_slice(self):
        return self.pre.slice if isinstance(self.pre, ObjView) else slice(None)

    @property
    def size_in(self):
        """(int) The number of output dimensions of the pre object.

        Also the input size of the function, if one is specified.
        """
        return self.pre.size_out

    @property
    def size_mid(self):
        """(int) The number of output dimensions of the function, if specified.

        If the function is not specified, then ``size_in == size_mid``.
        """
        size = self.function_info.size
        return self.size_in if size is None else size

    @property
    def size_out(self):
        """(int) The number of input dimensions of the post object.

        Also the number of output dimensions of the transform.
        """
        return self.post.size_in
Beispiel #23
0
class Temporal(Solver, SupportDefaultsMixin):
    """Solves for connection weights by accounting for the neural dynamics.

    This allows the optimization procedure to potentially harness any
    correlations in spike-timing between neurons, and/or the adaptative
    dynamics of more detailed neuron models, given the dynamics
    of the desired function with respect to the evaluation points.
    This works by explicitly simulating the neurons given the stimulus, and
    then learning to decode the desired function in the time-domain.

    To use this method, pass it to the ``solver`` parameter for a
    :class:`nengo.Connection`. The ``pre`` object on this connection should be
    a :class:`nengo.Ensemble` that uses some dynamic neuron model.

    Parameters
    ----------
    synapse : :class:`nengo.synapses.Synapse`, optional
        The :class:`nengo.synapses.Synapse` model used to filter the
        pre-synaptic activities of the neurons before being passed to the
        underlying solver. A value of ``None`` will bypass any filtering.
        Defaults to a :class:`nengo.Lowpass` filter with a time-constant of
        5 ms.
    solver : :class:`nengo.solvers.Solver`, optional
        The underlying :class:`nengo.solvers.Solver` used to solve the problem
        ``AD = Y``, where ``A`` are the (potentially filtered) neural
        activities (in response to the evaluation points, over time), ``D``
        are the Nengo decoders, and ``Y`` are the corresponding targets given
        by the ``function`` supplied to the connection.
        Defaults to :class:`nengo.solvers.LstsqL2`.

    See Also
    --------
    :class:`.RLS`
    :class:`nengo.Connection`
    :class:`nengo.solvers.Solver`
    :mod:`.synapses`

    Notes
    -----
    Requires ``nengo>=2.5.0``
    (specifically, `PR #1313 <https://github.com/nengo/nengo/pull/1313>`_).

    If the neuron model for the pre-synaptic population includes some
    internal state that varies over time (which it should, otherwise there is
    little point in using this solver), then the order of the given evaluation
    points will matter. You will likely want to supply them as an array, rather
    than as a distribution. Likewise, you may want to filter your desired
    output, and specify the function as an array on the connection (see example
    below).

    The effect of the solver's regularization has a very different
    interpretation in this context (due to the filtered spiking error having
    its own statistics), and so you may also wish to instantiate the solver
    yourself with some value other than the default regularization.

    Examples
    --------
    Below we use the temporal solver to learn a filtered communication-channel
    (the identity function) using 100 low-threshold spiking (LTS) Izhikevich
    neurons. The training and test data are sampled independently from the
    same band-limited white-noise process.

    >>> from nengolib import Temporal, Network
    >>> import nengo
    >>> neuron_type = nengo.Izhikevich(coupling=0.25)
    >>> tau = 0.005
    >>> process = nengo.processes.WhiteSignal(period=5, high=5, y0=0, rms=0.3)
    >>> eval_points = process.run_steps(5000)
    >>> with Network() as model:
    >>>     stim = nengo.Node(output=process)
    >>>     x = nengo.Ensemble(100, 1, neuron_type=neuron_type)
    >>>     out = nengo.Node(size_in=1)
    >>>     nengo.Connection(stim, x, synapse=None)
    >>>     nengo.Connection(x, out, synapse=None,
    >>>                      eval_points=eval_points,
    >>>                      function=nengo.Lowpass(tau).filt(eval_points),
    >>>                      solver=Temporal(synapse=tau))
    >>>     p_actual = nengo.Probe(out, synapse=tau)
    >>>     p_ideal = nengo.Probe(stim, synapse=tau)
    >>> with nengo.Simulator(model) as sim:
    >>>     sim.run(5)

    >>> import matplotlib.pyplot as plt
    >>> plt.plot(sim.trange(), sim.data[p_actual], label="Actual")
    >>> plt.plot(sim.trange(), sim.data[p_ideal], label="Ideal")
    >>> plt.xlabel("Time (s)")
    >>> plt.legend()
    >>> plt.show()
    """

    synapse = SynapseParam('synapse',
                           default=Lowpass(tau=0.005),
                           readonly=True)
    solver = SolverParam('solver', default=LstsqL2(), readonly=True)

    def __init__(self, synapse=Default, solver=Default):
        # We can't use super here because we need the defaults mixin
        # in order to determine self.solver.weights.
        SupportDefaultsMixin.__init__(self)
        self.synapse = synapse
        self.solver = solver
        Solver.__init__(self, weights=self.solver.weights)

    def __call__(self, A, Y, __hack__=None, **kwargs):
        assert __hack__ is None
        # __hack__ is necessary prior to nengo PR #1359 (<2.6.1)
        # and following nengo PR #1507 (>2.8.0)

        # Note: mul_encoders is never called directly on self.
        # It is invoked on the sub-solver through the following call.
        return self.solver.__call__(A, Y, **kwargs)
Beispiel #24
0
@pytest.mark.parametrize(
    "reference, equal, different",
    (
        (True, True, False),  # bool
        (False, False, True),  # bool
        (1.0, 1.0, 2.0),  # float
        (1, 1, 2),  # int
        (1.0 + 2.0j, 1 + 2j, 2.0 + 1j),  # complex
        (b"a", b"a", b"b"),  # bytes
        ((0, 1), (0, 1), (0, 2)),  # tuple
        ([0, 1], [0, 1], [0, 2]),  # list
        ("a", "a", "b"),  # str
        (np.eye(2), np.eye(2), np.array([[0, 1], [1, 0]])),  # array
        (DummyA(), DummyA(), DummyB()),  # object instance
        (DummyA(1), DummyA(1), DummyA(2)),  # object instance
        (LstsqL2(reg=0.1), LstsqL2(reg=0.1), LstsqL2(reg=0.2)),  # solver
    ),
)
def test_fingerprinting(reference, equal, different):
    assert str(Fingerprint(reference)) == str(Fingerprint(equal))
    assert str(Fingerprint(reference)) != str(Fingerprint(different))


@pytest.mark.parametrize(
    "obj",
    (
        np.array([object()]),  # array
        np.array([(1.0, )], dtype=[("field1", "f8")]),  # array
        object(),  # object instance
        dummy_fn,  # function
    ),
Beispiel #25
0
def modelController(neuronCount=100, tau=0.001):
    client = Client()
    # Set up neurons
    model = nengo.Network()

    with model:
        # Neuron ensembles
        error = nengo.Ensemble(neuronCount,
                               dimensions=4,
                               radius=100,
                               label='error')
        control = nengo.Ensemble(neuronCount,
                                 dimensions=2,
                                 radius=100,
                                 label='control')
        enModel = nengo.Ensemble(neuronCount,
                                 dimensions=4,
                                 radius=100,
                                 label='enModel')
        feedback = nengo.Ensemble(neuronCount,
                                  dimensions=4,
                                  radius=100,
                                  label='feedback')
        oracle = nengo.Ensemble(neuronCount, dimensions=4, radius=100)

        # Non-neural nodes and functions
        socket_in = nengo.Node(client.get, size_out=8, label='socket_in')
        state = nengo.Node(stateFilter, size_in=8, size_out=4, label='state')
        goal = nengo.Node(goalFilter, size_in=8, size_out=4, label='goal')
        socket_out = nengo.Node(client.put, size_in=2, label='socket_out')

        # Neural connections

        # Split up socket input into current observed state and goal state
        nengo.Connection(socket_in, state)
        nengo.Connection(socket_in, goal)

        # Create error signal between current state and goal
        nengo.Connection(state, error, synapse=tau)
        nengo.Connection(goal,
                         error,
                         synapse=tau,
                         transform=[[-1, 0, 0, 0], [0, -1, 0, 0],
                                    [0, 0, -1, 0], [0, 0, 0, -1]])

        # Naive first estimate of control signal, where f = 1*error_p + 4*error_v
        nengo.Connection(error,
                         control,
                         synapse=tau,
                         solver=LstsqL2(weights=True),
                         transform=[[1, 0, 10, 0], [0, 1, 0, 10]])

        # Create model of system by double integrating control
        model_conn = nengo.Connection(control,
                                      enModel,
                                      synapse=tau,
                                      solver=LstsqL2(weights=True),
                                      transform=[[0, 0], [0, 0], [-tau, 0],
                                                 [0, -tau]])
        model_fb_conn = nengo.Connection(enModel,
                                         enModel,
                                         synapse=tau,
                                         solver=LstsqL2(weights=True),
                                         transform=[[1, 0, 1, 0], [0, 1, 0, 1],
                                                    [0, 0, 1, 0], [0, 0, 0,
                                                                   1]])
        # Create feedback signals that compare the models to the actual states
        nengo.Connection(enModel,
                         feedback,
                         synapse=tau,
                         solver=LstsqL2(weights=True))
        nengo.Connection(state,
                         feedback,
                         synapse=tau,
                         transform=[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, -1, 0],
                                    [0, 0, 0, -1]])

        # Implement learning rules for models
        model_conn.learning_rule_type = nengo.PES(learning_rate=1e-7)
        model_fb_conn.learning_rule_type = nengo.PES(learning_rate=1e-7)
        nengo.Connection(feedback, model_conn.learning_rule)
        nengo.Connection(feedback, model_fb_conn.learning_rule)

        # Route control signal back out to the socket
        nengo.Connection(control, socket_out, synapse=tau)

    return model
Beispiel #26
0
class Connection(NengoObject):
    """Connects two objects together.

    The connection between the two object is unidirectional,
    transmitting information from the first argument, ``pre``,
    to the second argument, ``post``.

    Almost any Nengo object can act as the pre or post side of a connection.
    Additionally, you can use Python slice syntax to access only some of the
    dimensions of the pre or post object.

    For example, if ``node`` has ``size_out=2`` and ``ensemble`` has
    ``size_in=1``:

    .. testcode::

       with nengo.Network() as net:
           node = nengo.Node(np.zeros(2))
           ensemble = nengo.Ensemble(10, 1)

    We could not create the following connection:

    .. testcode::

       with net:
           nengo.Connection(node, ensemble)

    .. testoutput::
       :hide:

       Traceback (most recent call last):
       ...
       nengo.exceptions.ValidationError: init: Shape of initial value () does not \
       match expected shape (1, 2)

    But, we could create either of these two connections:

    .. testcode::

       with net:
           nengo.Connection(node[0], ensemble)
           nengo.Connection(node[1], ensemble)

    Parameters
    ----------
    pre : Ensemble or Neurons or Node
        The source Nengo object for the connection.
    post : Ensemble or Neurons or Node or LearningRule
        The destination object for the connection.
    synapse : Synapse or None, optional
        Synapse model to use for filtering (see `~nengo.synapses.Synapse`).
        If *None*, no synapse will be used and information will be transmitted
        without any delay (if supported by the backend---some backends may
        introduce a single time step delay).

        Note that at least one connection must have a synapse that is not
        *None* if components are connected in a cycle. Furthermore, a synaptic
        filter with a zero time constant is different from a *None* synapse
        as a synaptic filter will always add a delay of at least one time step.
    function : callable or (n_eval_points, size_mid) array_like, optional
        Function to compute across the connection. Note that ``pre`` must be
        an ensemble to apply a function across the connection.
        If an array is passed, the function is implicitly defined by the
        points in the array and the provided ``eval_points``, which have a
        one-to-one correspondence.
    transform : (size_out, size_mid) array_like, optional
        Linear transform mapping the pre output to the post input.
        This transform is in terms of the sliced size; if either pre
        or post is a slice, the transform must be shaped according to
        the sliced dimensionality. Additionally, the function is applied
        before the transform, so if a function is computed across the
        connection, the transform must be of shape ``(size_out, size_mid)``.
    solver : Solver, optional
        Solver instance to compute decoders or weights
        (see `~nengo.solvers.Solver`). If ``solver.weights`` is True, a full
        connection weight matrix is computed instead of decoders.
    learning_rule_type : LearningRuleType or iterable of LearningRuleType, optional
        Modifies the decoders or connection weights during simulation.
    eval_points : (n_eval_points, size_in) array_like or int, optional
        Points at which to evaluate ``function`` when computing decoders,
        spanning the interval (-pre.radius, pre.radius) in each dimension.
        If None, will use the eval_points associated with ``pre``.
    scale_eval_points : bool, optional
        Indicates whether the evaluation points should be scaled
        by the radius of the pre Ensemble.
    label : str, optional
        A descriptive label for the connection.
    seed : int, optional
        The seed used for random number generation.

    Attributes
    ----------
    function : callable
        The given function.
    function_size : int
        The output dimensionality of the given function. If no function is
        specified, function_size will be 0.
    label : str
        A human-readable connection label for debugging and visualization.
        If not overridden, incorporates the labels of the pre and post objects.
    learning_rule_type : instance or list or dict of LearningRuleType, optional
        The learning rule types.
    post : Ensemble or Neurons or Node or Probe or ObjView
        The given post object.
    post_obj : Ensemble or Neurons or Node or Probe
        The underlying post object, even if ``post`` is an ``ObjView``.
    post_slice : slice or list or None
        The slice associated with ``post`` if it is an ObjView, or None.
    pre : Ensemble or Neurons or Node or ObjView
        The given pre object.
    pre_obj : Ensemble or Neurons or Node
        The underlying pre object, even if ``post`` is an ``ObjView``.
    pre_slice : slice or list or None
        The slice associated with ``pre`` if it is an ObjView, or None.
    seed : int
        The seed used for random number generation.
    solver : Solver
        The Solver instance that will be used to compute decoders or weights
        (see ``nengo.solvers``).
    synapse : Synapse
        The Synapse model used for filtering across the connection
        (see ``nengo.synapses``).
    transform : (size_out, size_mid) array_like
        Linear transform mapping the pre function output to the post input.

    Properties
    ----------
    learning_rule : LearningRule or iterable of LearningRule
        Connectable learning rule object(s) associated with this connection.
    size_in : int
        The number of output dimensions of the pre object.
        Also the input size of the function, if one is specified.
    size_mid : int
        The number of output dimensions of the function, if specified.
        If the function is not specified, then ``size_in == size_mid``.
    size_out : int
        The number of input dimensions of the post object.
        Also the number of output dimensions of the transform.
    """

    probeable = ("output", "input", "weights")

    pre = PrePostParam("pre", nonzero_size_out=True)
    post = PrePostParam("post", nonzero_size_in=True)
    synapse = SynapseParam("synapse", default=Lowpass(tau=0.005))
    function_info = ConnectionFunctionParam("function",
                                            default=None,
                                            optional=True)
    transform = ConnectionTransformParam("transform",
                                         default=None,
                                         optional=True)
    solver = ConnectionSolverParam("solver", default=LstsqL2())
    learning_rule_type = ConnectionLearningRuleTypeParam("learning_rule_type",
                                                         default=None,
                                                         optional=True)
    eval_points = EvalPointsParam("eval_points",
                                  default=None,
                                  optional=True,
                                  sample_shape=("*", "size_in"))
    scale_eval_points = BoolParam("scale_eval_points", default=True)

    _param_init_order = [
        "pre",
        "post",
        "synapse",
        "eval_points",
        "function_info",
        "transform",
        "solver",
        "learning_rule_type",
    ]

    def __init__(
        self,
        pre,
        post,
        synapse=Default,
        function=Default,
        transform=Default,
        solver=Default,
        learning_rule_type=Default,
        eval_points=Default,
        scale_eval_points=Default,
        label=Default,
        seed=Default,
    ):
        super().__init__(label=label, seed=seed)

        self.pre = pre
        self.post = post

        self.synapse = synapse
        self.eval_points = eval_points  # Must be set before function
        self.scale_eval_points = scale_eval_points
        self.function_info = function
        self.transform = transform  # Must be set after function
        self.solver = solver  # Must be set before learning rule
        self.learning_rule_type = learning_rule_type  # set after transform

    def __str__(self):
        return self._str(include_id=False)

    def __repr__(self):
        return self._str(include_id=True)

    def _str(self, include_id):
        desc = "<Connection "
        if include_id:
            desc += f"at 0x{id(self):x} "

        if self.label is None:
            func_txt = ("" if self.function is None else
                        f" computing '{function_name(self.function)}'")
            desc += f"from {self.pre} to {self.post}{func_txt}"
        else:
            desc += self.label

        desc += ">"

        return desc

    @property
    def function(self):
        return self.function_info.function

    @function.setter
    def function(self, function):
        self.function_info = function

    @property
    def has_weights(self):
        return not isinstance(self.transform, NoTransform) or (
            isinstance(self.pre_obj, Ensemble)
            and not isinstance(self.pre_obj.neuron_type, Direct))

    @property
    def is_decoded(self):
        warnings.warn(
            "is_decoded is deprecated; directly check the pre/post objects for the "
            "properties of interest instead",
            DeprecationWarning,
        )
        return not (self.solver.weights or
                    (isinstance(self.pre_obj, Neurons)
                     and isinstance(self.post_obj, Neurons)))

    @property
    def _to_neurons(self):
        return isinstance(self.post_obj, Neurons) or (isinstance(
            self.pre_obj, Ensemble) and isinstance(self.post_obj, Ensemble)
                                                      and self.solver.weights)

    @property
    def learning_rule(self):
        """(LearningRule or iterable) Connectable learning rule object(s)."""
        if self.learning_rule_type is None:
            return None

        types = self.learning_rule_type
        if isinstance(types, dict):
            learning_rule = type(types)()  # dict of same type
            for k, v in types.items():
                learning_rule[k] = LearningRule(self, v)
        elif is_iterable(types):
            learning_rule = [LearningRule(self, v) for v in types]
        elif isinstance(types, LearningRuleType):
            learning_rule = LearningRule(self, types)
        else:
            assert False, "Validation should catch this"

        return learning_rule

    @property
    def post_obj(self):
        return self.post.obj if isinstance(self.post, ObjView) else self.post

    @property
    def post_slice(self):
        return self.post.slice if isinstance(self.post,
                                             ObjView) else slice(None)

    @property
    def pre_obj(self):
        return self.pre.obj if isinstance(self.pre, ObjView) else self.pre

    @property
    def pre_slice(self):
        return self.pre.slice if isinstance(self.pre, ObjView) else slice(None)

    @property
    def size_in(self):
        """(int) The number of output dimensions of the pre object.

        Also the input size of the function, if one is specified.
        """
        return self.pre.size_out

    @property
    def size_mid(self):
        """(int) The number of output dimensions of the function, if specified.

        If the function is not specified, then ``size_in == size_mid``.
        """
        size = self.function_info.size
        return self.size_in if size is None else size

    @property
    def size_out(self):
        """(int) The number of input dimensions of the post object.

        Also the number of output dimensions of the transform.
        """
        return self.post.size_in
Beispiel #27
0
    def train(self,
              function,
              t,
              dt,
              process,
              seed=None,
              t_init=0,
              solver=LstsqL2(),
              rng=None):
        """Train an optimal linear readout.

        Afterwards, the decoded and filtered output will be available in the
        model by connecting from the ``output`` Node, or by invoking the
        ``run`` method.

        Parameters
        ----------
        function : callable
            A function that maps the input signal obtained from simulating the
            process (as an ``M``-by-``D`` array, where ``M`` is the number of
            timesteps, and ``D`` is the input dimensionality), to the desired
            signal (of the same shape).
        t : float
            A positive number indicating how long the training signal should be
            in simulation seconds.
        dt : float
            A positive number indicating the time elapsed between each
            timestep. The length of the test signal will be ``int(t / dt)``.
        process : nengo.Process
            An autonomous process that provides a training signal of
            appropriate dimensionality to match the input objects.
        seed : int, optional (Default: ``None``)
            Seed used to initialize the simulator.
        t_init : int, optional (Default: ``0``)
            The number of seconds to discard from the start.
        solver : nengo.solvers.Solver (Default: ``nengo.solvers.LstsqL2()``)
            Solves for ``D`` such that ``AD ~ Y``.
        rng : ``numpy.random.RandomState``, optional (Default: ``None``)
            Random state passed to the solver.
        """

        # Do a safety check for seeds. Note that even if the overall
        # network has a seed, that doesn't necessarily mean everything will
        # be good, because adding components to the network after training
        # may result in seeds being shuffled around within the objects.
        for ens in self.network.all_ensembles:
            if ens.seed is None:
                warnings.warn("reservoir ensemble (%s) should have its own "
                              "seed to help ensure that its parameters do not "
                              "change between training and testing" % ens)

        sim, (data_in, data_mid, _) = self.run(t, dt, process, seed)

        target = np.atleast_1d(function(data_in))
        if target.ndim == 1:
            target = target[:, None]
        if len(data_in) != len(target):
            raise RuntimeError(
                "function expected to return signal of length %d, received %d "
                "instead" % (len(data_in), len(target)))

        offset = int(t_init / dt)
        decoders, info = solver(data_mid[offset:], target[offset:], rng=rng)

        # Update dummy node
        self.output.size_in = self.output.size_out = self.size_out = (
            target.shape[1])
        self._readout.transform = decoders.T

        info.update({'sim': sim, 'data_in': data_in, 'data_mid': data_mid})
        return decoders, info
Beispiel #28
0
def pre_fixture(model):
    brd.add_params(model)

    solver = solvers.FallbackSolver(
        [LstsqL2(reg=0.01), solvers.CVXSolver(reg=0.01)])
    model.config[nengo.Ensemble].solver = solver
Beispiel #29
0
@pytest.mark.parametrize(
    'reference, equal, different',
    (
        (True, True, False),  # bool
        (False, False, True),  # bool
        (1.0, 1.0, 2.0),  # float
        (1, 1, 2),  # int
        (1.0 + 2.0j, 1 + 2j, 2.0 + 1j),  # complex
        (b'a', b'a', b'b'),  # bytes
        ((0, 1), (0, 1), (0, 2)),  # tuple
        ([0, 1], [0, 1], [0, 2]),  # list
        ('a', 'a', 'b'),  # str
        (np.eye(2), np.eye(2), np.array([[0, 1], [1, 0]])),  # array
        (DummyA(), DummyA(), DummyB()),  # object instance
        (DummyA(1), DummyA(1), DummyA(2)),  # object instance
        (LstsqL2(reg=.1), LstsqL2(reg=.1), LstsqL2(reg=.2)),  # solver
    ))
def test_fingerprinting(reference, equal, different):
    assert str(Fingerprint(reference)) == str(Fingerprint(equal))
    assert str(Fingerprint(reference)) != str(Fingerprint(different))


@pytest.mark.parametrize(
    'obj',
    (
        np.array([object()]),  # array
        np.array([(1., )], dtype=[('field1', 'f8')]),  # array
        {
            'a': 1,
            'b': 2
        },  # dict
Beispiel #30
0
class Connection(NengoObject):
    """Connects two objects together.

    Almost any Nengo object can act as the pre or post side of a connection.
    Additionally, you can use Python slice syntax to access only some of the
    dimensions of the pre or post object.

    For example, if ``node`` has ``size_out=2`` and ``ensemble`` has
    ``size_in=1``, we could not create the following connection::

        nengo.Connection(node, ensemble)

    But, we could create either of these two connections.

        nengo.Connection(node[0], ensemble)
        nengo.Connection(ndoe[1], ensemble)

    Parameters
    ----------
    pre : Ensemble or Neurons or Node
        The source Nengo object for the connection.
    post : Ensemble or Neurons or Node or Probe
        The destination object for the connection.

    label : string
        A descriptive label for the connection.
    dimensions : int
        The number of output dimensions of the pre object, including
        `function`, but not including `transform`.
    eval_points : (n_eval_points, pre_size) array_like or int
        Points at which to evaluate `function` when computing decoders,
        spanning the interval (-pre.radius, pre.radius) in each dimension.
    synapse : float, optional
        Post-synaptic time constant (PSTC) to use for filtering.
    transform : (post_size, pre_size) array_like, optional
        Linear transform mapping the pre output to the post input.
        This transform is in terms of the sliced size; if either pre
        or post is a slice, the transform must be of shape
        (len(pre_slice), len(post_slice)).
    solver : Solver
        Instance of a Solver class to compute decoders or weights
        (see `nengo.solvers`). If solver.weights is True, a full
        connection weight matrix is computed instead of decoders.
    function : callable, optional
        Function to compute using the pre population (pre must be Ensemble).
    modulatory : bool, optional
        Specifies whether the connection is modulatory (does not physically
        connect to post, for use by learning rules), or not (default).
    eval_points : (n_eval_points, pre_size) array_like or int, optional
        Points at which to evaluate `function` when computing decoders,
        spanning the interval (-pre.radius, pre.radius) in each dimension.
    scale_eval_points : bool
        Indicates whether the eval_points should be scaled by the radius of
        the pre Ensemble. Defaults to True.
    learning_rule_type : instance or list or dict of LearningRuleType, optional
        Methods of modifying the connection weights during simulation.

    Attributes
    ----------
    dimensions : int
        The number of output dimensions of the pre object, including
        `function`, but before applying the `transform`.
    function : callable
        The given function.
    function_size : int
        The output dimensionality of the given function. Defaults to 0.
    label : str
        A human-readable connection label for debugging and visualization.
        Incorporates the labels of the pre and post objects.
    learning_rule : LearningRule or collection of LearningRule
        The LearningRule objects corresponding to `learning_rule_type`, and in
        the same format. Use these to probe the learning rules.
    learning_rule_type : instance or list or dict of LearningRuleType, optional
        The learning rule types.
    post : Ensemble or Neurons or Node or Probe
        The given pre object.
    pre : Ensemble or Neurons or Node
        The given pre object.
    transform : (post_size, pre_size) array_like
        Linear transform mapping the pre output to the post input.
    modulatory : bool
        Whether the output of this signal is to act as an error signal for a
        learning rule.
    seed : int
        The seed used for random number generation.
    """

    pre = NengoObjectParam(nonzero_size_out=True)
    post = NengoObjectParam(nonzero_size_in=True)
    synapse = SynapseParam(default=Lowpass(0.005))
    transform = TransformParam(default=np.array(1.0))
    solver = ConnectionSolverParam(default=LstsqL2())
    function_info = ConnectionFunctionParam(default=None, optional=True)
    modulatory = BoolParam(default=False)
    learning_rule_type = ConnectionLearningRuleTypeParam(default=None,
                                                         optional=True)
    eval_points = EvalPointsParam(default=None,
                                  optional=True,
                                  sample_shape=('*', 'size_in'))
    scale_eval_points = BoolParam(default=True)
    seed = IntParam(default=None, optional=True)
    probeable = ListParam(default=['output', 'input', 'transform', 'decoders'])

    def __init__(self,
                 pre,
                 post,
                 synapse=Default,
                 transform=Default,
                 solver=Default,
                 learning_rule_type=Default,
                 function=Default,
                 modulatory=Default,
                 eval_points=Default,
                 scale_eval_points=Default,
                 seed=Default):
        self.pre = pre
        self.post = post

        self.probeable = Default
        self.solver = solver  # Must be set before learning rule
        self.learning_rule_type = learning_rule_type
        self.modulatory = modulatory
        self.synapse = synapse
        self.transform = transform
        self.scale_eval_points = scale_eval_points
        self.eval_points = eval_points  # Must be set before function
        self.function_info = function  # Must be set after transform

    @property
    def function(self):
        return self.function_info.function

    @function.setter
    def function(self, function):
        self.function_info = function

    @property
    def pre_obj(self):
        return self.pre.obj if isinstance(self.pre, ObjView) else self.pre

    @property
    def pre_slice(self):
        return self.pre.slice if isinstance(self.pre, ObjView) else slice(None)

    @property
    def post_obj(self):
        return self.post.obj if isinstance(self.post, ObjView) else self.post

    @property
    def post_slice(self):
        return (self.post.slice
                if isinstance(self.post, ObjView) else slice(None))

    @property
    def size_in(self):
        """Output size of sliced `pre`; input size of the function."""
        return self.pre.size_out

    @property
    def size_mid(self):
        """Output size of the function; input size of the transform.

        If the function is None, then `size_in == size_mid`.
        """
        size = self.function_info.size
        return self.size_in if size is None else size

    @property
    def size_out(self):
        """Output size of the transform; input size to the sliced post."""
        return self.post.size_in

    @property
    def _label(self):
        return "from %s to %s%s" % (self.pre, self.post,
                                    " computing '%s'" % self.function.__name__
                                    if self.function is not None else "")

    def __str__(self):
        return "<Connection %s>" % self._label

    def __repr__(self):
        return "<Connection at 0x%x %s>" % (id(self), self._label)

    @property
    def learning_rule(self):
        if self.learning_rule_type is not None and self._learning_rule is None:
            types = self.learning_rule_type
            if isinstance(types, dict):
                self._learning_rule = types.__class__()  # dict of same type
                for k, v in iteritems(types):
                    self._learning_rule[k] = LearningRule(self, v)
            elif is_iterable(types):
                self._learning_rule = [LearningRule(self, v) for v in types]
            elif isinstance(types, LearningRuleType):
                self._learning_rule = LearningRule(self, types)
            else:
                raise ValueError("Invalid type for `learning_rule_type`: %s" %
                                 (types.__class__.__name__))
        return self._learning_rule