Ejemplo n.º 1
0
def test_pes_learning_rate(Simulator, plt, seed):
    n = 50
    dt = 0.0005
    T = 1.0
    initial = 0.7
    desired = -0.9
    epsilon = 1e-3  # get to factor epsilon with T seconds

    # Get activity vector and initial decoders
    with Network(seed=seed) as model:
        x = nengo.Ensemble(
            n, 1, seed=seed, neuron_type=nengo.neurons.LIFRate())
        y = nengo.Node(size_in=1)
        conn = nengo.Connection(x, y, synapse=None)

    sim = Simulator(model, dt=dt)
    a = get_activities(sim.model, x, [initial])
    d = sim.data[conn].weights
    assert np.any(a > 0)

    # Use util function to calculate learning_rate
    init_error = float(desired - np.dot(d, a))
    learning_rate, gamma = pes_learning_rate(
        epsilon / abs(init_error), a, T, dt)

    # Build model with no filtering on any connections
    with model:
        stim = nengo.Node(output=initial)
        ystar = nengo.Node(output=desired)

        conn.learning_rule_type = nengo.PES(
            pre_tau=1e-15, learning_rate=learning_rate)

        nengo.Connection(stim, x, synapse=None)
        nengo.Connection(ystar, conn.learning_rule, synapse=0, transform=-1)
        nengo.Connection(y, conn.learning_rule, synapse=0)

        p = nengo.Probe(y, synapse=None)
        decoders = nengo.Probe(conn, 'weights', synapse=None)

    sim = Simulator(model, dt=dt)
    sim.run(T)

    # Check that the final error is exactly epsilon
    assert np.allclose(abs(desired - sim.data[p][-1]), epsilon)

    # Check that all of the errors are given exactly by gamma**k
    k = np.arange(len(sim.trange()))
    error = init_error * gamma ** k
    assert np.allclose(sim.data[p].flatten(), desired - error)

    # Check that all of the decoders are equal to their analytical solution
    dk = d.T + init_error * a.T[:, None] * (1 - gamma ** k) / np.dot(a, a.T)
    assert np.allclose(dk, np.squeeze(sim.data[decoders].T))

    plt.figure()
    plt.plot(sim.trange(), sim.data[p], lw=5, alpha=0.5)
    plt.plot(sim.trange(), desired - error, linestyle='--', lw=5, alpha=0.5)
Ejemplo n.º 2
0
def build_linear_system(model, conn, rng):
    eval_points = get_eval_points(model, conn, rng)
    activities = get_activities(model, conn.pre_obj, eval_points)
    if np.count_nonzero(activities) == 0:
        raise RuntimeError(
            "Building %s: 'activites' matrix is all zero for %s. "
            "This is because no evaluation points fall in the firing "
            "ranges of any neurons." % (conn, conn.pre_obj))

    targets = get_targets(model, conn, eval_points)
    return eval_points, activities, targets
Ejemplo n.º 3
0
def build_linear_system(model, conn, rng):
    eval_points = get_eval_points(model, conn, rng)
    activities = get_activities(model, conn.pre_obj, eval_points)
    if np.count_nonzero(activities) == 0:
        raise RuntimeError(
            "Building %s: 'activites' matrix is all zero for %s. "
            "This is because no evaluation points fall in the firing "
            "ranges of any neurons." % (conn, conn.pre_obj))

    targets = get_targets(model, conn, eval_points)
    return eval_points, activities, targets
