Skip to content

Commit

Permalink
Major refactoring to more closely conform to Python style best practi…
Browse files Browse the repository at this point in the history
…ces.
  • Loading branch information
Dan Saunders committed Jun 11, 2018
1 parent 7895b44 commit e48cbdd
Show file tree
Hide file tree
Showing 13 changed files with 482 additions and 269 deletions.
152 changes: 105 additions & 47 deletions bindsnet/analysis/plotting.py

Large diffs are not rendered by default.

35 changes: 22 additions & 13 deletions bindsnet/analysis/visualization.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,9 @@ def plot_weights_movie(ws, sample_every=1):
Inputs:
| :code:`ws` (:code:`numpy.array`): Numpy array of shape :code:`[N_examples, source, target, time]`
| :code:`sample_every` (:code:`int`): Sub-sample using this parameter. For example if :code:`time` is
too large (500), set this parameter to 20 to sample weights
every 20 iterations.
| :code:`ws` (:code:`numpy.array`): Numpy array
of shape :code:`[n_examples, source, target, time]`
| :code:`sample_every` (:code:`int`): Sub-sample using this parameter.
"""
weights = []

Expand Down Expand Up @@ -49,10 +48,13 @@ def plot_spike_trains_for_example(spikes, n_ex=None, top_k=None, indices=None):
Inputs:
| :code:`spikes` (:code:`torch.Tensor (n_examples, n_neurons, time)`): Spiking train data for a population of neurons for one example.
| :code:`n_ex` (:code:`int`): Allows user to pick which example to plot spikes for. Must be >= 0.
| :code:`spikes` (:code:`torch.Tensor (n_examples, n_neurons, time)`):
Spiking train data for a population of neurons for one example.
| :code:`n_ex` (:code:`int`): Allows user to pick
which example to plot spikes for. Must be >= 0.
| :code:`top_k` (:code:`int`): Plot k neurons that spiked the most for n_ex example.
| :code:`indices` (:code:`list(int)`): Plot specific neurons' spiking activity instead of top_k. Meant to replace top_k.
| :code:`indices` (:code:`list(int)`): Plot specific neurons'
spiking activity instead of top_k. Meant to replace top_k.
'''

assert (n_ex is not None and n_ex >= 0 and n_ex < spikes.shape[0])
Expand Down Expand Up @@ -84,11 +86,16 @@ def plot_voltage(voltage, n_ex=0, n_neuron=0, time=None, threshold=None):
Inputs:
| :code:`voltage` (:code:`torch.Tensor` or :code:`numpy.array`): Tensor or array of shape :code:`[n_examples, n_neurons, time]`.
| :code:`n_ex` (:code:`int`): Allows user to pick which example to plot voltage for.
| :code:`n_neuron` (:code:`int`): Neuron index for which to plot voltages for.
| :code:`time` (:code:`tuple(int)`): Plot spiking activity of neurons between the given range of time.
| :code:`threshold` (:code:`float`): Neuron spiking threshold. Will be shown on the plot.
| :code:`voltage` (:code:`torch.Tensor` or :code:`numpy.array`):
Tensor or array of shape :code:`[n_examples, n_neurons, time]`.
| :code:`n_ex` (:code:`int`): Allows user
to pick which example to plot voltage for.
| :code:`n_neuron` (:code:`int`): Neuron
index for which to plot voltages for.
| :code:`time` (:code:`tuple(int)`): Plot spiking
activity of neurons between the given range of time.
| :code:`threshold` (:code:`float`): Neuron
spiking threshold. Will be shown on the plot.
'''

assert (n_ex >= 0 and n_neuron >= 0)
Expand All @@ -105,7 +112,9 @@ def plot_voltage(voltage, n_ex=0, n_neuron=0, time=None, threshold=None):

plt.figure()
plt.plot(voltage[n_ex, n_neuron, timer])
plt.xlabel('Simulation Time'); plt.ylabel('Voltage'); plt.title('Membrane voltage of neuron %d for example %d'%(n_neuron, n_ex+1))
plt.xlabel('Simulation Time')
plt.ylabel('Voltage')
plt.title('Membrane voltage of neuron %d for example %d' % (n_neuron, n_ex + 1))
locs, labels = plt.xticks()
locs = range(int(locs[1]), int(locs[-1]), 10)
plt.xticks(locs, time_ticks)
Expand Down
139 changes: 86 additions & 53 deletions bindsnet/datasets/__init__.py

Large diffs are not rendered by default.

11 changes: 4 additions & 7 deletions bindsnet/datasets/preprocess.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,12 @@ def gray_scale(im):
| :code:`im` (:code:`numpy.array`): Grayscaled image
'''
im = cv2.cvtColor(im, cv2.COLOR_RGB2GRAY)
return im
return cv2.cvtColor(im, cv2.COLOR_RGB2GRAY)


def crop(im, x1, x2, y1, y2):
return im[x1:x2, y1:y2, :]


def binary_image(im):
'''
Expand All @@ -32,7 +31,7 @@ def binary_image(im):
| :code:`im` (:code:`numpy.array`): Black and white image.
'''
ret, im = cv2.threshold(im, 0, 1, cv2.THRESH_BINARY)
_, im = cv2.threshold(im, 0, 1, cv2.THRESH_BINARY)
return im


Expand All @@ -50,6 +49,4 @@ def subsample(im, x, y):
| :code:`im` (:code:`numpy.array`): Rescaled image.
'''
im = cv2.resize(im, (x, y))
return im

return cv2.resize(im, (x, y))
53 changes: 36 additions & 17 deletions bindsnet/encoding/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@

def bernoulli(datum, time=None, **kwargs):
'''
Generates Bernoulli-distributed spike trains based on input intensity. Inputs must be non-negative. Spikes correspond to successful Bernoulli trials, with success probability equal to (normalized in [0, 1]) input value.
Generates Bernoulli-distributed spike trains based on input intensity.
Inputs must be non-negative. Spikes correspond to successful Bernoulli
trials, with success probability equal to (normalized in [0, 1]) input value.
Inputs:
Expand All @@ -13,11 +15,13 @@ def bernoulli(datum, time=None, **kwargs):
Keyword arguments:
| :code:`max_prob` (:code:`float`): Maximum probability of spike per Bernoulli trial.
| :code:`max_prob` (:code:`float`): Maximum
probability of spike per Bernoulli trial.
Returns:
| (:code:`torch.Tensor`): Tensor of shape :code:`[time, n_1, ..., n_k]` of Bernoulli-distributed spikes.
| (:code:`torch.Tensor`): Tensor of shape :code:`[time, n_1, ..., n_k]`
of Bernoulli-distributed spikes.
'''
# Setting kwargs.
max_prob = kwargs.get('max_prob', 1.0)
Expand Down Expand Up @@ -48,7 +52,8 @@ def bernoulli_loader(data, time=None, **kwargs):
Inputs:
| :code:`data` (:code:`torch.Tensor` or iterable of :code:`torch.Tensor`s): Tensor of shape :code:`[n_samples, n_1, ..., n_k]`.
| :code:`data` (:code:`torch.Tensor` or iterable of :code:`torch.Tensor`s):
Tensor of shape :code:`[n_samples, n_1, ..., n_k]`.
| :code:`time` (:code:`int`): Length of Bernoulli spike train per input variable.
Keyword arguments:
Expand All @@ -57,7 +62,8 @@ def bernoulli_loader(data, time=None, **kwargs):
Yields:
| (:code:`torch.Tensor`): Tensors of shape :code:`[time, n_1, ..., n_k]` of Bernoulli-distributed spikes.
| (:code:`torch.Tensor`): Tensors of shape :code:`[time, n_1, ..., n_k]`
of Bernoulli-distributed spikes.
'''

# Setting kwargs.
Expand All @@ -68,7 +74,8 @@ def bernoulli_loader(data, time=None, **kwargs):

def poisson(datum, time, **kwargs):
'''
Generates Poisson-distributed spike trains based on input intensity. Inputs must be non-negative.
Generates Poisson-distributed spike trains based
on input intensity. Inputs must be non-negative.
Inputs:
Expand All @@ -77,7 +84,8 @@ def poisson(datum, time, **kwargs):
Returns:
| (:code:`torch.Tensor`): Tensor of shape :code:`[time, n_1, ..., n_k]` of Poisson-distributed spikes.
| (:code:`torch.Tensor`): Tensor of shape :code:`[time, n_1, ..., n_k]`
of Poisson-distributed spikes.
'''
datum = np.copy(datum)
shape, size = datum.shape, datum.size
Expand Down Expand Up @@ -107,29 +115,36 @@ def poisson_loader(data, time, **kwargs):
Inputs:
| :code:`data` (:code:`torch.Tensor` or iterable of :code:`torch.Tensor`s): Tensor of shape :code:`[n_samples, n_1, ..., n_k]`
| :code:`data` (:code:`torch.Tensor` or iterable of :code:`torch.Tensor`s):
Tensor of shape :code:`[n_samples, n_1, ..., n_k]`
| :code:`time` (:code:`int`): Length of Poisson spike train per input variable.
Yields:
| (:code:`torch.Tensor`): Tensors of shape :code:`[time, n_1, ..., n_k]` of Poisson-distributed spikes.
| (:code:`torch.Tensor`): Tensors of shape :code:`[time, n_1, ..., n_k]`
of Poisson-distributed spikes.
'''
for i in range(len(data)):
yield poisson(data[i], time) # Encode datum as Poisson spike trains.


def rank_order(datum, time, **kwargs):
'''
Encodes data via a rank order coding-like representation. One spike per neuron, temporally ordered by decreasing intensity. Inputs must be non-negative.
Encodes data via a rank order coding-like representation. One
spike per neuron, temporally ordered by decreasing intensity.
Inputs must be non-negative.
Inputs:
| :code:`data` (:code:`torch.Tensor`): Tensor of shape :code:`[n_samples, n_1, ..., n_k]`
| :code:`time` (:code:`int`): Length of Poisson spike train per input variable.
| :code:`data` (:code:`torch.Tensor`): Tensor
of shape :code:`[n_samples, n_1, ..., n_k]`
| :code:`time` (:code:`int`): Length of rank
order-encoded spike train per input variable.
Returns:
| (:code:`torch.Tensor`): Tensor of shape :code:`[time, n_1, ..., n_k]` of Poisson-distributed spikes.
| (:code:`torch.Tensor`): Tensor of shape
:code:`[time, n_1, ..., n_k]` of rank order-encoded spikes.
'''
datum = np.copy(datum)
shape, size = datum.shape, datum.size
Expand All @@ -155,16 +170,20 @@ def rank_order(datum, time, **kwargs):

def rank_order_loader(data, time, **kwargs):
'''
Lazily invokes :code:`bindsnet.encoding.rank_order` to iteratively encode a sequence of data.
Lazily invokes :code:`bindsnet.encoding.rank_order`
to iteratively encode a sequence of data.
Inputs:
| :code:`data` (:code:`torch.Tensor` or iterable of :code:`torch.Tensor`s): Tensor of shape :code:`[n_samples, n_1, ..., n_k]`
| :code:`time` (:code:`int`): Length of Poisson spike train per input variable.
| :code:`data` (:code:`torch.Tensor` or iterable of :code:`torch.Tensor`s):
Tensor of shape :code:`[n_samples, n_1, ..., n_k]`
| :code:`time` (:code:`int`): Length of rank
order-encoded spike train per input variable.
Yields:
| (:code:`torch.Tensor`): Tensors of shape :code:`[time, n_1, ..., n_k]` of rank order-encoded spikes.
| (:code:`torch.Tensor`): Tensors of shape :code:`[time, n_1, ..., n_k]`
of rank order-encoded spikes.
'''
for i in range(len(data)):
yield rank_order(data[i], time) # Encode datum as rank order-encoded spike trains.
12 changes: 7 additions & 5 deletions bindsnet/environment/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ def __init__(self, dataset, train=True, time=350, **kwargs):
self.intensity = kwargs.get('intensity', 1)
self.max_prob = kwargs.get('max_prob', 1)

assert self.max_prob > 0 and self.max_prob <= 1, 'Maximum spiking probability must be in (0, 1].'
assert self.max_prob > 0 and self.max_prob <= 1, \
'Maximum spiking probability must be in (0, 1].'

if train:
self.data, self.labels = self.dataset.get_train()
Expand All @@ -50,14 +51,14 @@ def step(self, a=None):
Inputs:
| :code:`a` (:code:`None`): There is no interaction of the network with the MNIST dataset.
| :code:`a` (:code:`None`): There is no interaction of the network the dataset.
Returns:
| :code:`obs` (:code:`torch.Tensor`): Observation from the environment (spike train-encoded MNIST digit).
| :code:`obs` (:code:`torch.Tensor`): Observation from the environment.
| :code:`reward` (:code:`float`): Fixed to :code:`0`.
| :code:`done` (:code:`bool`): Fixed to :code:`False`.
| :code:`info` (:code:`dict`): Contains label of MNIST digit.
| :code:`info` (:code:`dict`): Contains label of data item.
'''
try:
# Attempt to fetch the next observation.
Expand Down Expand Up @@ -140,7 +141,8 @@ def __init__(self, name, **kwargs):
# Keyword arguments.
self.max_prob = kwargs.get('max_prob', 1)

assert self.max_prob > 0 and self.max_prob <= 1, 'Maximum spiking probability must be in (0, 1].'
assert self.max_prob > 0 and self.max_prob <= 1, \
'Maximum spiking probability must be in (0, 1].'

def step(self, a):
'''
Expand Down
39 changes: 26 additions & 13 deletions bindsnet/evaluation/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,21 @@ def assign_labels(spikes, labels, n_labels, rates=None, alpha=1.0):
Inputs:
| :code:`spikes` (:code:`torch.Tensor`): Binary tensor of shape :code:`(n_samples, time, n_neurons)` of a single layer's spiking activity.
| :code:`labels` (:code:`torch.Tensor`): Vector of shape :code:`(n_samples,)` with data labels corresponding to spiking activity.
| :code:`spikes` (:code:`torch.Tensor`): Binary tensor of shape
:code:`(n_samples, time, n_neurons)` of a single layer's spiking activity.
| :code:`labels` (:code:`torch.Tensor`): Vector of shape :code:`(n_samples,)`
with data labels corresponding to spiking activity.
| :code:`n_labels` (:code:`int`): The number of target labels in the data.
| :code:`rates` (:code:`torch.Tensor`): If passed, these represent spike rates from a previous :code:`assign_labels()` call.
| :code:`rates` (:code:`torch.Tensor`): If passed, these represent spike
rates from a previous :code:`assign_labels()` call.
| :code:`alpha` (:code:`float`): Rate of decay of label assignments.
Returns:
| (:code:`torch.Tensor`): Vector of shape :code:`(n_neurons,)` of neuron label assignments.
| (:code:`torch.Tensor`): Vector of shape :code:`(n_neurons, n_labels)` of proportions of firing activity per neuron, per data label.
| (:code:`torch.Tensor`): Vector of shape
:code:`(n_neurons,)` of neuron label assignments.
| (:code:`torch.Tensor`): Vector of shape :code:`(n_neurons, n_labels)`
of proportions of firing activity per neuron, per data label.
'''
n_neurons = spikes.size(2)

Expand Down Expand Up @@ -53,13 +58,16 @@ def all_activity(spikes, assignments, n_labels):
Inputs:
| :code:`spikes` (:code:`torch.Tensor`): Binary tensor of shape :code:`(n_samples, time, n_neurons)` of a layer's spiking activity.
| :code:`assignments` (:code:`torch.Tensor`): A vector of shape :code:`(n_neurons,)` of neuron label assignments.
| :code:`spikes` (:code:`torch.Tensor`): Binary tensor of shape
:code:`(n_samples, time, n_neurons)` of a layer's spiking activity.
| :code:`assignments` (:code:`torch.Tensor`): A vector of shape
:code:`(n_neurons,)` of neuron label assignments.
| :code:`n_labels` (:code:`int`): The number of target labels in the data.
Returns:
| (:code:`torch.Tensor`): Predictions tensor of shape :code:`(n_samples,)` resulting from the "all activity" classification scheme.
| (:code:`torch.Tensor`): Predictions tensor of shape :code:`(n_samples,)`
resulting from the "all activity" classification scheme.
'''
n_samples = spikes.size(0)

Expand Down Expand Up @@ -88,18 +96,23 @@ def all_activity(spikes, assignments, n_labels):

def proportion_weighting(spikes, assignments, proportions, n_labels):
'''
Classify data with the label with highest average spiking activity over all neurons, weighted by class-wise proportion..
Classify data with the label with highest average spiking
activity over all neurons, weighted by class-wise proportion.
Inputs:
| :code:`spikes` (:code:`torch.Tensor`): Binary tensor of shape :code:`(n_samples, time, n_neurons)` of a single layer's spiking activity.
| :code:`assignments` (:code:`torch.Tensor`): A vector of shape :code:`(n_neurons,)` of neuron label assignments.
| :code:`proportions` (torch.Tensor): A matrix of shape :code:`(n_neurons, n_labels)` giving the per-class proportions of neuron spiking activity.
| :code:`spikes` (:code:`torch.Tensor`): Binary tensor of shape
:code:`(n_samples, time, n_neurons)` of a single layer's spiking activity.
| :code:`assignments` (:code:`torch.Tensor`): A vector of shape
:code:`(n_neurons,)` of neuron label assignments.
| :code:`proportions` (torch.Tensor): A matrix of shape :code:`(n_neurons, n_labels)`
giving the per-class proportions of neuron spiking activity.
| :code:`n_labels` (:code:`int`): The number of target labels in the data.
Returns:
| (:code:`torch.Tensor`): Predictions tensor of shape :code:`(n_samples,)` resulting from the "proportion weighting" classification scheme.
| (:code:`torch.Tensor`): Predictions tensor of shapez:code:`(n_samples,)`
resulting from the "proportion weighting" classification scheme.
'''
n_samples = spikes.size(0)

Expand Down
Loading

0 comments on commit e48cbdd

Please sign in to comment.