1 1
import numpy as np
2

3 1
from nengo import LIF
4 1
from nengo.builder.builder import Builder
5 1
from nengo.builder.neurons import SimNeurons
6 1
from nengo.neurons import NeuronType
7

8 1
__all__ = ['Unit', 'Tanh', 'init_lif']
9

10

11 1
def _sample_lif_state(sim, ens, x0, rng):
12
    """Sample the LIF's voltage/refractory assuming uniform within ISI.
13
    """
14 1
    lif = ens.neuron_type
15 1
    params = sim.model.params
16

17 1
    eval_points = x0[None, :]
18 1
    x = np.dot(eval_points, params[ens].encoders.T / ens.radius)[0]
19 1
    a = ens.neuron_type.rates(x, params[ens].gain, params[ens].bias)
20 1
    J = params[ens].gain * x + params[ens].bias
21

22
    # fast-forward to a random time within the ISI for any active neurons
23 1
    is_active = a > 0
24 1
    t_isi = np.zeros_like(a)
25 1
    t_isi[is_active] = rng.rand(np.count_nonzero(is_active)) / a[is_active]
26

27
    # work backwards through the LIF solution to find the corresponding voltage
28
    # since the refractory period is at the beginning of the ISI, we must
29
    # set that first, then subtract and use the remaining delta
30 1
    refractory_time = np.where(
31
        is_active & (t_isi < lif.tau_ref),
32
        sim.dt + (lif.tau_ref - t_isi), 0)  # dt immediately subtracted
33 1
    delta_t = (t_isi - lif.tau_ref).clip(0)
34 1
    voltage = -J * np.expm1(-delta_t / lif.tau_rc)
35

36
    # fast-forward to the steady-state for any subthreshold neurons
37 1
    subthreshold = ~is_active
38 1
    voltage[subthreshold] = J[subthreshold].clip(0)
39

40 1
    return voltage, refractory_time
41

42

43 1
def init_lif(sim, ens, x0=None, rng=None):
44
    """Initialize an ensemble of LIF Neurons to represent ``x0``.
45

46
    Must be called from within a simulator context block, and before
47
    the simulation (see example below).
48

49
    Parameters
50
    ----------
51
    sim : :class:`nengo.Simulator`
52
       The created simulator, from whose context the call is within.
53
    ens : :class:`nengo.Ensemble`
54
       The ensemble of LIF neurons to be initialized.
55
    x0 : ``(d,) array_like``, optional
56
       A ``d``-dimensional state-vector that the
57
       ensemble should be initialized to represent, where
58
       ``d = ens.dimensions``. Defaults to the zero vector.
59
    rng : :class:`numpy.random.RandomState` or ``None``, optional
60
        Random number generator state.
61

62
    Returns
63
    -------
64
    v : ``(n,) np.array``
65
       Array of initialized voltages, where ``n = ens.n_neurons``.
66
    r : ``(n,) np.array``
67
       Array of initialized refractory times, where ``n = ens.n_neurons``.
68

69
    Notes
70
    -----
71
    This will not initialize the synapses.
72

73
    Examples
74
    --------
75
    >>> import nengo
76
    >>> from nengolib import Network
77
    >>> from nengolib.neurons import init_lif
78
    >>>
79
    >>> with Network() as model:
80
    >>>      u = nengo.Node(0)
81
    >>>      x = nengo.Ensemble(100, 1)
82
    >>>      nengo.Connection(u, x)
83
    >>>      p_v = nengo.Probe(x.neurons, 'voltage')
84
    >>>
85
    >>> with nengo.Simulator(model, dt=1e-4) as sim:
86
    >>>      init_lif(sim, x)
87
    >>>      sim.run(0.01)
88
    >>>
89
    >>> import matplotlib.pyplot as plt
90
    >>> plt.title("Initialized LIF Voltage Traces")
91
    >>> plt.plot(1e3 * sim.trange(), sim.data[p_v])
92
    >>> plt.xlabel("Time (ms)")
93
    >>> plt.ylabel("Voltage (Unitless)")
94
    >>> plt.show()
95
    """
96

97 1
    if rng is None:
98 1
        rng = sim.rng
99

100 1
    if x0 is None:
101 1
        x0 = np.zeros(ens.dimensions)
102
    else:
103 1
        x0 = np.atleast_1d(x0)
104 1
        if x0.shape != (ens.dimensions,):
105 1
            raise ValueError(
106
                "x0 must be an array of length %d" % ens.dimensions)
107

108 1
    if not isinstance(ens.neuron_type, LIF):
109 1
        raise ValueError("ens.neuron_type=%r must be an instance of "
110
                         "nengo.LIF" % ens.neuron_type)
111

112 1
    vr = _sample_lif_state(sim, ens, x0, rng)
113

114
    # https://github.com/nengo/nengo/issues/1415
115 1
    signal = sim.model.sig[ens.neurons]
116 1
    sim.signals[signal['voltage']], sim.signals[signal['refractory_time']] = vr
117 1
    return vr
118

119

120 1
class Unit(NeuronType):
121
    """A neuron model with gain=1 and bias=0 on its input."""
122

123 1
    def rates(self, x, gain, bias):
124
        raise NotImplementedError("unit does not support decoding")
125

126 1
    def gain_bias(self, max_rates, intercepts):
127 1
        return np.ones_like(max_rates), np.zeros_like(max_rates)
128

129

130 1
class Tanh(Unit):
131
    """Common hyperbolic tangent neural nonlinearity."""
132

133 1
    def step_math(self, dt, J, output):
134 1
        output[...] = np.tanh(J)
135

136

137 1
@Builder.register(Unit)
138
def build_unit(model, unit, neurons):
139
    """Adds all unit neuron types to the nengo reference backend."""
140 1
    model.add_op(SimNeurons(neurons=unit,
141
                            J=model.sig[neurons]['in'],
142
                            output=model.sig[neurons]['out']))

Read our documentation on viewing source code .

Loading