Ejemplo n.º 4
0
def tuning_curves(ens, sim, inputs=None):
    """Calculates the tuning curves of an ensemble.

    That is the neuron responses in dependence of the vector represented by the
    ensemble.

    For 1-dimensional ensembles, the unpacked return value of this function
    can be passed directly to ``matplotlib.pyplot.plot``.

    Parameters
    ----------
    ens : nengo.Ensemble
        Ensemble to calculate the tuning curves of.
    sim : nengo.Simulator
        Simulator providing information about the built ensemble. (An unbuilt
        ensemble does not have tuning curves assigned to it.)
    inputs : sequence of ndarray, optional
        The inputs at which the tuning curves will be evaluated. For each of
        the ``D`` ensemble dimensions one array of dimensionality ``D`` is needed.
        The output of :func:`numpy.meshgrid` with ``indexing='ij'`` is in the
        right format.

    Returns
    -------
    inputs : sequence of ndarray
        The passed or auto-generated ``inputs``.
    activities : ndarray
        The activities of the individual neurons given the ``inputs``.
        For ensembles with 1 dimension, the rows correspond to the ``inputs``
        and the columns to individual neurons.
        For ensembles with > 1 dimension, the last dimension enumerates the
        neurons, the remaining dimensions map to ``inputs``.

    See Also
    --------
    response_curves
    """
    # note: imported here to avoid circular imports
    from nengo.builder.ensemble import (  # pylint: disable=import-outside-toplevel
        get_activities, )

    if inputs is None:
        inputs = np.linspace(-ens.radius, ens.radius)
        if ens.dimensions > 1:
            inputs = npext.meshgrid_nd(*(ens.dimensions * [inputs]))
        else:
            inputs = [inputs]
        inputs = np.asarray(inputs).T
    else:
        inputs = np.asarray(inputs)

    eval_points = inputs.reshape((-1, ens.dimensions))
    activities = get_activities(sim.data[ens], ens, eval_points)
    return inputs, activities.reshape(inputs.shape[:-1] + (-1, ))
Ejemplo n.º 5
0
def build_linear_system(model, conn, rng):
    eval_points = get_eval_points(model, conn, rng)
    ens = conn.pre_obj
    activities = get_activities(model.params[ens], ens, eval_points)
    if np.count_nonzero(activities) == 0:
        raise BuildError(
            "Building %s: 'activites' matrix is all zero for %s. "
            "This is because no evaluation points fall in the firing "
            "ranges of any neurons." % (conn, conn.pre_obj))

    targets = get_targets(conn, eval_points, dtype=rc.float_dtype)
    return eval_points, activities, targets
Ejemplo n.º 6
0
def tuning_curves(ens, sim, inputs=None):
    """Calculates the tuning curves of an ensemble.

    That is the neuron responses in dependence of the vector represented by the
    ensemble.

    For 1-dimensional ensembles, the unpacked return value of this function
    can be passed directly to :func:`matplotlib.pyplot.plot`.

    Parameters
    ----------
    ens : nengo.Ensemble
        Ensemble to calculate the tuning curves of.
    sim : nengo.Simulator
        Simulator providing information about the built ensemble. (An unbuilt
        ensemble does not have tuning curves assigned to it.)
    inputs : sequence of ndarray, optional
        The inputs at which the tuning curves will be evaluated. For each of
        the `D` ensemble dimensions one array of dimensionality `D` is needed.
        The output of :func:`numpy.meshgrid` with ``indexing='ij'`` is in the
        right format.

    Returns
    -------
    inputs : sequence of ndarray
        The passed or auto-generated `inputs`.
    activities : ndarray
        The activities of the individual neurons given the `inputs`.
        For ensembles with 1 dimension, the rows correspond to the `inputs`
        and the columns to individual neurons.
        For ensembles with > 1 dimension, the last dimension enumerates the
        neurons, the remaining dimensions map to `inputs`.

    See Also
    --------
    response_curves
    """
    from nengo.builder.ensemble import get_activities

    if inputs is None:
        inputs = np.linspace(-ens.radius, ens.radius)
        if ens.dimensions > 1:
            inputs = npext.meshgrid_nd(*(ens.dimensions * [inputs]))
        else:
            inputs = [inputs]
        inputs = np.asarray(inputs).T
    else:
        inputs = np.asarray(inputs)

    eval_points = inputs.reshape((-1, ens.dimensions))
    activities = get_activities(sim.data[ens], ens, eval_points)
    return inputs, activities.reshape(inputs.shape[:-1] + (-1,))
Ejemplo n.º 7
0
def build_linear_system(model, conn, rng):
    """Get all arrays needed to compute decoders."""
    eval_points = get_eval_points(model, conn, rng)
    ens = conn.pre_obj
    activities = get_activities(model.params[ens], ens, eval_points)
    if np.count_nonzero(activities) == 0:
        raise BuildError(
            f"Building {conn}: 'activities' matrix is all zero for {conn.pre_obj}. "
            "This is because no evaluation points fall in the firing "
            "ranges of any neurons.")

    targets = get_targets(conn, eval_points, dtype=rc.float_dtype)
    return eval_points, activities, targets
