# Source code for neurodynex3.ojas_rule.oja

"""
This file implements Oja's hebbian learning rule.

Relevant book chapters:
- http://neuronaldynamics.epfl.ch/online/Ch19.S2.html#SS1.p6
"""

# This file is part of the exercise code repository accompanying
# the book: Neuronal Dynamics (see http://neuronaldynamics.epfl.ch)
# located at http://github.com/EPFL-LCN/neuronaldynamics-exercises.

# This free software: you can redistribute it and/or modify it under
# Free Software Foundation. You should have received a copy of the
# GNU General Public License along with the repository. If not,

# Should you reuse and publish the code for your own purposes,
# please cite the book or point to the webpage http://neuronaldynamics.epfl.ch.

# Wulfram Gerstner, Werner M. Kistler, Richard Naud, and Liam Paninski.
# Neuronal Dynamics: From Single Neurons to Networks and Models of Cognition.
# Cambridge University Press, 2014.

import matplotlib.pyplot as plt
import numpy as np

[docs]def make_cloud(n=2000, ratio=1, angle=0):
"""Returns an oriented elliptic
gaussian cloud of 2D points

Args:
n (int, optional): number of points in the cloud
ratio (int, optional): (std along the short axis) /
(std along the long axis)
angle (int, optional): rotation angle [deg]

Returns:
numpy.ndarray: array of datapoints
"""

if ratio > 1.:
ratio = 1. / ratio

x = np.random.randn(n, 1)
y = ratio * np.random.randn(n, 1)
z = np.concatenate((x, y), 1)
radangle = (180. - angle) * np.pi / 180.
transfo = [
]
return np.dot(transfo, z.T).T

[docs]def learn(cloud, initial_angle=None, eta=0.005):
"""Run one batch of Oja's learning over
a cloud of datapoints.

Args:
cloud (numpy.ndarray): An N by 2 array of datapoints. You can
think of each of the two columns as the time series of firing rates of one presynaptic neuron.
initial_angle (float, optional): angle of initial
set of weights [deg]. If None, this is random.
eta (float, optional): learning rate

Returns:
numpy.ndarray: time course of the weight vector
"""

# get angle if not set
if initial_angle is None:
initial_angle = np.random.rand() * 360.
radangle = initial_angle * np.pi / 180.

wcourse = np.zeros((len(cloud), 2), float)
for i in range(0, len(cloud)):
wcourse[i] = w
y = np.dot(w, cloud[i])  # output: postsynaptic firing rate of a linear neuron.
# ojas rule (cloud[i] are the two presynaptic firing rates at time point i
w = w + eta * y * (cloud[i] - y * w)
return wcourse

[docs]def plot_oja_trace(data_cloud, weights_course):
"""
Plots the datapoints and the time series of the weights
Args:
data_cloud (numpy.ndarray): n by 2 data
weights_course (numpy.ndarray): n by 2 weights

Returns:

"""
plt.scatter(
data_cloud[:, 0],
data_cloud[:, 1],
marker=".",
facecolor="none",
edgecolor="#222222",
alpha=.2
)
plt.xlabel("x1")
plt.ylabel("x2")

# color time and plot with colorbar
time = np.arange(len(weights_course))
colors = plt.cm.cool(time / float(len(time)))
sm = plt.cm.ScalarMappable(
cmap=plt.cm.cool,
norm=plt.Normalize(vmin=0, vmax=len(data_cloud))
)
sm.set_array(time)
cb = plt.colorbar(sm)
cb.set_label("Iteration")
plt.scatter(
weights_course[:, 0],
weights_course[:, 1],
facecolor=colors,
edgecolor="none",
lw=2
)

# ensure rectangular plot
x_min = data_cloud[:, 0].min()
x_max = data_cloud[:, 0].max()
y_min = data_cloud[:, 1].min()
y_max = data_cloud[:, 1].max()
lims = [min(x_min, y_min), max(x_max, y_max)]
plt.xlim(lims)
plt.ylim(lims)
plt.show()

[docs]def run_oja(n=2000, ratio=1., angle=0., learning_rate=0.01, do_plot=True):
"""Generates a point cloud and runs Oja's learning
rule once. Optionally plots the result.

Args:
n (int, optional): number of points in the cloud
ratio (float, optional): (std along the short axis) /
(std along the long axis)
angle (float, optional): rotation angle [deg]
do_plot (bool, optional): plot the result
"""

cloud = make_cloud(n=n, ratio=ratio, angle=angle)
wcourse = learn(cloud, eta=learning_rate)

if do_plot:
plot_oja_trace(cloud, wcourse)
return wcourse, cloud

if __name__ == "__main__":
run_oja(n=2000, ratio=1.1, angle=30, learning_rate=0.2)