Ejemplo n.º 8
0
def eval_point_decoding(conn, sim, eval_points=None):
    """Get the targets and actual decoded values for a set of eval points.

    This function evaluates the static decoding (i.e. using the neuron type's
    ``rates`` function) of a connection for a given set of evaluation points.

    Parameters
    ----------
    conn : Connection
        The Connection to evaluate the decoding of.
    sim : Simulator
        A Nengo simulator storing the built connection.
    eval_points : array_like (N, E) (optional)
        An N x E array of evaluation points to evaluate the decoding for, where
        N is the number of points and E is the dimensionality of the input
        ensemble (i.e. ``conn.size_in``). If None (default), use the connection's
        training evaluation points.

    Returns
    -------
    eval_points : ndarray (N, E)
        A shallow copy of the evaluation points used. E is the dimensionality
        of the connection input ensemble (i.e. ``conn.size_in``).
    targets : ndarray (N, D)
        The target function value at each evaluation point.
    decoded : ndarray (N, D)
        The decoded function value at each evaluation point.
    """
    # pylint: disable=import-outside-toplevel
    # note: these are imported here to avoid circular imports
    from nengo import rc
    from nengo.builder.ensemble import get_activities
    from nengo.builder.connection import get_targets

    dtype = rc.float_dtype

    if eval_points is None:
        eval_points = sim.data[conn].eval_points
    else:
        eval_points = np.asarray(eval_points, dtype=dtype)

    ens = conn.pre_obj
    weights = sim.data[conn].weights
    activities = get_activities(sim.data[ens], ens, eval_points)
    decoded = np.dot(activities, weights.T)
    targets = get_targets(conn, eval_points, dtype=dtype)
    return eval_points, targets, decoded
Ejemplo n.º 9
0
def eval_point_decoding(conn, sim, eval_points=None):
    """Get the targets and actual decoded values for a set of eval points.

    This function evaluates the static decoding (i.e. using the neuron type's
    `rates` function) of a connection for a given set of evaluation points.

    Parameters
    ----------
    conn : Connection
        The Connection to evaluate the decoding of.
    sim : Simulator
        A Nengo simulator storing the built connection.
    eval_points : array_like (N, E) (optional)
        An N x E array of evaluation points to evaluate the decoding for, where
        N is the number of points and E is the dimensionality of the input
        ensemble (i.e. `conn.size_in`). If None (default), use the connection's
        training evaluation points.

    Returns
    -------
    eval_points : ndarray (N, E)
        A shallow copy of the evaluation points used. E is the dimensionality
        of the connection input ensemble (i.e. `conn.size_in`).
    targets : ndarray (N, D)
        The target function value at each evaluation point.
    decoded : ndarray (N, D)
        The decoded function value at each evaluation point.
    """
    from nengo.builder.ensemble import get_activities
    from nengo.builder.connection import get_targets

    if eval_points is None:
        eval_points = sim.data[conn].eval_points
    else:
        eval_points = np.asarray(eval_points)

    decoders = sim.data[conn].decoders
    if decoders is None:
        raise ValueError("Connection must have decoders")

    activities = get_activities(sim.model, conn.pre_obj, eval_points)
    decoded = np.dot(activities, decoders.T)
    targets = get_targets(sim.model, conn, eval_points)
    return eval_points, targets, decoded
Ejemplo n.º 10
0
def eval_point_decoding(conn, sim, eval_points=None):
    """Get the targets and actual decoded values for a set of eval points.

    This function evaluates the static decoding (i.e. using the neuron type's
    `rates` function) of a connection for a given set of evaluation points.

    Parameters
    ----------
    conn : Connection
        The Connection to evaluate the decoding of.
    sim : Simulator
        A Nengo simulator storing the built connection.
    eval_points : array_like (N, E) (optional)
        An N x E array of evaluation points to evaluate the decoding for, where
        N is the number of points and E is the dimensionality of the input
        ensemble (i.e. `conn.size_in`). If None (default), use the connection's
        training evaluation points.

    Returns
    -------
    eval_points : ndarray (N, E)
        A shallow copy of the evaluation points used. E is the dimensionality
        of the connection input ensemble (i.e. `conn.size_in`).
    targets : ndarray (N, D)
        The target function value at each evaluation point.
    decoded : ndarray (N, D)
        The decoded function value at each evaluation point.
    """
    from nengo.builder.ensemble import get_activities
    from nengo.builder.connection import get_targets

    if eval_points is None:
        eval_points = sim.data[conn].eval_points
    else:
        eval_points = np.asarray(eval_points)

    decoders = sim.data[conn].decoders
    if decoders is None:
        raise ValueError("Connection must have decoders")

    activities = get_activities(sim.model, conn.pre_obj, eval_points)
    decoded = np.dot(activities, decoders.T)
    targets = get_targets(sim.model, conn, eval_points)
    return eval_points, targets, decoded
Ejemplo n.º 11
0
def _test_rls_network(
        Simulator,
        seed,
        plt,
        tols,
        dims=1,
        lrate=0.01,
        neuron_type=nengo.LIFRate(),
        tau=None,
        t_train=0.5,
        t_test=0.25,
):
    # Input is a scalar sinusoid with given frequency
    n_neurons = 100
    freq = 5

    # Learn a linear transformation within t_train seconds
    transform = np.random.RandomState(seed=seed).randn(dims, 1)
    lr = RLS(learning_rate=lrate, pre_synapse=tau)

    with nengo.Network(seed=seed) as model:
        u = nengo.Node(output=lambda t: np.sin(freq * 2 * np.pi * t))
        x = nengo.Ensemble(n_neurons, 1, neuron_type=neuron_type)
        y = nengo.Node(size_in=dims)
        y_on = nengo.Node(size_in=dims)
        y_off = nengo.Node(size_in=dims)

        e = nengo.Node(size_in=dims,
                       output=lambda t, e: e
                       if t < t_train else np.zeros_like(e))

        nengo.Connection(u, y, synapse=None, transform=transform)
        nengo.Connection(u, x, synapse=None)
        conn_on = nengo.Connection(
            x,
            y_on,
            synapse=None,
            learning_rule_type=lr,
            function=lambda _: np.zeros(dims),
        )
        nengo.Connection(y, e, synapse=None, transform=-1)
        nengo.Connection(y_on, e, synapse=None)
        nengo.Connection(e, conn_on.learning_rule, synapse=tau)

        nengo.Connection(x, y_off, synapse=None, transform=transform)

        p_y = nengo.Probe(y, synapse=tau)
        p_y_on = nengo.Probe(y_on, synapse=tau)
        p_y_off = nengo.Probe(y_off, synapse=tau)
        p_inv_gamma = nengo.Probe(conn_on.learning_rule, "inv_gamma")

    with Simulator(model) as sim:
        sim.run(t_train + t_test)

    plt.plot(sim.trange(), sim.data[p_y_off], "k")
    plt.plot(sim.trange(), sim.data[p_y_on])

    # Check _descstr
    ops = [op for op in sim.model.operators if isinstance(op, SimRLS)]
    assert len(ops) == 1
    assert str(ops[0]).startswith("SimRLS")

    test = sim.trange() >= t_train

    on_versus_off = nrmse(sim.data[p_y_on][test], sim.data[p_y_off][test])
    on_versus_ideal = nrmse(sim.data[p_y_on][test], sim.data[p_y][test])
    off_versus_ideal = nrmse(sim.data[p_y_off][test], sim.data[p_y][test])

    A = get_activities(sim.data[x], x, np.linspace(-1, 1, 1000)[:, None])
    gamma_off = A.T.dot(A) + np.eye(n_neurons) / lr.learning_rate
    gamma_on = np.linalg.inv(sim.data[p_inv_gamma][-1])

    gamma_off /= np.linalg.norm(gamma_off)
    gamma_on /= np.linalg.norm(gamma_on)
    gamma_diff = nrmse(gamma_on, gamma_off)

    print()
    print(on_versus_off, on_versus_ideal, off_versus_ideal, gamma_diff)
    print()
    assert on_versus_off < tols[0]
    assert on_versus_ideal < tols[1]
    assert off_versus_ideal < tols[2]
    assert gamma_diff < tols[3]
Ejemplo n.º 12
0
def build_bias(model, bioensemble, biases, method, bias_gain=5e-5):
    rng = np.random.RandomState(bioensemble.seed)
    neurons_lif = 100
    neurons_bio = bioensemble.n_neurons
    tau = 0.01

    lif = nengo.Ensemble(
            neuron_type=nengo.LIF(),
            dimensions=1,
            n_neurons=neurons_lif,
            seed=bioensemble.seed,
            add_to_container=False)
    model.seeds[lif] = bioensemble.seed  # seeds normally set early in builder
    model.build(lif)  # add to the model
    model.add_op(Copy(Signal(0), model.sig[lif]['in'], inc=True))  # connect input(t)=1
    A = get_activities(model.params[lif],  # grab tuning curve activities
        lif,
        model.params[lif].eval_points)

    # Desired output function Y -- just repeat "bias" m times
    Y = np.tile(biases, (A.shape[0], 1))
    bias_decoders = nengo.solvers.LstsqL2()(A, Y)[0]

    # initialize synaptic locations
    syn_loc = get_synaptic_locations(
        rng,
        neurons_lif,
        neurons_bio,
        n_syn=1)
    syn_weights = np.zeros((
        neurons_bio,
        neurons_lif,
        syn_loc.shape[2]))
    if method == 'weights':
        for b in range(syn_weights.shape[0]):
            syn_weights[b] = rng.uniform(np.max(biases), np.min(biases), size=syn_weights[b].shape)
    if method == 'weights_fixed':
        for b in range(syn_weights.shape[0]):
            syn_weights[b] = bias_gain * biases[b]**5 * np.ones(syn_weights[b].shape)

    # unit test that synapse and weight arrays are compatible shapes
    if not syn_loc.shape[:-1] == bias_decoders.T.shape:
        raise BuildError("Shape mismatch: syn_loc=%s, bias_decoders=%s"
                         % (syn_loc.shape[:-1], bias_decoders))

    # add synapses to the bioneurons with weights = bias_decoders
    neurons = model.params[bioensemble.neurons]
    for j, bahl in enumerate(neurons):
        assert isinstance(bahl, Bahl)
        loc = syn_loc[j]
        bahl.synapses[lif] = np.empty(
            (loc.shape[0], loc.shape[1]), dtype=object)
        for pre in range(loc.shape[0]):
            for syn in range(loc.shape[1]):
                # section = bahl.cell.tuft(loc[pre, syn])
                section = bahl.cell.apical(loc[pre, syn])
                # w_ij = np.dot(decoders[pre], gain * encoder)
                if method == 'decode':
                    syn_weights[j, pre, syn] = bias_decoders[pre, j]
                w_ij = syn_weights[j, pre, syn]
                synapse = ExpSyn(section, w_ij, tau, loc[pre, syn])
                bahl.synapses[lif][pre][syn] = synapse
    neuron.init()

    model.add_op(TransmitSpikes(
        lif, bioensemble, None, neurons,
        model.sig[lif]['out'], states=[model.time]))
Ejemplo n.º 13
0
def transform_ensemble(
        ens,
        conn_ins,
        sim,
        e_rev_E=4.33,  # equiv. to 0mV for v_rest=-65mV, v_th=-50mV
        e_rev_I=-0.33,  # equiv. to -70mV
        use_linear_avg_pot=False,
        use_conductance_synapses=True,
        use_factorised_weights=False,
        use_jbias=False):
    """
    Creates an equivalent conductance based ensemble for the given input
    ensemble. Returns the node corresponding to the ensemble or None if the
    ensemble cannot be transformed. As a second return value returns a list
    containing the target dimensionalities of the newly created node for each
    dimension.

    ens: Ensemble that should be converted.
    conn_ins: list of input connections to that ensemble.
    conn_outs: list of output connections to that ensemble.
    sim: Simulation object that is used to fetch the decoders and encoders for
    the network.
    use_linear_avg_pot: if True, uses the linear average membrane potential
    estimate.
    use_conductance_synapses: if True, implements "normal" non-conductance based
    synapses. This is only useful for testing purposes.
    use_factorised_weights: if True, factorises the internal weight matrix into
    artificial encoders and decoders.
    use_jbias: if True, uses an external current source for the bias,
    otherwise decodes the bias from the pre-synaptic population.
    """

    from nengo.builder.ensemble import get_activities

    # Make sure the ensemble this transformation operates on has either the
    # neuron type LIF or LIFRate. Fetch gains, biases, and encoders from the
    # ensemble.
    if not isinstance(ens.neuron_type, nengo.neurons.LIF):
        return None, None

    # Abort if the ensemble has no input.
    if len(conn_ins) == 0:
        return None, None

    # Abort if the user requested the ensemble not to be transformed
    if hasattr(
            ens,
            'use_conductance_synapses') and not ens.use_conductance_synapses:
        return None, None

    n_neurons = ens.n_neurons
    encoder = sim.data[ens].encoders
    bias = sim.data[ens].bias
    gain = sim.data[ens].gain

    # Iterate over all input connections and calculate the total number of
    # neurons feeding into the node. Make sure the input connections are all
    # Ensemble objects. Fetch the decoders for the individual connections.
    n_dims_in = 0
    decoders = [None] * len(conn_ins)
    activities = [None] * len(conn_ins)
    encoders = [None] * len(conn_ins)
    connectivity = [None] * len(conn_ins)
    direct = [False] * len(conn_ins)
    for i, conn_in in enumerate(conn_ins):
        pre_obj = conn_in.pre_obj
        post_obj = conn_in.post_obj

        # Fetch the decoders and the number of neurons/dimensions in the pre-
        # ensemble
        if isinstance(pre_obj, nengo.Ensemble):
            decoders[i] = sim.data[conn_in].weights
            n_dims = pre_obj.n_neurons
        elif isinstance(pre_obj, nengo.ensemble.Neurons):
            pre_obj = pre_obj.ensemble
            n_dims = pre_obj.n_neurons
            W = sim.data[conn_in].weights
            if (np.ndim(W) == 0):
                W = np.eye(n_dims) * W
            elif (np.ndim(W) == 1):
                W = np.diag(W)
            out_dim, in_dim = W.shape
            decoders[i] = np.zeros((out_dim, n_dims))
            decoders[i][:, conn_in.pre_slice] = W
        elif isinstance(pre_obj, nengo.Node):
            n_dims = pre_obj.size_out
            decoders[i] = sim.data[conn_in].weights
            if (np.ndim(decoders[i]) == 0):
                decoders[i] = (
                    np.eye(n_dims) * decoders[i])[conn_in.pre_slice, :]
            direct[i] = True

        # Fetch the activities required for bias decoding
        if not use_jbias:
            if isinstance(pre_obj, nengo.Ensemble):
                activities[i] = get_activities(sim.data[pre_obj], pre_obj,
                                               sim.data[pre_obj].eval_points)
            elif isinstance(pre_obj, nengo.Node):
                activities[i] = np.zeros((1, pre_obj.size_out))

        # Apply the post-slice (pre-slice is already included in the decoder),
        # special treatment required for ".neurons" connections
        if isinstance(post_obj, nengo.ensemble.Neurons):
            encoders[i] = (
                np.eye(n_neurons) / gain.reshape(-1, 1))[:, conn_in.post_slice]
        else:
            encoders[i] = encoder[:, conn_in.post_slice]

        # Scale the encoders by the radius
        encoders[i] = encoders[i] / ens.radius

        connectivity[i] = slice(n_dims_in, n_dims_in + n_dims)
        n_dims_in += n_dims

    # Create the IfCondExp instance
    model = IfCondExp(
        ens.n_neurons,
        tau_rc=ens.neuron_type.tau_rc,
        tau_ref=ens.neuron_type.tau_ref,
        e_rev_E=e_rev_E,
        e_rev_I=e_rev_I,
        use_linear_avg_pot=use_linear_avg_pot,
        use_conductance_synapses=use_conductance_synapses)

    # Assemble the simulator node
    node = nengo.Node(
        size_out=n_neurons,
        size_in=n_dims_in,
        output=sim_if_cond_exp(
            decoders=decoders,
            activities=activities,
            encoders=encoders,
            direct=direct,
            bias=bias,
            gain=gain,
            model=model,
            dt=sim.dt,
            use_jbias=use_jbias,
            use_factorised_weights=use_factorised_weights),
        label=ens.label)

    return node, connectivity