diff --git a/.images/readme/fig_of_eight.png b/.images/readme/fig_of_eight.png
new file mode 100644
index 00000000..bde5ec10
Binary files /dev/null and b/.images/readme/fig_of_eight.png differ
diff --git a/.images/readme/theta_sequences.gif b/.images/readme/theta_sequences.gif
new file mode 100644
index 00000000..33ab70ba
Binary files /dev/null and b/.images/readme/theta_sequences.gif differ
diff --git a/.images/readme/trapezium.png b/.images/readme/trapezium.png
new file mode 100644
index 00000000..68db12f1
Binary files /dev/null and b/.images/readme/trapezium.png differ
diff --git a/.images/readme/wall_repel.png b/.images/readme/wall_repel.png
index 1a46bced..5e42e38e 100644
Binary files a/.images/readme/wall_repel.png and b/.images/readme/wall_repel.png differ
diff --git a/README.md b/README.md
index eefc0fa3..c0a1f09a 100644
--- a/README.md
+++ b/README.md
@@ -72,15 +72,17 @@ Here is a list of features loosely organised into three categories: those pertai
(i) the [`Environment`](#i-environment-features)
* [Adding walls](#walls)
+* [Polygon-shaped Environments](#polygon-shaped-environments)
+* [Holes](#holes)
* [Boundary conditions](#boundary-conditions)
* [1- or 2-dimensions](#1--or-2-dimensions)
-
(ii) the [`Agent`](#ii-agent-features)
* [Random motion](#random-motion-model)
* [Importing trajectories](#importing-trajectories)
* [Policy control](#policy-control)
* [Wall repelling](#wall-repelling)
+* [Advanced `Agent` classes](#advanced-agent-classes)
(iii) the [`Neurons`](#iii-neurons-features).
* [Cell types](#multiple-cell-types)
@@ -105,8 +107,28 @@ Here are some easy to make examples.
+#### Polygon-shaped `Environments`
+By default, `Environments` in RatInABox are square (or rectangular if `aspect != 1`). It is possible to create arbitrary environment shapes using the `"boundary"` parameter at initialisation:
+```python
+Env = Environment(params={'boundary':[[0,-0.2],[0,0.2],[1.5,0.5],[1.5,-0.5]]})
+```
+
+
+
+#### Holes
+One can add holes to the `Environment` using the `"holes"` parameter at initialisation
+```python
+Env = Environment(params={
+ 'aspect':1.8,
+ 'holes' : [[[0.2,0.2],[0.8,0.2],[0.8,0.8],[0.2,0.8]],
+ [[1,0.2],[1.6,0.2],[1.6,0.8],[1,0.8]]]
+})
+```
+
+
+
#### Boundary conditions
-Boundary conditions can be "periodic" or "solid". Place cells and the motion of the Agent will respect these boundaries accordingly.
+Boundary conditions (for default square/rectangular environments) can be "periodic" or "solid". Place cells and the motion of the Agent will respect these boundaries accordingly.
```python
Env = Environment(
params = {'boundary_conditions':'periodic'} #or 'solid' (default)
@@ -172,7 +194,13 @@ Under the random motion policy, walls in the environment mildly "repel" the `Age
Αgent.thigmotaxis = 0.8 #1 = high thigmotaxis (left plot), 0 = low (right)
```
-
+
+
+
+#### Advanced `Agent` classes
+One can make more advanced Agent classes, for example `ThetaSequenceAgent()` where the position "sweeps" (blue) over the position of an underlying true (regular) `Agent()` (purple), highly reminiscent of theta sequences observed when one decodes position from the hippocampal populaton code on sub-theta (10 Hz) timescales. This class can be found in the [`contribs`](./ratinabox/contribs/) directory.
+
+
### (iii) `Neurons` features
diff --git a/demos/decoding_position_example.ipynb b/demos/decoding_position_example.ipynb
index 38d2e1e8..8e97f6c4 100644
--- a/demos/decoding_position_example.ipynb
+++ b/demos/decoding_position_example.ipynb
@@ -24,7 +24,7 @@
"import ratinabox\n",
"from ratinabox.Environment import Environment\n",
"from ratinabox.Agent import Agent\n",
- "from ratinabox.Neurons import PlaceCells,GridCells,BoundaryVectorCells\n",
+ "from ratinabox.Neurons import PlaceCells, GridCells, BoundaryVectorCells\n",
"\n",
"import matplotlib\n",
"import matplotlib.pyplot as plt\n",
@@ -39,10 +39,11 @@
"metadata": {},
"outputs": [],
"source": [
- "#Leave this as False. \n",
- "#For paper/readme production I use a plotting library (tomplotlib) to format and save figures. Without this they will still show but not save. \n",
- "if False: \n",
+ "# Leave this as False.\n",
+ "# For paper/readme production I use a plotting library (tomplotlib) to format and save figures. Without this they will still show but not save.\n",
+ "if False:\n",
" import tomplotlib.tomplotlib as tpl\n",
+ "\n",
" tpl.figureDirectory = \"../figures/\"\n",
" tpl.setColorscheme(colorscheme=2)\n",
" save_plots = True\n",
@@ -65,43 +66,61 @@
"metadata": {},
"outputs": [],
"source": [
- "def train_decoder(Neurons,t_start=None,t_end=None):\n",
+ "def train_decoder(Neurons, t_start=None, t_end=None):\n",
" \"\"\"t_start and t_end allow you to pick the poritions of the saved data to train on.\"\"\"\n",
- " #Get training data\n",
- " t = np.array(Neurons.history['t'])\n",
- " if t_start is None: i_start = 0\n",
- " else: i_start = np.argmin(np.abs(t-t_start))\n",
- " if t_end is None: i_end = -1\n",
- " else: i_end = np.argmin(np.abs(t-t_end))\n",
- " t = t[i_start:i_end][::5] #subsample data for training (most of it is redundant anyway)\n",
- " fr = np.array(Neurons.history['firingrate'])[i_start:i_end][::5]\n",
- " pos = np.array(Neurons.Agent.history['pos'])[i_start:i_end][::5]\n",
- " #Initialise and fit model\n",
+ " # Get training data\n",
+ " t = np.array(Neurons.history[\"t\"])\n",
+ " if t_start is None:\n",
+ " i_start = 0\n",
+ " else:\n",
+ " i_start = np.argmin(np.abs(t - t_start))\n",
+ " if t_end is None:\n",
+ " i_end = -1\n",
+ " else:\n",
+ " i_end = np.argmin(np.abs(t - t_end))\n",
+ " t = t[i_start:i_end][\n",
+ " ::5\n",
+ " ] # subsample data for training (most of it is redundant anyway)\n",
+ " fr = np.array(Neurons.history[\"firingrate\"])[i_start:i_end][::5]\n",
+ " pos = np.array(Neurons.Agent.history[\"pos\"])[i_start:i_end][::5]\n",
+ " # Initialise and fit model\n",
" from sklearn.gaussian_process.kernels import RBF\n",
- " model_GP = GaussianProcessRegressor(alpha=0.01, kernel=RBF(1\n",
- " *np.sqrt(Neurons.n/20), #<-- kernel size scales with typical input size ~sqrt(N)\n",
- " length_scale_bounds=\"fixed\"\n",
- " ))\n",
+ "\n",
+ " model_GP = GaussianProcessRegressor(\n",
+ " alpha=0.01,\n",
+ " kernel=RBF(\n",
+ " 1\n",
+ " * np.sqrt(\n",
+ " Neurons.n / 20\n",
+ " ), # <-- kernel size scales with typical input size ~sqrt(N)\n",
+ " length_scale_bounds=\"fixed\",\n",
+ " ),\n",
+ " )\n",
" model_LR = Ridge(alpha=0.01)\n",
- " model_GP.fit(fr,pos) \n",
- " model_LR.fit(fr,pos) \n",
- " #Save models into Neurons class for later use\n",
+ " model_GP.fit(fr, pos)\n",
+ " model_LR.fit(fr, pos)\n",
+ " # Save models into Neurons class for later use\n",
" Neurons.decoding_model_GP = model_GP\n",
" Neurons.decoding_model_LR = model_LR\n",
- " return \n",
+ " return\n",
"\n",
- "def decode_position(Neurons,t_start=None,t_end=None):\n",
+ "\n",
+ "def decode_position(Neurons, t_start=None, t_end=None):\n",
" \"\"\"t_start and t_end allow you to pick the poritions of the saved data to train on.\n",
" Returns a list of times and decoded positions\"\"\"\n",
- " #Get testing data\n",
- " t = np.array(Neurons.history['t'])\n",
- " if t_start is None: i_start = 0\n",
- " else: i_start = np.argmin(np.abs(t-t_start))\n",
- " if t_end is None: i_end = -1\n",
- " else: i_end = np.argmin(np.abs(t-t_end))\n",
+ " # Get testing data\n",
+ " t = np.array(Neurons.history[\"t\"])\n",
+ " if t_start is None:\n",
+ " i_start = 0\n",
+ " else:\n",
+ " i_start = np.argmin(np.abs(t - t_start))\n",
+ " if t_end is None:\n",
+ " i_end = -1\n",
+ " else:\n",
+ " i_end = np.argmin(np.abs(t - t_end))\n",
" t = t[i_start:i_end]\n",
- " fr = np.array(Neurons.history['firingrate'])[i_start:i_end]\n",
- " #decode position from the data and using the decoder saved in the Neurons class \n",
+ " fr = np.array(Neurons.history[\"firingrate\"])[i_start:i_end]\n",
+ " # decode position from the data and using the decoder saved in the Neurons class\n",
" decoded_position_GP = Neurons.decoding_model_GP.predict(fr)\n",
" decoded_position_LR = Neurons.decoding_model_LR.predict(fr)\n",
" return (t, decoded_position_GP, decoded_position_LR)"
@@ -120,16 +139,22 @@
"metadata": {},
"outputs": [],
"source": [
- "np.random.seed(10) #make reproducible\n",
+ "np.random.seed(10) # make reproducible\n",
"\n",
"Env = Environment()\n",
- "Env.add_wall(np.array([[0.4,0],[0.4,0.4]]))\n",
- "Ag = Agent(Env, params={'dt':50e-3})\n",
- "\n",
- "\n",
- "PCs = PlaceCells(Ag,params={'description':'gaussian_threshold','widths':0.4,'n':20,'color':'C1'})\n",
- "GCs = GridCells(Ag,params={'n':20,'color':'C2'},)\n",
- "BVCs = BoundaryVectorCells(Ag,params={'n':20,'color':'C3'})"
+ "Env.add_wall(np.array([[0.4, 0], [0.4, 0.4]]))\n",
+ "Ag = Agent(Env, params={\"dt\": 50e-3})\n",
+ "\n",
+ "\n",
+ "PCs = PlaceCells(\n",
+ " Ag,\n",
+ " params={\"description\": \"gaussian_threshold\", \"widths\": 0.4, \"n\": 20, \"color\": \"C1\"},\n",
+ ")\n",
+ "GCs = GridCells(\n",
+ " Ag,\n",
+ " params={\"n\": 20, \"color\": \"C2\"},\n",
+ ")\n",
+ "BVCs = BoundaryVectorCells(Ag, params={\"n\": 20, \"color\": \"C3\"})"
]
},
{
@@ -164,8 +189,9 @@
],
"source": [
"np.random.seed(9)\n",
- "from tqdm import tqdm \n",
- "for i in tqdm(range(int(5*60/Ag.dt))):\n",
+ "from tqdm import tqdm\n",
+ "\n",
+ "for i in tqdm(range(int(5 * 60 / Ag.dt))):\n",
" Ag.update()\n",
" PCs.update()\n",
" GCs.update()\n",
@@ -211,12 +237,15 @@
}
],
"source": [
- "fig, ax = PCs.plot_rate_map(chosen_neurons='all')\n",
- "if save_plots == True: tpl.saveFigure(fig, \"PCs\")\n",
- "fig, ax = GCs.plot_rate_map(chosen_neurons='all')\n",
- "if save_plots == True: tpl.saveFigure(fig, \"GCs\")\n",
- "fig, ax = BVCs.plot_rate_map(chosen_neurons='all')\n",
- "if save_plots == True: tpl.saveFigure(fig, \"BVCs\")"
+ "fig, ax = PCs.plot_rate_map(chosen_neurons=\"all\")\n",
+ "if save_plots == True:\n",
+ " tpl.saveFigure(fig, \"PCs\")\n",
+ "fig, ax = GCs.plot_rate_map(chosen_neurons=\"all\")\n",
+ "if save_plots == True:\n",
+ " tpl.saveFigure(fig, \"GCs\")\n",
+ "fig, ax = BVCs.plot_rate_map(chosen_neurons=\"all\")\n",
+ "if save_plots == True:\n",
+ " tpl.saveFigure(fig, \"BVCs\")"
]
},
{
@@ -270,15 +299,18 @@
],
"source": [
"np.random.seed(10)\n",
- "for i in tqdm(range(int(60/Ag.dt))):\n",
+ "for i in tqdm(range(int(60 / Ag.dt))):\n",
" Ag.update()\n",
" PCs.update()\n",
" GCs.update()\n",
" BVCs.update()\n",
"\n",
- "fig_t, ax_t = Ag.plot_trajectory(fig=fig_t, ax=ax_t,t_start=Ag.t-60,color='black',alpha=0.5)\n",
- "if save_plots == True: tpl.saveFigure(fig_t,\"data\")\n",
- "fig_t\n"
+ "fig_t, ax_t = Ag.plot_trajectory(\n",
+ " fig=fig_t, ax=ax_t, t_start=Ag.t - 60, color=\"black\", alpha=0.5\n",
+ ")\n",
+ "if save_plots == True:\n",
+ " tpl.saveFigure(fig_t, \"data\")\n",
+ "fig_t"
]
},
{
@@ -287,9 +319,9 @@
"metadata": {},
"outputs": [],
"source": [
- "t, pos_PCs_GP, pos_PCs_LR = decode_position(PCs,t_start=Ag.t-60)\n",
- "t, pos_GCs_GP, pos_GCs_LR = decode_position(GCs,t_start=Ag.t-60)\n",
- "t, pos_BVCs_GP, pos_BVCs_LR = decode_position(BVCs,t_start=Ag.t-60)"
+ "t, pos_PCs_GP, pos_PCs_LR = decode_position(PCs, t_start=Ag.t - 60)\n",
+ "t, pos_GCs_GP, pos_GCs_LR = decode_position(GCs, t_start=Ag.t - 60)\n",
+ "t, pos_BVCs_GP, pos_BVCs_LR = decode_position(BVCs, t_start=Ag.t - 60)"
]
},
{
@@ -316,27 +348,32 @@
}
],
"source": [
- "fig, ax = plt.subplots(2,3,figsize=(12,8))\n",
- "Ag.plot_trajectory(t_start=Ag.t-60,fig=fig, ax=ax[0,0],color='black',alpha=0.5)\n",
- "ax[0,0].scatter(pos_PCs_GP[:,0],pos_PCs_GP[:,1],s=5,c='C1',alpha=0.2,zorder=3.1)\n",
- "Ag.plot_trajectory(t_start=Ag.t-60,fig=fig, ax=ax[1,0],color='black', alpha=0.5)\n",
- "ax[1,0].scatter(pos_PCs_LR[:,0],pos_PCs_LR[:,1],s=5,c='C1',alpha=0.2,zorder=3.1)\n",
- "ax[0,0].set_title(\"Place cells\")\n",
- "\n",
- "Ag.plot_trajectory(t_start=Ag.t-60,fig=fig, ax=ax[0,1],color='black', alpha=0.5)\n",
- "ax[0,1].scatter(pos_GCs_GP[:,0],pos_GCs_GP[:,1],s=5,c='C2',alpha=0.2,zorder=3.1)\n",
- "Ag.plot_trajectory(t_start=Ag.t-60,fig=fig, ax=ax[1,1],color='black', alpha=0.5)\n",
- "ax[1,1].scatter(pos_GCs_LR[:,0],pos_GCs_LR[:,1],s=5,c='C2',alpha=0.2,zorder=3.1)\n",
- "ax[0,1].set_title(\"GAUSSIAN PROCESSS REGRESSION\\n\\nGrid cells\")\n",
- "ax[1,1].set_title(\"LINEAR REGRESSION\")\n",
- "\n",
- "Ag.plot_trajectory(t_start=Ag.t-60,fig=fig, ax=ax[0,2],color='black', alpha=0.5)\n",
- "ax[0,2].scatter(pos_BVCs_GP[:,0],pos_BVCs_GP[:,1],s=5,c='C3',alpha=0.5,zorder=3.1)\n",
- "Ag.plot_trajectory(t_start=Ag.t-60,fig=fig, ax=ax[1,2],color='black', alpha=0.5)\n",
- "ax[1,2].scatter(pos_BVCs_LR[:,0],pos_BVCs_LR[:,1],s=5,c='C3',alpha=0.5,zorder=3.1)\n",
- "ax[0,2].set_title(\"Boundary vector cells\")\n",
- "\n",
- "if save_plots == True: tpl.saveFigure(fig, \"decoded\")"
+ "fig, ax = plt.subplots(2, 3, figsize=(12, 8))\n",
+ "Ag.plot_trajectory(t_start=Ag.t - 60, fig=fig, ax=ax[0, 0], color=\"black\", alpha=0.5)\n",
+ "ax[0, 0].scatter(pos_PCs_GP[:, 0], pos_PCs_GP[:, 1], s=5, c=\"C1\", alpha=0.2, zorder=3.1)\n",
+ "Ag.plot_trajectory(t_start=Ag.t - 60, fig=fig, ax=ax[1, 0], color=\"black\", alpha=0.5)\n",
+ "ax[1, 0].scatter(pos_PCs_LR[:, 0], pos_PCs_LR[:, 1], s=5, c=\"C1\", alpha=0.2, zorder=3.1)\n",
+ "ax[0, 0].set_title(\"Place cells\")\n",
+ "\n",
+ "Ag.plot_trajectory(t_start=Ag.t - 60, fig=fig, ax=ax[0, 1], color=\"black\", alpha=0.5)\n",
+ "ax[0, 1].scatter(pos_GCs_GP[:, 0], pos_GCs_GP[:, 1], s=5, c=\"C2\", alpha=0.2, zorder=3.1)\n",
+ "Ag.plot_trajectory(t_start=Ag.t - 60, fig=fig, ax=ax[1, 1], color=\"black\", alpha=0.5)\n",
+ "ax[1, 1].scatter(pos_GCs_LR[:, 0], pos_GCs_LR[:, 1], s=5, c=\"C2\", alpha=0.2, zorder=3.1)\n",
+ "ax[0, 1].set_title(\"GAUSSIAN PROCESSS REGRESSION\\n\\nGrid cells\")\n",
+ "ax[1, 1].set_title(\"LINEAR REGRESSION\")\n",
+ "\n",
+ "Ag.plot_trajectory(t_start=Ag.t - 60, fig=fig, ax=ax[0, 2], color=\"black\", alpha=0.5)\n",
+ "ax[0, 2].scatter(\n",
+ " pos_BVCs_GP[:, 0], pos_BVCs_GP[:, 1], s=5, c=\"C3\", alpha=0.5, zorder=3.1\n",
+ ")\n",
+ "Ag.plot_trajectory(t_start=Ag.t - 60, fig=fig, ax=ax[1, 2], color=\"black\", alpha=0.5)\n",
+ "ax[1, 2].scatter(\n",
+ " pos_BVCs_LR[:, 0], pos_BVCs_LR[:, 1], s=5, c=\"C3\", alpha=0.5, zorder=3.1\n",
+ ")\n",
+ "ax[0, 2].set_title(\"Boundary vector cells\")\n",
+ "\n",
+ "if save_plots == True:\n",
+ " tpl.saveFigure(fig, \"decoded\")"
]
},
{
@@ -488,62 +525,71 @@
"source": [
"from tqdm.notebook import tqdm # notebook compatible loading bars\n",
"\n",
- "N_features = [320,160,80,40,20,10,5]\n",
+ "N_features = [320, 160, 80, 40, 20, 10, 5]\n",
"N_repeats = 15\n",
"\n",
- "results_array = np.zeros(shape=(3,len(N_features),N_repeats,2))\n",
+ "results_array = np.zeros(shape=(3, len(N_features), N_repeats, 2))\n",
"\n",
"Env = Environment()\n",
- "Env.add_wall(np.array([[0.4,0],[0.4,0.4]]))\n",
- "\n",
- "for (i,N) in enumerate(tqdm(N_features, desc=\"Features\")): \n",
- " for j in tqdm(range(N_repeats),leave=False, desc=\"Repeats\"):\n",
- " #Initialise agent and features\n",
- " Ag = Agent(Env, params={'dt':50e-3})\n",
- " PCs = PlaceCells(Ag,params={'n':N,'description':'gaussian_threshold','widths':0.4})\n",
- " GCs = GridCells(Ag,params={'n':N,'gridscale':0.4},)\n",
- " BVCs = BoundaryVectorCells(Ag,params={'n':N})\n",
- "\n",
- " #Generate training data \n",
- " for _ in range(int(5*60/Ag.dt)):\n",
+ "Env.add_wall(np.array([[0.4, 0], [0.4, 0.4]]))\n",
+ "\n",
+ "for (i, N) in enumerate(tqdm(N_features, desc=\"Features\")):\n",
+ " for j in tqdm(range(N_repeats), leave=False, desc=\"Repeats\"):\n",
+ " # Initialise agent and features\n",
+ " Ag = Agent(Env, params={\"dt\": 50e-3})\n",
+ " PCs = PlaceCells(\n",
+ " Ag, params={\"n\": N, \"description\": \"gaussian_threshold\", \"widths\": 0.4}\n",
+ " )\n",
+ " GCs = GridCells(\n",
+ " Ag,\n",
+ " params={\"n\": N, \"gridscale\": 0.4},\n",
+ " )\n",
+ " BVCs = BoundaryVectorCells(Ag, params={\"n\": N})\n",
+ "\n",
+ " # Generate training data\n",
+ " for _ in range(int(5 * 60 / Ag.dt)):\n",
" Ag.update()\n",
" PCs.update()\n",
" GCs.update()\n",
" BVCs.update()\n",
- " \n",
- " #Train\n",
+ "\n",
+ " # Train\n",
" train_decoder(PCs)\n",
" train_decoder(GCs)\n",
" train_decoder(BVCs)\n",
"\n",
- " #Generate test data \n",
- " steps = int(1*60/Ag.dt)\n",
+ " # Generate test data\n",
+ " steps = int(1 * 60 / Ag.dt)\n",
" for _ in range(steps):\n",
" Ag.update()\n",
" PCs.update()\n",
" GCs.update()\n",
" BVCs.update()\n",
- " \n",
- " #Test\n",
- " t, pos_PCs_GP, pos_PCs_LR = decode_position(PCs,t_start=Ag.t-60)\n",
- " t, pos_GCs_GP, pos_GCs_LR = decode_position(GCs,t_start=Ag.t-60)\n",
- " t, pos_BVCs_GP, pos_BVCs_LR = decode_position(BVCs,t_start=Ag.t-60)\n",
- " pos_groundtruth = np.array(Ag.history['pos'])[-steps:,:]\n",
- "\n",
- " #Save results (error in cm) for both gaussian process and linear regression\n",
- " PC_error_GP = 100*np.linalg.norm(pos_PCs_GP-pos_groundtruth,axis=1).mean()\n",
- " GC_error_GP = 100*np.linalg.norm(pos_GCs_GP-pos_groundtruth,axis=1).mean()\n",
- " BVC_error_GP = 100*np.linalg.norm(pos_BVCs_GP-pos_groundtruth,axis=1).mean()\n",
- " PC_error_LR = 100*np.linalg.norm(pos_PCs_LR-pos_groundtruth,axis=1).mean()\n",
- " GC_error_LR = 100*np.linalg.norm(pos_GCs_LR-pos_groundtruth,axis=1).mean()\n",
- " BVC_error_LR = 100*np.linalg.norm(pos_BVCs_LR-pos_groundtruth,axis=1).mean()\n",
- "\n",
- " results_array[0,i,j,0] = PC_error_GP\n",
- " results_array[1,i,j,0] = GC_error_GP\n",
- " results_array[2,i,j,0] = BVC_error_GP\n",
- " results_array[0,i,j,1] = PC_error_LR\n",
- " results_array[1,i,j,1] = GC_error_LR\n",
- " results_array[2,i,j,1] = BVC_error_LR\n"
+ "\n",
+ " # Test\n",
+ " t, pos_PCs_GP, pos_PCs_LR = decode_position(PCs, t_start=Ag.t - 60)\n",
+ " t, pos_GCs_GP, pos_GCs_LR = decode_position(GCs, t_start=Ag.t - 60)\n",
+ " t, pos_BVCs_GP, pos_BVCs_LR = decode_position(BVCs, t_start=Ag.t - 60)\n",
+ " pos_groundtruth = np.array(Ag.history[\"pos\"])[-steps:, :]\n",
+ "\n",
+ " # Save results (error in cm) for both gaussian process and linear regression\n",
+ " PC_error_GP = 100 * np.linalg.norm(pos_PCs_GP - pos_groundtruth, axis=1).mean()\n",
+ " GC_error_GP = 100 * np.linalg.norm(pos_GCs_GP - pos_groundtruth, axis=1).mean()\n",
+ " BVC_error_GP = (\n",
+ " 100 * np.linalg.norm(pos_BVCs_GP - pos_groundtruth, axis=1).mean()\n",
+ " )\n",
+ " PC_error_LR = 100 * np.linalg.norm(pos_PCs_LR - pos_groundtruth, axis=1).mean()\n",
+ " GC_error_LR = 100 * np.linalg.norm(pos_GCs_LR - pos_groundtruth, axis=1).mean()\n",
+ " BVC_error_LR = (\n",
+ " 100 * np.linalg.norm(pos_BVCs_LR - pos_groundtruth, axis=1).mean()\n",
+ " )\n",
+ "\n",
+ " results_array[0, i, j, 0] = PC_error_GP\n",
+ " results_array[1, i, j, 0] = GC_error_GP\n",
+ " results_array[2, i, j, 0] = BVC_error_GP\n",
+ " results_array[0, i, j, 1] = PC_error_LR\n",
+ " results_array[1, i, j, 1] = GC_error_LR\n",
+ " results_array[2, i, j, 1] = BVC_error_LR"
]
},
{
@@ -577,79 +623,116 @@
}
],
"source": [
- "#Get means and std from the results data frame \n",
- "means_GP = np.mean(results_array[:,:,:,0],axis=2)\n",
- "stds_GP = np.std(results_array[:,:,:,0],axis=2) / np.sqrt(15)\n",
- "means_LR = np.mean(results_array[:,:,:,1],axis=2)\n",
- "stds_LR = np.std(results_array[:,:,:,1],axis=2) / np.sqrt(15)\n",
+ "# Get means and std from the results data frame\n",
+ "means_GP = np.mean(results_array[:, :, :, 0], axis=2)\n",
+ "stds_GP = np.std(results_array[:, :, :, 0], axis=2) / np.sqrt(15)\n",
+ "means_LR = np.mean(results_array[:, :, :, 1], axis=2)\n",
+ "stds_LR = np.std(results_array[:, :, :, 1], axis=2) / np.sqrt(15)\n",
"\n",
- "#Make figure for Gaussian process regression\n",
+ "# Make figure for Gaussian process regression\n",
"fig, ax = plt.subplots()\n",
- "ax.scatter(N_features,means_GP[0,:],c='C1')\n",
- "ax.plot(N_features,means_GP[0,:],c='C1',label='Place cells',linewidth=1)\n",
- "ax.fill_between(N_features,means_GP[0,:]+stds_GP[0,:],means_GP[0,:]-stds_GP[0,:],facecolor='C1',alpha=0.3)\n",
- "\n",
- "ax.scatter(N_features,means_GP[1,:],c='C2')\n",
- "ax.plot(N_features,means_GP[1,:],c='C2',label='Grid cells',linewidth=1)\n",
- "ax.fill_between(N_features,means_GP[1,:]+stds_GP[1,:],means_GP[1,:]-stds_GP[1,:],facecolor='C2',alpha=0.3)\n",
- "\n",
- "ax.scatter(N_features,means_GP[2,:],c='C3')\n",
- "ax.plot(N_features,means_GP[2,:],c='C3',label='Boundary vector cells',linewidth=1)\n",
- "ax.fill_between(N_features,means_GP[2,:]+stds_GP[2,:],means_GP[2,:]-stds_GP[2,:],facecolor='C3',alpha=0.3)\n",
- "\n",
- "log2_cms = np.logspace(0,4,5,base=2,dtype=int)\n",
+ "ax.scatter(N_features, means_GP[0, :], c=\"C1\")\n",
+ "ax.plot(N_features, means_GP[0, :], c=\"C1\", label=\"Place cells\", linewidth=1)\n",
+ "ax.fill_between(\n",
+ " N_features,\n",
+ " means_GP[0, :] + stds_GP[0, :],\n",
+ " means_GP[0, :] - stds_GP[0, :],\n",
+ " facecolor=\"C1\",\n",
+ " alpha=0.3,\n",
+ ")\n",
+ "\n",
+ "ax.scatter(N_features, means_GP[1, :], c=\"C2\")\n",
+ "ax.plot(N_features, means_GP[1, :], c=\"C2\", label=\"Grid cells\", linewidth=1)\n",
+ "ax.fill_between(\n",
+ " N_features,\n",
+ " means_GP[1, :] + stds_GP[1, :],\n",
+ " means_GP[1, :] - stds_GP[1, :],\n",
+ " facecolor=\"C2\",\n",
+ " alpha=0.3,\n",
+ ")\n",
+ "\n",
+ "ax.scatter(N_features, means_GP[2, :], c=\"C3\")\n",
+ "ax.plot(N_features, means_GP[2, :], c=\"C3\", label=\"Boundary vector cells\", linewidth=1)\n",
+ "ax.fill_between(\n",
+ " N_features,\n",
+ " means_GP[2, :] + stds_GP[2, :],\n",
+ " means_GP[2, :] - stds_GP[2, :],\n",
+ " facecolor=\"C3\",\n",
+ " alpha=0.3,\n",
+ ")\n",
+ "\n",
+ "log2_cms = np.logspace(0, 4, 5, base=2, dtype=int)\n",
"\n",
"ax.set_xlabel(\"Number of cells \\n (log scale)\")\n",
"ax.set_xscale(\"log\")\n",
"ax.set_yscale(\"log\")\n",
- "ax.tick_params(axis='x', which='minor', bottom=False)\n",
- "ax.tick_params(axis='y', which='minor', left=False)\n",
- "ax.set_xbound(lower=N_features[-1]*0.8, upper=N_features[0]/0.8)\n",
+ "ax.tick_params(axis=\"x\", which=\"minor\", bottom=False)\n",
+ "ax.tick_params(axis=\"y\", which=\"minor\", left=False)\n",
+ "ax.set_xbound(lower=N_features[-1] * 0.8, upper=N_features[0] / 0.8)\n",
"ax.set_ylabel(\"Average decoding error / cm, \\n (log scale)\")\n",
"ax.set_title(\"Gaussian process regression\")\n",
- "ax.spines['right'].set_color('none')\n",
- "ax.spines['top'].set_color('none')\n",
+ "ax.spines[\"right\"].set_color(\"none\")\n",
+ "ax.spines[\"top\"].set_color(\"none\")\n",
"ax.set_xticks(N_features)\n",
"ax.set_yticks(log2_cms)\n",
"ax.set_xticklabels(N_features)\n",
"ax.set_yticklabels(log2_cms)\n",
"ax.legend()\n",
"\n",
- "if save_plots is True: tpl.saveFigure(fig, \"GPanalysis\")\n",
+ "if save_plots is True:\n",
+ " tpl.saveFigure(fig, \"GPanalysis\")\n",
"\n",
"\n",
- "\n",
- "#Make identical figure for linear ridge regression\n",
+ "# Make identical figure for linear ridge regression\n",
"fig, ax = plt.subplots()\n",
- "ax.scatter(N_features,means_LR[0,:],c='C1')\n",
- "ax.plot(N_features,means_LR[0,:],c='C1',label='Place cells',linewidth=1)\n",
- "ax.fill_between(N_features,means_LR[0,:]+stds_LR[0,:],means_LR[0,:]-stds_LR[0,:],facecolor='C1',alpha=0.3)\n",
- "\n",
- "ax.scatter(N_features,means_LR[1,:],c='C2')\n",
- "ax.plot(N_features,means_LR[1,:],c='C2',label='Grid cells',linewidth=1)\n",
- "ax.fill_between(N_features,means_LR[1,:]+stds_LR[1,:],means_LR[1,:]-stds_LR[1,:],facecolor='C2',alpha=0.3)\n",
- "\n",
- "ax.scatter(N_features,means_LR[2,:],c='C3')\n",
- "ax.plot(N_features,means_LR[2,:],c='C3',label='Boundary vector cells',linewidth=1)\n",
- "ax.fill_between(N_features,means_LR[2,:]+stds_LR[2,:],means_LR[2,:]-stds_LR[2,:],facecolor='C3',alpha=0.3)\n",
+ "ax.scatter(N_features, means_LR[0, :], c=\"C1\")\n",
+ "ax.plot(N_features, means_LR[0, :], c=\"C1\", label=\"Place cells\", linewidth=1)\n",
+ "ax.fill_between(\n",
+ " N_features,\n",
+ " means_LR[0, :] + stds_LR[0, :],\n",
+ " means_LR[0, :] - stds_LR[0, :],\n",
+ " facecolor=\"C1\",\n",
+ " alpha=0.3,\n",
+ ")\n",
+ "\n",
+ "ax.scatter(N_features, means_LR[1, :], c=\"C2\")\n",
+ "ax.plot(N_features, means_LR[1, :], c=\"C2\", label=\"Grid cells\", linewidth=1)\n",
+ "ax.fill_between(\n",
+ " N_features,\n",
+ " means_LR[1, :] + stds_LR[1, :],\n",
+ " means_LR[1, :] - stds_LR[1, :],\n",
+ " facecolor=\"C2\",\n",
+ " alpha=0.3,\n",
+ ")\n",
+ "\n",
+ "ax.scatter(N_features, means_LR[2, :], c=\"C3\")\n",
+ "ax.plot(N_features, means_LR[2, :], c=\"C3\", label=\"Boundary vector cells\", linewidth=1)\n",
+ "ax.fill_between(\n",
+ " N_features,\n",
+ " means_LR[2, :] + stds_LR[2, :],\n",
+ " means_LR[2, :] - stds_LR[2, :],\n",
+ " facecolor=\"C3\",\n",
+ " alpha=0.3,\n",
+ ")\n",
"\n",
"ax.set_xlabel(\"Number of cells \\n (log scale)\")\n",
"ax.set_xscale(\"log\")\n",
"ax.set_yscale(\"log\")\n",
- "ax.tick_params(axis='x', which='minor', bottom=False)\n",
- "ax.tick_params(axis='y', which='minor', left=False)\n",
- "ax.set_xbound(lower=N_features[-1]*0.8, upper=N_features[0]/0.8)\n",
+ "ax.tick_params(axis=\"x\", which=\"minor\", bottom=False)\n",
+ "ax.tick_params(axis=\"y\", which=\"minor\", left=False)\n",
+ "ax.set_xbound(lower=N_features[-1] * 0.8, upper=N_features[0] / 0.8)\n",
"ax.set_ylabel(\"Average decoding error / cm, \\n (log scale)\")\n",
"ax.set_title(\"Linear ridge regression\")\n",
- "ax.spines['right'].set_color('none')\n",
- "ax.spines['top'].set_color('none')\n",
+ "ax.spines[\"right\"].set_color(\"none\")\n",
+ "ax.spines[\"top\"].set_color(\"none\")\n",
"ax.set_xticks(N_features)\n",
"ax.set_yticks(log2_cms)\n",
"ax.set_xticklabels(N_features)\n",
"ax.set_yticklabels(log2_cms)\n",
"ax.legend()\n",
"\n",
- "if save_plots is True: tpl.saveFigure(fig, \"LRanalysis\")"
+ "if save_plots is True:\n",
+ " tpl.saveFigure(fig, \"LRanalysis\")"
]
},
{
diff --git a/demos/extensive_example.ipynb b/demos/extensive_example.ipynb
index dace2f0b..a87b477b 100644
--- a/demos/extensive_example.ipynb
+++ b/demos/extensive_example.ipynb
@@ -26,7 +26,7 @@
"metadata": {},
"outputs": [],
"source": [
- "#Import ratinabox\n",
+ "# Import ratinabox\n",
"import ratinabox\n",
"from ratinabox.Environment import Environment\n",
"from ratinabox.Agent import Agent\n",
@@ -48,40 +48,41 @@
],
"source": [
"# 1 Initialise environment.\n",
- "Env = Environment(\n",
- " params = {'aspect':2,\n",
- " 'scale':1})\n",
+ "Env = Environment(params={\"aspect\": 2, \"scale\": 1})\n",
"\n",
- "# 2 Add walls. \n",
- "Env.add_wall([[1,0],[1,0.35]])\n",
- "Env.add_wall([[1,0.65],[1,1]])\n",
+ "# 2 Add walls.\n",
+ "Env.add_wall([[1, 0], [1, 0.35]])\n",
+ "Env.add_wall([[1, 0.65], [1, 1]])\n",
"\n",
"# 3 Add Agent.\n",
"Ag = Agent(Env)\n",
- "Ag.pos = np.array([0.5,0.5])\n",
+ "Ag.pos = np.array([0.5, 0.5])\n",
"Ag.speed_mean = 0.2\n",
"\n",
- "# 4 Add place cells. \n",
- "PCs = PlaceCells(Ag,\n",
- " params={'n':100,\n",
- " 'description':'gaussian_threshold',\n",
- " 'widths':0.40,\n",
- " 'wall_geometry':'line_of_sight',\n",
- " 'max_fr':10,\n",
- " 'min_fr':0.1,\n",
- " 'color':'C1'})\n",
- "PCs.place_cell_centres[-1] = np.array([1.1,0.5])\n",
+ "# 4 Add place cells.\n",
+ "PCs = PlaceCells(\n",
+ " Ag,\n",
+ " params={\n",
+ " \"n\": 100,\n",
+ " \"description\": \"gaussian_threshold\",\n",
+ " \"widths\": 0.40,\n",
+ " \"wall_geometry\": \"line_of_sight\",\n",
+ " \"max_fr\": 10,\n",
+ " \"min_fr\": 0.1,\n",
+ " \"color\": \"C1\",\n",
+ " },\n",
+ ")\n",
+ "PCs.place_cell_centres[-1] = np.array([1.1, 0.5])\n",
"\n",
"# 5 Add boundary vector cells.\n",
- "BVCs = BoundaryVectorCells(Ag,\n",
- " params = {'n':30,\n",
- " 'color':'C2'})\n",
+ "BVCs = BoundaryVectorCells(Ag, params={\"n\": 30, \"color\": \"C2\"})\n",
"\n",
- "# 6 Simulate. \n",
- "dt = 50e-3 \n",
- "T = 10*60\n",
- "from tqdm import tqdm #gives time bar\n",
- "for i in tqdm(range(int(T/dt))):\n",
+ "# 6 Simulate.\n",
+ "dt = 50e-3\n",
+ "T = 10 * 60\n",
+ "from tqdm import tqdm # gives time bar\n",
+ "\n",
+ "for i in tqdm(range(int(T / dt))):\n",
" Ag.update(dt=dt)\n",
" PCs.update()\n",
" BVCs.update()"
@@ -106,9 +107,9 @@
}
],
"source": [
- "# 7 Plot trajectory. \n",
+ "# 7 Plot trajectory.\n",
"fig, ax = Ag.plot_position_heatmap()\n",
- "fig, ax = Ag.plot_trajectory(t_start=50,t_end=60,fig=fig,ax=ax)"
+ "fig, ax = Ag.plot_trajectory(t_start=50, t_end=60, fig=fig, ax=ax)"
]
},
{
@@ -130,8 +131,10 @@
}
],
"source": [
- "# 8 Plot timeseries. \n",
- "fig, ax = BVCs.plot_rate_timeseries(t_start=0,t_end=60,chosen_neurons='12',spikes=True)"
+ "# 8 Plot timeseries.\n",
+ "fig, ax = BVCs.plot_rate_timeseries(\n",
+ " t_start=0, t_end=60, chosen_neurons=\"12\", spikes=True\n",
+ ")"
]
},
{
@@ -153,7 +156,7 @@
}
],
"source": [
- "# 9 Plot place cells. \n",
+ "# 9 Plot place cells.\n",
"fig, ax = PCs.plot_place_cell_locations()"
]
},
@@ -188,9 +191,9 @@
}
],
"source": [
- "# 10 Plot rate maps. \n",
- "fig, ax = PCs.plot_rate_map(chosen_neurons='3',method='groundtruth')\n",
- "fig, ax = PCs.plot_rate_map(chosen_neurons='3',method='history',spikes=True)"
+ "# 10 Plot rate maps.\n",
+ "fig, ax = PCs.plot_rate_map(chosen_neurons=\"3\", method=\"groundtruth\")\n",
+ "fig, ax = PCs.plot_rate_map(chosen_neurons=\"3\", method=\"history\", spikes=True)"
]
},
{
@@ -223,8 +226,8 @@
],
"source": [
"# 11 Display BVC rate maps and polar receptive fields\n",
- "fig, ax = BVCs.plot_rate_map(chosen_neurons='2')\n",
- "fig, ax = BVCs.plot_BVC_receptive_field(chosen_neurons='2')"
+ "fig, ax = BVCs.plot_rate_map(chosen_neurons=\"2\")\n",
+ "fig, ax = BVCs.plot_BVC_receptive_field(chosen_neurons=\"2\")"
]
},
{
@@ -256,21 +259,25 @@
}
],
"source": [
- "# 12 Multipanel figure \n",
- "fig, axes = plt.subplots(2,8,figsize=(24,6))\n",
- "Ag.plot_trajectory(t_start=0, t_end=60,fig=fig,ax=axes[0,0])\n",
- "axes[0,0].set_title(\"Trajectory (last minute)\")\n",
- "Ag.plot_position_heatmap(fig=fig,ax=axes[1,0])\n",
- "axes[1,0].set_title(\"Full trajectory heatmap\")\n",
- "PCs.plot_rate_timeseries(t_start=0,t_end=60,chosen_neurons='6',spikes=True,fig=fig, ax=axes[0,1])\n",
- "axes[0,1].set_title(\"Place cell activity\")\n",
- "axes[0,1].set_xlabel(\"\")\n",
- "BVCs.plot_rate_timeseries(t_start=0,t_end=60,chosen_neurons='6',spikes=True,fig=fig, ax=axes[1,1])\n",
- "axes[1,1].set_title(\"BVC activity\")\n",
- "PCs.plot_rate_map(chosen_neurons='6',method='groundtruth',fig=fig,ax=axes[0,2:])\n",
- "axes[0,2].set_title(\"Place cell receptive fields\")\n",
- "BVCs.plot_rate_map(chosen_neurons='6',method='groundtruth',fig=fig,ax=axes[1,2:])\n",
- "axes[1,2].set_title(\"BVC receptive fields\")"
+ "# 12 Multipanel figure\n",
+ "fig, axes = plt.subplots(2, 8, figsize=(24, 6))\n",
+ "Ag.plot_trajectory(t_start=0, t_end=60, fig=fig, ax=axes[0, 0])\n",
+ "axes[0, 0].set_title(\"Trajectory (last minute)\")\n",
+ "Ag.plot_position_heatmap(fig=fig, ax=axes[1, 0])\n",
+ "axes[1, 0].set_title(\"Full trajectory heatmap\")\n",
+ "PCs.plot_rate_timeseries(\n",
+ " t_start=0, t_end=60, chosen_neurons=\"6\", spikes=True, fig=fig, ax=axes[0, 1]\n",
+ ")\n",
+ "axes[0, 1].set_title(\"Place cell activity\")\n",
+ "axes[0, 1].set_xlabel(\"\")\n",
+ "BVCs.plot_rate_timeseries(\n",
+ " t_start=0, t_end=60, chosen_neurons=\"6\", spikes=True, fig=fig, ax=axes[1, 1]\n",
+ ")\n",
+ "axes[1, 1].set_title(\"BVC activity\")\n",
+ "PCs.plot_rate_map(chosen_neurons=\"6\", method=\"groundtruth\", fig=fig, ax=axes[0, 2:])\n",
+ "axes[0, 2].set_title(\"Place cell receptive fields\")\n",
+ "BVCs.plot_rate_map(chosen_neurons=\"6\", method=\"groundtruth\", fig=fig, ax=axes[1, 2:])\n",
+ "axes[1, 2].set_title(\"BVC receptive fields\")"
]
},
{
@@ -300,7 +307,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.9.7"
+ "version": "3.10.9"
},
"orig_nbformat": 4
},
diff --git a/demos/list_of_plotting_fuctions.md b/demos/list_of_plotting_fuctions.md
index b1010a60..c6b4e463 100644
--- a/demos/list_of_plotting_fuctions.md
+++ b/demos/list_of_plotting_fuctions.md
@@ -12,7 +12,7 @@ Displays the environment. Works for both 1 or 2D environments.
Examples:
* `Env.plot_environment()`
-
+
@@ -24,37 +24,37 @@ Plots the agent trajectory. Works for 1 or 2D.
* `Ag.plot_trajectory(t_end=120)`
-
+
* `Ag1D.plot_trajectory(t_end=120)`
-
+
## `Agent.animate_trajectory()`
Makes an animation of the agents trajectory.
-
+
## `Agent.plot_position_heatmap()`
Plots a heatmap of the Agents past locations (2D and 1D example shown)
-
+
-
+
## `Agent.plot_histogram_of_speeds()`
-
+
## `Agent.plot_histogram_of_rotational_velocities()`
-
+
# `Neurons`
@@ -62,18 +62,18 @@ Plots a heatmap of the Agents past locations (2D and 1D example shown)
## `Neurons.plot_rate_timeseries()`
Plots a timeseries of the firing rates
-
+
## `Neurons.plot_rate_timeseries(imshow=True)`
Plots a timeseries of the firing rates as an image
-
+
Plots a timeseries of the firing rates
-
+
## `Neurons.animate_rate_timeseries()`
Makes an animation of the firing rates timeseries
-
+
## `Neurons.plot_ratemap()`
@@ -86,21 +86,21 @@ As an example here we show this function for a set of 3 (two dimensional) grid c
* `Neurons.plot_ratemap(method=`analytic`)
-
+
-
+
* `Neurons.plot_ratemap(method=`history`)
-
+
-
+
* `Neurons.plot_ratemap(method=`neither`, spikes=True)
-
+
-
+
@@ -108,12 +108,12 @@ As an example here we show this function for a set of 3 (two dimensional) grid c
Scatters where the place cells are centres
-
+
## `BoundaryVectorCells.plot_BVC_receptive_field()`
-
+
# Other details:
@@ -126,7 +126,7 @@ fig, ax = Neurons.plot_rate_map(chosen_neuron="1")
fig, ax = Ag.plot_trajectory(fig=fig, ax=ax)
```
-
+
2. Multipanel figures:
```python
@@ -136,7 +136,7 @@ Neurons.plot_rate_map(fig=fig,ax=[axes[1],axes[2],axes[3]],chosen_neurons='3') #
Neurons.plot_rate_timeseries(fig=fig,ax=axes[4])
```
-
+
* For rate maps and timeseries' by default **all** the cells will be plotted. This may take a long time if the number of cells is large. Control this with the `chosen_neurons` argument
diff --git a/demos/paper_figures.ipynb b/demos/paper_figures.ipynb
index 13744b11..0d22492c 100644
--- a/demos/paper_figures.ipynb
+++ b/demos/paper_figures.ipynb
@@ -44,15 +44,17 @@
"metadata": {},
"outputs": [],
"source": [
- "#Leave this as False. \n",
- "#For paper/readme production I use a plotting library (tomplotlib) to format and save figures. Without this they will still show but not save. \n",
- "if False: \n",
+ "# Leave this as False.\n",
+ "# For paper/readme production I use a plotting library (tomplotlib) to format and save figures. Without this they will still show but not save.\n",
+ "if False:\n",
" import tomplotlib.tomplotlib as tpl\n",
+ "\n",
" tpl.figureDirectory = \"../figures/\"\n",
" tpl.setColorscheme(colorscheme=2)\n",
" save_plots = True\n",
" from matplotlib import rcParams, rc\n",
- " rcParams['figure.dpi']= 300\n",
+ "\n",
+ " rcParams[\"figure.dpi\"] = 300\n",
"else:\n",
" save_plots = False"
]
@@ -94,43 +96,32 @@
}
],
"source": [
- "ratinabox.verbose=False\n",
+ "ratinabox.verbose = False\n",
"Env = Environment()\n",
- "Env.add_wall(np.array([[0.4,0],[0.4,0.4]]))\n",
+ "Env.add_wall(np.array([[0.4, 0], [0.4, 0.4]]))\n",
"\n",
"Ag = Agent(Env)\n",
"\n",
- "PCs = PlaceCells(Ag,\n",
- " params={'n':4,\n",
- " 'description':'gaussian_threshold',\n",
- " 'widths':0.4,\n",
- " 'color':'C1'\n",
- " }\n",
+ "PCs = PlaceCells(\n",
+ " Ag,\n",
+ " params={\"n\": 4, \"description\": \"gaussian_threshold\", \"widths\": 0.4, \"color\": \"C1\"},\n",
")\n",
"\n",
- "GCs = GridCells(Ag,\n",
- " params={'n':4,\n",
- " 'color':'C2'\n",
- " }\n",
- ")\n",
+ "GCs = GridCells(Ag, params={\"n\": 4, \"color\": \"C2\"})\n",
"\n",
- "BVCs = BoundaryVectorCells(Ag,\n",
- " params={'n':4,\n",
- " 'color':'C3'\n",
- " }\n",
- ")\n",
+ "BVCs = BoundaryVectorCells(Ag, params={\"n\": 4, \"color\": \"C3\"})\n",
"\n",
- "VCs = VelocityCells(Ag,\n",
- " params={'color':'C5'\n",
- " }\n",
- ")\n",
+ "VCs = VelocityCells(Ag, params={\"color\": \"C5\"})\n",
"\n",
"fig, ax = PCs.plot_rate_map()\n",
- "if save_plots == True: tpl.saveFigure(fig,'PCs')\n",
+ "if save_plots == True:\n",
+ " tpl.saveFigure(fig, \"PCs\")\n",
"fig, ax = GCs.plot_rate_map()\n",
- "if save_plots == True: tpl.saveFigure(fig,'GCs')\n",
+ "if save_plots == True:\n",
+ " tpl.saveFigure(fig, \"GCs\")\n",
"fig, ax = BVCs.plot_rate_map()\n",
- "if save_plots == True: tpl.saveFigure(fig,'BVCs')\n"
+ "if save_plots == True:\n",
+ " tpl.saveFigure(fig, \"BVCs\")"
]
},
{
@@ -147,7 +138,7 @@
}
],
"source": [
- "for i in tqdm(range(int(60/Ag.dt))):\n",
+ "for i in tqdm(range(int(60 / Ag.dt))):\n",
" Ag.update()\n",
" PCs.update()\n",
" GCs.update()\n",
@@ -213,17 +204,21 @@
],
"source": [
"fig, ax = Ag.plot_trajectory()\n",
- "if save_plots == True: tpl.saveFigure(fig,'trajectory')\n",
+ "if save_plots == True:\n",
+ " tpl.saveFigure(fig, \"trajectory\")\n",
"\n",
"fig, ax = VCs.plot_rate_timeseries()\n",
- "if save_plots == True: tpl.saveFigure(fig,'VCs_ts')\n",
+ "if save_plots == True:\n",
+ " tpl.saveFigure(fig, \"VCs_ts\")\n",
"fig, ax = BVCs.plot_rate_timeseries()\n",
- "if save_plots == True: tpl.saveFigure(fig,'BVCs_ts')\n",
+ "if save_plots == True:\n",
+ " tpl.saveFigure(fig, \"BVCs_ts\")\n",
"fig, ax = GCs.plot_rate_timeseries()\n",
- "if save_plots == True: tpl.saveFigure(fig,'GCs_ts')\n",
+ "if save_plots == True:\n",
+ " tpl.saveFigure(fig, \"GCs_ts\")\n",
"fig, ax = PCs.plot_rate_timeseries()\n",
- "if save_plots == True: tpl.saveFigure(fig,'PCs_ts')\n",
- "\n"
+ "if save_plots == True:\n",
+ " tpl.saveFigure(fig, \"PCs_ts\")"
]
},
{
@@ -289,68 +284,70 @@
],
"source": [
"Env1 = Environment()\n",
- "Env1.add_wall([[0,0.5],[0.2,0.5]])\n",
- "Env1.add_wall([[0.3,0.5],[0.7,0.5]])\n",
- "Env1.add_wall([[0.8,0.5],[1,0.5]])\n",
- "Env1.add_wall([[0.5,0],[0.5,0.2]])\n",
- "Env1.add_wall([[0.5,0.3],[0.5,0.7]])\n",
- "Env1.add_wall([[0.5,0.8],[0.5,1]])\n",
+ "Env1.add_wall([[0, 0.5], [0.2, 0.5]])\n",
+ "Env1.add_wall([[0.3, 0.5], [0.7, 0.5]])\n",
+ "Env1.add_wall([[0.8, 0.5], [1, 0.5]])\n",
+ "Env1.add_wall([[0.5, 0], [0.5, 0.2]])\n",
+ "Env1.add_wall([[0.5, 0.3], [0.5, 0.7]])\n",
+ "Env1.add_wall([[0.5, 0.8], [0.5, 1]])\n",
"Ag1 = Agent(Env)\n",
- "Ag1.pos = np.array([0.4,0.25])\n",
- "Ag1.velocity = 0.3*np.array([1,0])\n",
+ "Ag1.pos = np.array([0.4, 0.25])\n",
+ "Ag1.velocity = 0.3 * np.array([1, 0])\n",
"\n",
"\n",
"Env2 = Environment()\n",
- "Env2.add_wall([[0.2,0],[0.2,0.8]])\n",
- "Env2.add_wall([[0.4,1],[0.4,0.2]])\n",
- "Env2.add_wall([[0.6,0],[0.6,0.8]])\n",
- "Env2.add_wall([[0.8,1],[0.8,0.2]])\n",
+ "Env2.add_wall([[0.2, 0], [0.2, 0.8]])\n",
+ "Env2.add_wall([[0.4, 1], [0.4, 0.2]])\n",
+ "Env2.add_wall([[0.6, 0], [0.6, 0.8]])\n",
+ "Env2.add_wall([[0.8, 1], [0.8, 0.2]])\n",
"Ag2 = Agent(Env2)\n",
- "Ag2.pos = np.array([0.1,0.1])\n",
- "Ag2.velocity = 0.3*np.array([0,1])\n",
+ "Ag2.pos = np.array([0.1, 0.1])\n",
+ "Ag2.velocity = 0.3 * np.array([0, 1])\n",
"\n",
"\n",
- "Env3 = Environment(params={'aspect':2,\n",
- " 'scale':0.5}) \n",
- "Env3.add_wall([[0.5,0],[0.5,0.4]])\n",
- "Env3.add_wall([[0,0.4],[0.2,0.4]])\n",
- "Env3.add_wall([[0.3,0.4],[0.7,0.4]])\n",
- "Env3.add_wall([[0.8,0.4],[1,0.4]])\n",
+ "Env3 = Environment(params={\"aspect\": 2, \"scale\": 0.5})\n",
+ "Env3.add_wall([[0.5, 0], [0.5, 0.4]])\n",
+ "Env3.add_wall([[0, 0.4], [0.2, 0.4]])\n",
+ "Env3.add_wall([[0.3, 0.4], [0.7, 0.4]])\n",
+ "Env3.add_wall([[0.8, 0.4], [1, 0.4]])\n",
"Ag3 = Agent(Env3)\n",
- "Ag3.pos = np.array([0.22,0.35])\n",
- "Ag3.velocity = 0.3*np.array([0.5,1])\n",
+ "Ag3.pos = np.array([0.22, 0.35])\n",
+ "Ag3.velocity = 0.3 * np.array([0.5, 1])\n",
"\n",
"\n",
- "Env4 = Environment(params={'aspect':2,\n",
- " 'scale':0.5})\n",
- "Env4.add_wall([[0.1,0.25],[0.5,0.45]])\n",
- "Env4.add_wall([[0.4,0.3],[0.65,0.05]])\n",
- "Env4.add_wall([[0.65,0.25],[0.9,0.3]])\n",
+ "Env4 = Environment(params={\"aspect\": 2, \"scale\": 0.5})\n",
+ "Env4.add_wall([[0.1, 0.25], [0.5, 0.45]])\n",
+ "Env4.add_wall([[0.4, 0.3], [0.65, 0.05]])\n",
+ "Env4.add_wall([[0.65, 0.25], [0.9, 0.3]])\n",
"\n",
"Ag4 = Agent(Env)\n",
- "Ag4.pos = np.array([0.5,0.05])\n",
- "Ag4.velocity = 0.3*np.array([0,1])\n",
+ "Ag4.pos = np.array([0.5, 0.05])\n",
+ "Ag4.velocity = 0.3 * np.array([0, 1])\n",
"\n",
"\n",
"train_time = 10\n",
- "for i in tqdm(range(int(train_time/Ag1.dt))): \n",
+ "for i in tqdm(range(int(train_time / Ag1.dt))):\n",
" Ag1.update()\n",
" Ag2.update()\n",
" Ag3.update()\n",
" Ag4.update()\n",
"\n",
"\n",
- "fig1,ax1=Ag1.plot_trajectory(t_end=5)\n",
- "if save_plots == True: tpl.saveFigure(fig1,'fourroom')\n",
+ "fig1, ax1 = Ag1.plot_trajectory(t_end=5)\n",
+ "if save_plots == True:\n",
+ " tpl.saveFigure(fig1, \"fourroom\")\n",
"\n",
- "fig2,ax2=Ag2.plot_trajectory(t_end=5)\n",
- "if save_plots == True: tpl.saveFigure(fig2,'hairpin')\n",
+ "fig2, ax2 = Ag2.plot_trajectory(t_end=5)\n",
+ "if save_plots == True:\n",
+ " tpl.saveFigure(fig2, \"hairpin\")\n",
"\n",
- "fig3,ax3=Ag3.plot_trajectory(t_end=5)\n",
- "if save_plots == True: tpl.saveFigure(fig3,'tworoom')\n",
+ "fig3, ax3 = Ag3.plot_trajectory(t_end=5)\n",
+ "if save_plots == True:\n",
+ " tpl.saveFigure(fig3, \"tworoom\")\n",
"\n",
- "fig4,ax4=Ag4.plot_trajectory(t_end=5)\n",
- "if save_plots == True: tpl.saveFigure(fig4,'random')"
+ "fig4, ax4 = Ag4.plot_trajectory(t_end=5)\n",
+ "if save_plots == True:\n",
+ " tpl.saveFigure(fig4, \"random\")"
]
},
{
@@ -377,18 +374,15 @@
}
],
"source": [
- "Env = Environment(params={'dimensionality':'1D',\n",
- " 'boundary_conditions':'periodic'})\n",
- "Ag = Agent(Env,\n",
- " params={'speed_mean':0.1,\n",
- " 'speed_std':0.2}\n",
- ")\n",
+ "Env = Environment(params={\"dimensionality\": \"1D\", \"boundary_conditions\": \"periodic\"})\n",
+ "Ag = Agent(Env, params={\"speed_mean\": 0.1, \"speed_std\": 0.2})\n",
"\n",
- "for i in range(int(60/Ag.dt)):\n",
+ "for i in range(int(60 / Ag.dt)):\n",
" Ag.update()\n",
"\n",
"fig, ax = Ag.plot_trajectory()\n",
- "if save_plots == True: tpl.saveFigure(fig,'1Dtrajectory')\n"
+ "if save_plots == True:\n",
+ " tpl.saveFigure(fig, \"1Dtrajectory\")"
]
},
{
@@ -431,50 +425,63 @@
"from scipy import io\n",
"from scipy.optimize import curve_fit\n",
"\n",
- "def rayleigh(x,sigma,K):\n",
- " return K*x*np.e**(-x**2/(2*(sigma**2)))\n",
- "def exponential(t,tau,K):\n",
- " return K*np.e**(-t/tau)\n",
- "def gaussian(x,sigma,K):\n",
- " return K*np.e**(-x**2/(2*(sigma**2)))\n",
- "def lagged_autocorrelation(t,x,max_t=10):\n",
+ "\n",
+ "def rayleigh(x, sigma, K):\n",
+ " return K * x * np.e ** (-(x**2) / (2 * (sigma**2)))\n",
+ "\n",
+ "\n",
+ "def exponential(t, tau, K):\n",
+ " return K * np.e ** (-t / tau)\n",
+ "\n",
+ "\n",
+ "def gaussian(x, sigma, K):\n",
+ " return K * np.e ** (-(x**2) / (2 * (sigma**2)))\n",
+ "\n",
+ "\n",
+ "def lagged_autocorrelation(t, x, max_t=10):\n",
" from scipy.stats.stats import pearsonr\n",
+ "\n",
" R, T = [], []\n",
" time, i = 0, 0\n",
" while time < max_t:\n",
- " if i == 0:r = pearsonr(x,x)[0]\n",
- " else: r = pearsonr(x[i:],x[:-i])[0]\n",
+ " if i == 0:\n",
+ " r = pearsonr(x, x)[0]\n",
+ " else:\n",
+ " r = pearsonr(x[i:], x[:-i])[0]\n",
" i += 1\n",
" T.append(t[i])\n",
" R.append(r)\n",
" time = t[i]\n",
" return np.array(T), np.array(R)\n",
"\n",
- "#import data\n",
- "mat = io.loadmat(\"../rawdata//8F6BE356-3277-475C-87B1-C7A977632DA7_1/11084-03020501_t2c1.mat\")\n",
- "x = ((mat['x1'] + mat['x2'])/2).reshape(-1)\n",
- "y = ((mat['y1'] + mat['y2'])/2).reshape(-1)\n",
- "t = (mat['t']).reshape(-1)\n",
- "#remove nans \n",
+ "\n",
+ "# import data\n",
+ "mat = io.loadmat(\n",
+ " \"../rawdata//8F6BE356-3277-475C-87B1-C7A977632DA7_1/11084-03020501_t2c1.mat\"\n",
+ ")\n",
+ "x = ((mat[\"x1\"] + mat[\"x2\"]) / 2).reshape(-1)\n",
+ "y = ((mat[\"y1\"] + mat[\"y2\"]) / 2).reshape(-1)\n",
+ "t = (mat[\"t\"]).reshape(-1)\n",
+ "# remove nans\n",
"y = y[np.logical_not(np.isnan(x))]\n",
"t = t[np.logical_not(np.isnan(x))]\n",
"x = x[np.logical_not(np.isnan(x))]\n",
- "#normalise and put in metres\n",
- "x = (x-min(x))/100\n",
- "y = (y-min(y))/100\n",
- "x = x + 0.5*(1-max(x))\n",
- "y = y + 0.5*(1-max(y))\n",
- "#downsample (so my code will later smooth it) (currently at 50Hz --> 2.5Hz)\n",
+ "# normalise and put in metres\n",
+ "x = (x - min(x)) / 100\n",
+ "y = (y - min(y)) / 100\n",
+ "x = x + 0.5 * (1 - max(x))\n",
+ "y = y + 0.5 * (1 - max(y))\n",
+ "# downsample (so my code will later smooth it) (currently at 50Hz --> 2.5Hz)\n",
"x = x[::20]\n",
"y = y[::20]\n",
"t = t[::20]\n",
- "#concatenate\n",
- "pos = np.stack((x,y)).T\n",
- "#make env, pass data to agent, and then upsample\n",
+ "# concatenate\n",
+ "pos = np.stack((x, y)).T\n",
+ "# make env, pass data to agent, and then upsample\n",
"Env = Environment()\n",
"Ag_s = Agent(Env)\n",
- "Ag_s.import_trajectory(times=t,positions=pos)\n",
- "for i in tqdm(range(int(max(t)/Ag_s.dt))):\n",
+ "Ag_s.import_trajectory(times=t, positions=pos)\n",
+ "for i in tqdm(range(int(max(t) / Ag_s.dt))):\n",
" Ag_s.update()"
]
},
@@ -559,75 +566,73 @@
}
],
"source": [
- "#plot sargolini trajectory\n",
- "fig, ax = Ag_s.plot_trajectory(t_end=5*60)\n",
- "if save_plots == True: tpl.saveFigure(fig,'sarg_trajectory')\n",
+ "# plot sargolini trajectory\n",
+ "fig, ax = Ag_s.plot_trajectory(t_end=5 * 60)\n",
+ "if save_plots == True:\n",
+ " tpl.saveFigure(fig, \"sarg_trajectory\")\n",
"\n",
"\n",
- "#plot sargolini speed histogram \n",
+ "# plot sargolini speed histogram\n",
"fig, ax, y_v, x_v, patches = Ag_s.plot_histogram_of_speeds(return_data=True)\n",
"ax.set_xlim(right=0.6)\n",
- "x_v = (x_v[1:]+x_v[:-1])/2\n",
- "sigma, K = curve_fit(rayleigh,x_v,y_v)[0]\n",
- "print(\"best Rayleigh sigma:\",sigma)\n",
- "y_fit = rayleigh(x_v,sigma,K)\n",
- "ax.plot(x_v,y_fit)\n",
- "if save_plots == True: \n",
- " tpl.xyAxes(ax)\n",
- " tpl.saveFigure(fig,'sarg_rayleigh')\n",
- "\n",
- "\n",
- "#plot sargolini rotational speed histogram \n",
- "fig, ax, y_v, x_v, patches = Ag_s.plot_histogram_of_rotational_velocities(return_data=True)\n",
- "ax.set_xlim(left=-1000,right=1000)\n",
- "x_v = (x_v[1:]+x_v[:-1])/2\n",
- "sigma, K = curve_fit(gaussian,x_v,y_v,p0=np.array([1000,500]))[0]\n",
- "print(\"best gaussian sigma:\",sigma)\n",
- "y_fit = gaussian(x_v,sigma,K)\n",
- "ax.plot(x_v,y_fit)\n",
- "if save_plots == True: \n",
+ "x_v = (x_v[1:] + x_v[:-1]) / 2\n",
+ "sigma, K = curve_fit(rayleigh, x_v, y_v)[0]\n",
+ "print(\"best Rayleigh sigma:\", sigma)\n",
+ "y_fit = rayleigh(x_v, sigma, K)\n",
+ "ax.plot(x_v, y_fit)\n",
+ "if save_plots == True:\n",
" tpl.xyAxes(ax)\n",
- " tpl.saveFigure(fig,'sarg_normal')\n",
+ " tpl.saveFigure(fig, \"sarg_rayleigh\")\n",
"\n",
"\n",
+ "# plot sargolini rotational speed histogram\n",
+ "fig, ax, y_v, x_v, patches = Ag_s.plot_histogram_of_rotational_velocities(\n",
+ " return_data=True\n",
+ ")\n",
+ "ax.set_xlim(left=-1000, right=1000)\n",
+ "x_v = (x_v[1:] + x_v[:-1]) / 2\n",
+ "sigma, K = curve_fit(gaussian, x_v, y_v, p0=np.array([1000, 500]))[0]\n",
+ "print(\"best gaussian sigma:\", sigma)\n",
+ "y_fit = gaussian(x_v, sigma, K)\n",
+ "ax.plot(x_v, y_fit)\n",
+ "if save_plots == True:\n",
+ " tpl.xyAxes(ax)\n",
+ " tpl.saveFigure(fig, \"sarg_normal\")\n",
"\n",
- "t = np.array(Ag_s.history['t'])\n",
- "speed = np.linalg.norm(np.array(Ag_s.history['vel']),axis=1)\n",
- "speed = (speed - np.mean(speed))/np.std(speed)\n",
- "lag, speed_autocorr = lagged_autocorrelation(t,speed)\n",
+ "\n",
+ "t = np.array(Ag_s.history[\"t\"])\n",
+ "speed = np.linalg.norm(np.array(Ag_s.history[\"vel\"]), axis=1)\n",
+ "speed = (speed - np.mean(speed)) / np.std(speed)\n",
+ "lag, speed_autocorr = lagged_autocorrelation(t, speed)\n",
"lag = lag[10:]\n",
"speed_autocorr = speed_autocorr[10:]\n",
"fig, ax = plt.subplots()\n",
- "ax.plot(lag,speed_autocorr)\n",
- "tau, K = curve_fit(exponential,lag,speed_autocorr)[0]\n",
- "print(\"best tau for speed is:\",tau)\n",
- "y_fit = exponential(lag,tau,K)\n",
- "ax.plot(lag,y_fit)\n",
- "ax.set_xlim(left=0,right=4)\n",
- "if save_plots == True: \n",
+ "ax.plot(lag, speed_autocorr)\n",
+ "tau, K = curve_fit(exponential, lag, speed_autocorr)[0]\n",
+ "print(\"best tau for speed is:\", tau)\n",
+ "y_fit = exponential(lag, tau, K)\n",
+ "ax.plot(lag, y_fit)\n",
+ "ax.set_xlim(left=0, right=4)\n",
+ "if save_plots == True:\n",
" tpl.xyAxes(ax)\n",
- " tpl.saveFigure(fig,'sarg_speedac')\n",
- "\n",
- "\n",
+ " tpl.saveFigure(fig, \"sarg_speedac\")\n",
"\n",
"\n",
- "\n",
- "rot_vel = np.array(Ag_s.history['rot_vel'])\n",
- "rot_vel = (rot_vel - np.mean(rot_vel))/np.std(rot_vel)\n",
- "lag, rot_vel_autocorr = lagged_autocorrelation(t,rot_vel)\n",
+ "rot_vel = np.array(Ag_s.history[\"rot_vel\"])\n",
+ "rot_vel = (rot_vel - np.mean(rot_vel)) / np.std(rot_vel)\n",
+ "lag, rot_vel_autocorr = lagged_autocorrelation(t, rot_vel)\n",
"lag = lag[10:]\n",
"rot_vel_autocorr = rot_vel_autocorr[10:]\n",
"fig, ax = plt.subplots()\n",
- "ax.plot(lag,rot_vel_autocorr)\n",
- "tau, K = curve_fit(exponential,lag,rot_vel_autocorr)[0]\n",
- "print(\"best tau for rotational_vel is:\",tau)\n",
- "y_fit = exponential(lag,tau,K)\n",
- "ax.plot(lag,y_fit)\n",
+ "ax.plot(lag, rot_vel_autocorr)\n",
+ "tau, K = curve_fit(exponential, lag, rot_vel_autocorr)[0]\n",
+ "print(\"best tau for rotational_vel is:\", tau)\n",
+ "y_fit = exponential(lag, tau, K)\n",
+ "ax.plot(lag, y_fit)\n",
"ax.set_xlim(right=4)\n",
- "if save_plots == True: \n",
+ "if save_plots == True:\n",
" tpl.xyAxes(ax)\n",
- " tpl.saveFigure(fig,'sarg_rotac')\n",
- "\n"
+ " tpl.saveFigure(fig, \"sarg_rotac\")"
]
},
{
@@ -654,7 +659,7 @@
"source": [
"Env = Environment()\n",
"Ag_r = Agent(Env)\n",
- "for i in tqdm(range(int(600/Ag_r.dt))):\n",
+ "for i in tqdm(range(int(600 / Ag_r.dt))):\n",
" Ag_r.update()"
]
},
@@ -731,53 +736,54 @@
}
],
"source": [
- "fig, ax = Ag_r.plot_trajectory(t_end = 60*5)\n",
- "if save_plots == True: tpl.saveFigure(fig,'riab_trajectory')\n",
+ "fig, ax = Ag_r.plot_trajectory(t_end=60 * 5)\n",
+ "if save_plots == True:\n",
+ " tpl.saveFigure(fig, \"riab_trajectory\")\n",
"\n",
"fig, ax = Ag_r.plot_histogram_of_speeds()\n",
- "ax.set_xlim(0,0.60)\n",
- "if save_plots == True: \n",
+ "ax.set_xlim(0, 0.60)\n",
+ "if save_plots == True:\n",
" tpl.xyAxes(ax)\n",
- " tpl.saveFigure(fig,'riab_rayleigh')\n",
+ " tpl.saveFigure(fig, \"riab_rayleigh\")\n",
"\n",
"fig, ax = Ag_r.plot_histogram_of_rotational_velocities()\n",
- "ax.set_xlim(-1000,1000)\n",
- "if save_plots == True: \n",
+ "ax.set_xlim(-1000, 1000)\n",
+ "if save_plots == True:\n",
" tpl.xyAxes(ax)\n",
- " tpl.saveFigure(fig,'riab_normal')\n",
+ " tpl.saveFigure(fig, \"riab_normal\")\n",
"\n",
- "t = np.array(Ag_r.history['t'])\n",
- "speed = np.linalg.norm(np.array(Ag_r.history['vel']),axis=1)\n",
- "speed = (speed - np.mean(speed))/np.std(speed)\n",
- "lag, speed_autocorr = lagged_autocorrelation(t,speed)\n",
+ "t = np.array(Ag_r.history[\"t\"])\n",
+ "speed = np.linalg.norm(np.array(Ag_r.history[\"vel\"]), axis=1)\n",
+ "speed = (speed - np.mean(speed)) / np.std(speed)\n",
+ "lag, speed_autocorr = lagged_autocorrelation(t, speed)\n",
"lag = lag[10:]\n",
"speed_autocorr = speed_autocorr[10:]\n",
"fig, ax = plt.subplots()\n",
- "ax.plot(lag,speed_autocorr)\n",
- "tau, K = curve_fit(exponential,lag,speed_autocorr)[0]\n",
- "print(\"best tau for speed is:\",tau)\n",
- "y_fit = exponential(lag,tau,K)\n",
- "ax.plot(lag,y_fit)\n",
- "ax.set_xlim(left=0,right=4)\n",
- "if save_plots == True: \n",
+ "ax.plot(lag, speed_autocorr)\n",
+ "tau, K = curve_fit(exponential, lag, speed_autocorr)[0]\n",
+ "print(\"best tau for speed is:\", tau)\n",
+ "y_fit = exponential(lag, tau, K)\n",
+ "ax.plot(lag, y_fit)\n",
+ "ax.set_xlim(left=0, right=4)\n",
+ "if save_plots == True:\n",
" tpl.xyAxes(ax)\n",
- " tpl.saveFigure(fig,'riab_speedac')\n",
+ " tpl.saveFigure(fig, \"riab_speedac\")\n",
"\n",
- "rot_vel = np.array(Ag_r.history['rot_vel'])\n",
- "rot_vel = (rot_vel - np.mean(rot_vel))/np.std(rot_vel)\n",
- "lag, rot_vel_autocorr = lagged_autocorrelation(t,rot_vel)\n",
+ "rot_vel = np.array(Ag_r.history[\"rot_vel\"])\n",
+ "rot_vel = (rot_vel - np.mean(rot_vel)) / np.std(rot_vel)\n",
+ "lag, rot_vel_autocorr = lagged_autocorrelation(t, rot_vel)\n",
"lag = lag[10:]\n",
"rot_vel_autocorr = rot_vel_autocorr[10:]\n",
"fig, ax = plt.subplots()\n",
- "ax.plot(lag,rot_vel_autocorr)\n",
- "tau, K = curve_fit(exponential,lag,rot_vel_autocorr)[0]\n",
- "print(\"best tau for rotational_vel is:\",tau)\n",
- "y_fit = exponential(lag,tau,K)\n",
- "ax.plot(lag,y_fit)\n",
+ "ax.plot(lag, rot_vel_autocorr)\n",
+ "tau, K = curve_fit(exponential, lag, rot_vel_autocorr)[0]\n",
+ "print(\"best tau for rotational_vel is:\", tau)\n",
+ "y_fit = exponential(lag, tau, K)\n",
+ "ax.plot(lag, y_fit)\n",
"ax.set_xlim(right=4)\n",
- "if save_plots == True: \n",
+ "if save_plots == True:\n",
" tpl.xyAxes(ax)\n",
- " tpl.saveFigure(fig,'riab_rotac')\n"
+ " tpl.saveFigure(fig, \"riab_rotac\")"
]
},
{
@@ -802,13 +808,23 @@
],
"source": [
"Env = Environment()\n",
- "Ag1 = Ag = Agent(Env,params={'thigmotaxis':0.8,})\n",
- "Ag2 = Ag = Agent(Env,params={'thigmotaxis':0.2,})\n",
+ "Ag1 = Ag = Agent(\n",
+ " Env,\n",
+ " params={\n",
+ " \"thigmotaxis\": 0.8,\n",
+ " },\n",
+ ")\n",
+ "Ag2 = Ag = Agent(\n",
+ " Env,\n",
+ " params={\n",
+ " \"thigmotaxis\": 0.2,\n",
+ " },\n",
+ ")\n",
"\n",
- "Ag1.dt=100e-3\n",
- "Ag2.dt=100e-3\n",
+ "Ag1.dt = 100e-3\n",
+ "Ag2.dt = 100e-3\n",
"\n",
- "for i in tqdm(range(int(90*60/Ag1.dt))):\n",
+ "for i in tqdm(range(int(90 * 60 / Ag1.dt))):\n",
" Ag1.update()\n",
" Ag2.update()"
]
@@ -861,14 +877,18 @@
],
"source": [
"fig, ax = Ag1.plot_position_heatmap()\n",
- "if save_plots == True: tpl.saveFigure(fig,'highthigmotaxis')\n",
+ "if save_plots == True:\n",
+ " tpl.saveFigure(fig, \"highthigmotaxis\")\n",
"fig, ax = Ag2.plot_position_heatmap()\n",
- "if save_plots == True: tpl.saveFigure(fig,'lowthigmotaxis')\n",
- "\n",
- "fig, ax = Ag1.plot_trajectory(t_end = 60*10,alpha=0.5)\n",
- "if save_plots == True: tpl.saveFigure(fig,'highthigmotaxis_traj')\n",
- "fig, ax = Ag2.plot_trajectory(t_end = 60*10,alpha=0.5)\n",
- "if save_plots == True: tpl.saveFigure(fig,'lowthigmotaxis_traj')"
+ "if save_plots == True:\n",
+ " tpl.saveFigure(fig, \"lowthigmotaxis\")\n",
+ "\n",
+ "fig, ax = Ag1.plot_trajectory(t_end=60 * 10, alpha=0.5)\n",
+ "if save_plots == True:\n",
+ " tpl.saveFigure(fig, \"highthigmotaxis_traj\")\n",
+ "fig, ax = Ag2.plot_trajectory(t_end=60 * 10, alpha=0.5)\n",
+ "if save_plots == True:\n",
+ " tpl.saveFigure(fig, \"lowthigmotaxis_traj\")"
]
},
{
@@ -932,56 +952,61 @@
}
],
"source": [
- "#import data\n",
- "from scipy import io \n",
- "mat = io.loadmat(\"../rawdata//8F6BE356-3277-475C-87B1-C7A977632DA7_1/11084-03020501_t2c1.mat\")\n",
- "x = ((mat['x1'] + mat['x2'])/2).reshape(-1)\n",
- "y = ((mat['y1'] + mat['y2'])/2).reshape(-1)\n",
- "t = (mat['t']).reshape(-1)\n",
- "#remove nans \n",
+ "# import data\n",
+ "from scipy import io\n",
+ "\n",
+ "mat = io.loadmat(\n",
+ " \"../rawdata//8F6BE356-3277-475C-87B1-C7A977632DA7_1/11084-03020501_t2c1.mat\"\n",
+ ")\n",
+ "x = ((mat[\"x1\"] + mat[\"x2\"]) / 2).reshape(-1)\n",
+ "y = ((mat[\"y1\"] + mat[\"y2\"]) / 2).reshape(-1)\n",
+ "t = (mat[\"t\"]).reshape(-1)\n",
+ "# remove nans\n",
"y = y[np.logical_not(np.isnan(x))]\n",
"t = t[np.logical_not(np.isnan(x))]\n",
"x = x[np.logical_not(np.isnan(x))]\n",
- "#normalise and put in metres\n",
- "x = (x-min(x))/100\n",
- "y = (y-min(y))/100\n",
- "x = x + 0.5*(1-max(x))\n",
- "y = y + 0.5*(1-max(y))\n",
- "#save_data\n",
- "pos = np.stack((x,y)).T\n",
+ "# normalise and put in metres\n",
+ "x = (x - min(x)) / 100\n",
+ "y = (y - min(y)) / 100\n",
+ "x = x + 0.5 * (1 - max(x))\n",
+ "y = y + 0.5 * (1 - max(y))\n",
+ "# save_data\n",
+ "pos = np.stack((x, y)).T\n",
"# np.savez(\"../ratinabox/data/sargolini.npz\",t=t,pos=pos) #(did this once but dont do it again)\n",
- "#data is 10 mins, we want 10 secs\n",
- "startid = np.argmin(np.abs(t-2)) #start at 2s\n",
- "endid = np.argmin(np.abs(t-2-25)) #end at 27s \n",
+ "# data is 10 mins, we want 10 secs\n",
+ "startid = np.argmin(np.abs(t - 2)) # start at 2s\n",
+ "endid = np.argmin(np.abs(t - 2 - 25)) # end at 27s\n",
"x = x[startid:endid]\n",
"y = y[startid:endid]\n",
"t = t[startid:endid]\n",
- "print(t[0],t[-1])\n",
- "#downsample (so my code will later smooth it) (currently at 50Hz --> 2.5Hz)\n",
- "print((t[1]-t[0])**-1)\n",
+ "print(t[0], t[-1])\n",
+ "# downsample (so my code will later smooth it) (currently at 50Hz --> 2.5Hz)\n",
+ "print((t[1] - t[0]) ** -1)\n",
"x_ds = x[::30]\n",
"y_ds = y[::30]\n",
"t_ds = t[::30]\n",
- "print((t_ds[1]-t_ds[0])**-1)\n",
- "#concatenate\n",
- "pos = np.stack((x,y)).T\n",
- "pos_ds = np.stack((x_ds,y_ds)).T\n",
+ "print((t_ds[1] - t_ds[0]) ** -1)\n",
+ "# concatenate\n",
+ "pos = np.stack((x, y)).T\n",
+ "pos_ds = np.stack((x_ds, y_ds)).T\n",
"\n",
"Env = Environment()\n",
"Ag1 = Agent(Env)\n",
"Ag2 = Agent(Env)\n",
- "Ag1.import_trajectory(times=t,positions=pos)\n",
- "Ag2.import_trajectory(times=t_ds,positions=pos_ds)\n",
+ "Ag1.import_trajectory(times=t, positions=pos)\n",
+ "Ag2.import_trajectory(times=t_ds, positions=pos_ds)\n",
"\n",
- "for i in tqdm(range(int(t_ds[-1]/Ag2.dt))):\n",
+ "for i in tqdm(range(int(t_ds[-1] / Ag2.dt))):\n",
" Ag1.update()\n",
" Ag2.update()\n",
"\n",
"fig, ax = Ag1.plot_trajectory()\n",
- "if save_plots == True: tpl.saveFigure(fig,'imported')\n",
+ "if save_plots == True:\n",
+ " tpl.saveFigure(fig, \"imported\")\n",
"fig, ax = Ag2.plot_trajectory()\n",
- "ax.scatter(x_ds,y_ds,c='C1',s=15,linewidth=1,zorder=11,alpha=0.7)\n",
- "if save_plots == True: tpl.saveFigure(fig,'upsampled')"
+ "ax.scatter(x_ds, y_ds, c=\"C1\", s=15, linewidth=1, zorder=11, alpha=0.7)\n",
+ "if save_plots == True:\n",
+ " tpl.saveFigure(fig, \"upsampled\")"
]
},
{
@@ -1010,23 +1035,17 @@
"Ag = Agent(Env)\n",
"\n",
"Ntest = 1000\n",
- "PCs = PlaceCells(Ag,\n",
- " params={'n':Ntest,\n",
- " 'color':'C1'\n",
- " }\n",
- ")\n",
+ "PCs = PlaceCells(Ag, params={\"n\": Ntest, \"color\": \"C1\"})\n",
"\n",
- "GCs = GridCells(Ag,\n",
- " params={'n':Ntest,\n",
- " 'color':'C2'\n",
- " }\n",
- ")\n",
+ "GCs = GridCells(Ag, params={\"n\": Ntest, \"color\": \"C2\"})\n",
"\n",
- "BVCs = BoundaryVectorCells(Ag,\n",
- " params={'n':Ntest,\n",
- " 'color':'C3',\n",
- " }\n",
- ")\n"
+ "BVCs = BoundaryVectorCells(\n",
+ " Ag,\n",
+ " params={\n",
+ " \"n\": Ntest,\n",
+ " \"color\": \"C3\",\n",
+ " },\n",
+ ")"
]
},
{
@@ -1043,7 +1062,7 @@
}
],
"source": [
- "import time \n",
+ "import time\n",
"\n",
"motion = []\n",
"pc = []\n",
@@ -1051,48 +1070,47 @@
"bvc = []\n",
"matmul = []\n",
"inverse = []\n",
- " \n",
+ "\n",
"for i in tqdm(range(100)):\n",
" t0 = time.time()\n",
" Ag.update()\n",
" t1 = time.time()\n",
- " motion.append(t1-t0)\n",
+ " motion.append(t1 - t0)\n",
"\n",
" t0 = time.time()\n",
" PCs.update()\n",
" t1 = time.time()\n",
- " pc.append(t1-t0)\n",
+ " pc.append(t1 - t0)\n",
"\n",
" t0 = time.time()\n",
" GCs.update()\n",
" t1 = time.time()\n",
- " gc.append(t1-t0)\n",
+ " gc.append(t1 - t0)\n",
"\n",
" t0 = time.time()\n",
" BVCs.update()\n",
" t1 = time.time()\n",
- " bvc.append(t1-t0)\n",
+ " bvc.append(t1 - t0)\n",
"\n",
" a = np.random.normal(size=(Ntest,))\n",
- " b = np.random.normal(size=(Ntest,Ntest))\n",
+ " b = np.random.normal(size=(Ntest, Ntest))\n",
" t0 = time.time()\n",
- " c = np.matmul(b,a)\n",
+ " c = np.matmul(b, a)\n",
" t1 = time.time()\n",
- " matmul.append(t1-t0)\n",
+ " matmul.append(t1 - t0)\n",
"\n",
- " a = np.random.normal(size=(Ntest,Ntest))\n",
+ " a = np.random.normal(size=(Ntest, Ntest))\n",
" t0 = time.time()\n",
" b = np.linalg.inv(a)\n",
" t1 = time.time()\n",
- " inverse.append(t1-t0)\n",
+ " inverse.append(t1 - t0)\n",
"\n",
"motion = np.array(motion)\n",
"pc = np.array(pc)\n",
"gc = np.array(gc)\n",
"bvc = np.array(bvc)\n",
"matmul = np.array(matmul)\n",
- "inverse = np.array(inverse)\n",
- "\n"
+ "inverse = np.array(inverse)"
]
},
{
@@ -1112,17 +1130,32 @@
}
],
"source": [
- "positions = [1,2,3,4,5.2,6.2]\n",
- "heights = [motion.mean(),pc.mean(),gc.mean(),bvc.mean(),matmul.mean(),inverse.mean()]\n",
- "uncertainties = [motion.std(),pc.std(),gc.std(),bvc.std(),matmul.std(),inverse.std()]\n",
- "color = ['C0','C0','C0','C0','C1','C1']\n",
+ "positions = [1, 2, 3, 4, 5.2, 6.2]\n",
+ "heights = [\n",
+ " motion.mean(),\n",
+ " pc.mean(),\n",
+ " gc.mean(),\n",
+ " bvc.mean(),\n",
+ " matmul.mean(),\n",
+ " inverse.mean(),\n",
+ "]\n",
+ "uncertainties = [\n",
+ " motion.std(),\n",
+ " pc.std(),\n",
+ " gc.std(),\n",
+ " bvc.std(),\n",
+ " matmul.std(),\n",
+ " inverse.std(),\n",
+ "]\n",
+ "color = [\"C0\", \"C0\", \"C0\", \"C0\", \"C1\", \"C1\"]\n",
"\n",
"fig, ax = plt.subplots()\n",
- "ax.bar(positions,heights,color=color,yerr=uncertainties,ecolor=color)\n",
- "ax.set_yscale('log')\n",
+ "ax.bar(positions, heights, color=color, yerr=uncertainties, ecolor=color)\n",
+ "ax.set_yscale(\"log\")\n",
"ax.set_ylim(bottom=1e-5)\n",
"ax.set_xticks([])\n",
- "if save_plots == True: tpl.saveFigure(fig,'clocktimes')\n"
+ "if save_plots == True:\n",
+ " tpl.saveFigure(fig, \"clocktimes\")"
]
},
{
@@ -1148,20 +1181,22 @@
],
"source": [
"Env = Environment()\n",
- "Env.add_wall(np.array([[0.3,0],[0.3,0.4]]))\n",
+ "Env.add_wall(np.array([[0.3, 0], [0.3, 0.4]]))\n",
"Ag = Agent(Env)\n",
"Ag.dt = 50e-3\n",
- "PCs = PlaceCells(Ag,params={'n':100})\n",
- "GCs = GridCells(Ag,params={'n':3,'color':None},)\n",
- "BVCs = BoundaryVectorCells(Ag,params={'n':3,'color':None})\n",
+ "PCs = PlaceCells(Ag, params={\"n\": 100})\n",
+ "GCs = GridCells(\n",
+ " Ag,\n",
+ " params={\"n\": 3, \"color\": None},\n",
+ ")\n",
+ "BVCs = BoundaryVectorCells(Ag, params={\"n\": 3, \"color\": None})\n",
"\n",
- "Env1D = Environment(params={'dimensionality':'1D'})\n",
- "Ag1D = Agent(Env1D,params={'speed_mean':0.0})\n",
+ "Env1D = Environment(params={\"dimensionality\": \"1D\"})\n",
+ "Ag1D = Agent(Env1D, params={\"speed_mean\": 0.0})\n",
"Ag1D.dt = 50e-3\n",
- "PCs1D = PlaceCells(Ag1D,params={'n':10,\n",
- " 'widths':0.2})\n",
+ "PCs1D = PlaceCells(Ag1D, params={\"n\": 10, \"widths\": 0.2})\n",
"\n",
- "for i in tqdm(range(int(3*60/Ag.dt))):\n",
+ "for i in tqdm(range(int(3 * 60 / Ag.dt))):\n",
" Ag.update()\n",
" PCs.update()\n",
" GCs.update()\n",
@@ -1418,59 +1453,58 @@
"fig8, ax8 = Ag.plot_histogram_of_rotational_velocities()\n",
"fig9, ax9 = GCs.plot_rate_map()\n",
"fig10, ax10 = PCs1D.plot_rate_map()\n",
- "fig11, ax11 = GCs.plot_rate_map(method='history')\n",
- "fig12, ax12 = PCs1D.plot_rate_map(method='history')\n",
- "fig13, ax13 = GCs.plot_rate_map(method='neither',spikes=True)\n",
- "fig14, ax14 = PCs1D.plot_rate_map(method='neither',spikes=True)\n",
+ "fig11, ax11 = GCs.plot_rate_map(method=\"history\")\n",
+ "fig12, ax12 = PCs1D.plot_rate_map(method=\"history\")\n",
+ "fig13, ax13 = GCs.plot_rate_map(method=\"neither\", spikes=True)\n",
+ "fig14, ax14 = PCs1D.plot_rate_map(method=\"neither\", spikes=True)\n",
"fig15, ax15 = GCs.plot_rate_timeseries(t_end=120)\n",
"fig16, ax16 = PCs.plot_place_cell_locations()\n",
"fig17, ax17 = BVCs.plot_BVC_receptive_field()\n",
"fig18, ax18 = BVCs.plot_rate_map(chosen_neurons=\"1\")\n",
- "fig18, ax18 = Ag.plot_trajectory(t_end=120,fig=fig18,ax=ax18[0])\n",
- "fig19, axes19 = plt.subplots(1,5,figsize=(20,4))\n",
- "fig20, ax20 = GCs.plot_rate_timeseries(t_end=120,imshow=True)\n",
- "Ag.plot_trajectory(fig=fig19,ax=axes19[0],t_end=30)\n",
- "BVCs.plot_rate_map(fig=fig19,ax=[axes19[1],axes19[2],axes19[3]],chosen_neurons='3') \n",
- "BVCs.plot_rate_timeseries(fig=fig19,ax=axes19[4],t_end=30) \n",
+ "fig18, ax18 = Ag.plot_trajectory(t_end=120, fig=fig18, ax=ax18[0])\n",
+ "fig19, axes19 = plt.subplots(1, 5, figsize=(20, 4))\n",
+ "fig20, ax20 = GCs.plot_rate_timeseries(t_end=120, imshow=True)\n",
+ "Ag.plot_trajectory(fig=fig19, ax=axes19[0], t_end=30)\n",
+ "BVCs.plot_rate_map(fig=fig19, ax=[axes19[1], axes19[2], axes19[3]], chosen_neurons=\"3\")\n",
+ "BVCs.plot_rate_timeseries(fig=fig19, ax=axes19[4], t_end=30)\n",
"\n",
"\n",
"anim = True\n",
"if anim == True:\n",
- " anim1 = Ag.animate_trajectory(t_end=60,speed_up=5)\n",
+ " anim1 = Ag.animate_trajectory(t_end=60, speed_up=5)\n",
" anim1.save(\"../figures/plotting_examples_save/trajectory_animation.gif\")\n",
- " anim2 = GCs.animate_rate_timeseries(t_end=60,speed_up=5)\n",
+ " anim2 = GCs.animate_rate_timeseries(t_end=60, speed_up=5)\n",
" anim2.save(\"../figures/plotting_examples_save/animate_rate_timeseries.gif\")\n",
"\n",
"\n",
- "if save_plots == True: \n",
+ "if save_plots == True:\n",
" tpl.figureDirectory = \"../figures/plotting_examples_save/\"\n",
- " \n",
- " tpl.saveFigure(fig1,\"plot_env\")\n",
- " tpl.saveFigure(fig2,\"plot_env_1D\")\n",
- " tpl.saveFigure(fig3,\"plot_traj\")\n",
- " tpl.saveFigure(fig4,\"plot_traj_1D\")\n",
- " tpl.saveFigure(fig5,\"plot_heatmap\")\n",
- " tpl.saveFigure(fig6,\"plot_heatmap_1D\")\n",
- " tpl.saveFigure(fig7,\"plot_histogram_speed\")\n",
- " tpl.saveFigure(fig8,\"plot_histogram_rotvel\")\n",
- " tpl.saveFigure(fig9,\"gc_plotrm\")\n",
- " tpl.saveFigure(fig10,\"pc1d_plotrm\")\n",
- " tpl.saveFigure(fig11,\"gc_plotrm_history\")\n",
- " tpl.saveFigure(fig12,\"pc1d_plotrm_history\")\n",
- " tpl.saveFigure(fig13,\"gc_plotrm_spikes\")\n",
- " tpl.saveFigure(fig14,\"pc1d_plotrm_spikes\")\n",
- " tpl.saveFigure(fig15,\"gc_plotrts\")\n",
- " tpl.saveFigure(fig16,\"pc_locations\")\n",
- " tpl.saveFigure(fig17,\"bvc_rfs\")\n",
- " tpl.saveFigure(fig18,\"trajectory_on_ratemap\")\n",
- " tpl.saveFigure(fig19,\"multipanel_riab\")\n",
- " tpl.saveFigure(fig19,\"multipanel_riab\")\n",
- " tpl.saveFigure(fig20,\"gcs_plotrts_imshow\")\n",
+ "\n",
+ " tpl.saveFigure(fig1, \"plot_env\")\n",
+ " tpl.saveFigure(fig2, \"plot_env_1D\")\n",
+ " tpl.saveFigure(fig3, \"plot_traj\")\n",
+ " tpl.saveFigure(fig4, \"plot_traj_1D\")\n",
+ " tpl.saveFigure(fig5, \"plot_heatmap\")\n",
+ " tpl.saveFigure(fig6, \"plot_heatmap_1D\")\n",
+ " tpl.saveFigure(fig7, \"plot_histogram_speed\")\n",
+ " tpl.saveFigure(fig8, \"plot_histogram_rotvel\")\n",
+ " tpl.saveFigure(fig9, \"gc_plotrm\")\n",
+ " tpl.saveFigure(fig10, \"pc1d_plotrm\")\n",
+ " tpl.saveFigure(fig11, \"gc_plotrm_history\")\n",
+ " tpl.saveFigure(fig12, \"pc1d_plotrm_history\")\n",
+ " tpl.saveFigure(fig13, \"gc_plotrm_spikes\")\n",
+ " tpl.saveFigure(fig14, \"pc1d_plotrm_spikes\")\n",
+ " tpl.saveFigure(fig15, \"gc_plotrts\")\n",
+ " tpl.saveFigure(fig16, \"pc_locations\")\n",
+ " tpl.saveFigure(fig17, \"bvc_rfs\")\n",
+ " tpl.saveFigure(fig18, \"trajectory_on_ratemap\")\n",
+ " tpl.saveFigure(fig19, \"multipanel_riab\")\n",
+ " tpl.saveFigure(fig19, \"multipanel_riab\")\n",
+ " tpl.saveFigure(fig20, \"gcs_plotrts_imshow\")\n",
"\n",
" # anim1.save(\"../figures/plotting_examples_save/trajectory_animation.gif\")\n",
"\n",
- " tpl.figureDirectory = \"../figures/\"\n",
- "\n"
+ " tpl.figureDirectory = \"../figures/\""
]
},
{
diff --git a/demos/path_integration_example.ipynb b/demos/path_integration_example.ipynb
index 335b8612..43ac0884 100644
--- a/demos/path_integration_example.ipynb
+++ b/demos/path_integration_example.ipynb
@@ -81,7 +81,7 @@
}
],
"source": [
- "#Import ratinabox\n",
+ "# Import ratinabox\n",
"import ratinabox\n",
"from ratinabox.Environment import Environment\n",
"from ratinabox.Agent import Agent\n",
@@ -90,7 +90,7 @@
"\n",
"import numpy as np\n",
"import matplotlib\n",
- "import matplotlib.pyplot as plt \n",
+ "import matplotlib.pyplot as plt\n",
"\n",
"%load_ext autoreload\n",
"%autoreload 2\n",
@@ -104,10 +104,11 @@
"metadata": {},
"outputs": [],
"source": [
- "#Leave this as False. \n",
- "#For paper/readme production I use a plotting library (tomplotlib) to format and save figures. Without this they will still show but not save. \n",
- "if False: \n",
+ "# Leave this as False.\n",
+ "# For paper/readme production I use a plotting library (tomplotlib) to format and save figures. Without this they will still show but not save.\n",
+ "if False:\n",
" import tomplotlib.tomplotlib as tpl\n",
+ "\n",
" tpl.figureDirectory = \"../figures/\"\n",
" tpl.setColorscheme(colorscheme=2)\n",
" save_plots = True\n",
@@ -130,11 +131,11 @@
"source": [
"class PyramidalNeurons(Neurons):\n",
" \"\"\"The PyramidalNeuorn class defines a layer of Neurons() whos firing rates are derived from the firing rates in two DendriticCompartments. They are theta modulated, during early theta phase the apical DendriticCompartment (self.apical_compartment) drives the soma, during late theta phases the basal DendriticCompartment (self.basal_compartment) drives the soma.\n",
- " \n",
- " Must be initialised with an Agent and a 'params' dictionary. \n",
"\n",
- " Check that the input layers are all named differently. \n",
- " List of functions: \n",
+ " Must be initialised with an Agent and a 'params' dictionary.\n",
+ "\n",
+ " Check that the input layers are all named differently.\n",
+ " List of functions:\n",
" • get_state()\n",
" • update()\n",
" • update_dendritic_compartments()\n",
@@ -142,154 +143,166 @@
" • plot_loss()\n",
" • plot_rate_map()\n",
" \"\"\"\n",
- " def __init__(self,Agent,params={}):\n",
+ "\n",
+ " def __init__(self, Agent, params={}):\n",
" \"\"\"Initialises a layer of pyramidal neurons\n",
"\n",
" Args:\n",
" Agent (_type_): _description_\n",
" params (dict, optional): _description_. Defaults to {}.\n",
- " \"\"\" \n",
+ " \"\"\"\n",
" default_params = {\n",
- " 'n':10,\n",
- " 'name':'PyramidalNeurons',\n",
- " #theta params \n",
- " 'theta_freq':5,\n",
- " 'theta_frac':0.5, #-->0 all basal input, -->1 all apical input\n",
+ " \"n\": 10,\n",
+ " \"name\": \"PyramidalNeurons\",\n",
+ " # theta params\n",
+ " \"theta_freq\": 5,\n",
+ " \"theta_frac\": 0.5, # -->0 all basal input, -->1 all apical input\n",
" }\n",
" default_params.update(params)\n",
" self.params = default_params\n",
" super().__init__(Agent, self.params)\n",
"\n",
- " self.history['loss']=[]\n",
- " self.error=None\n",
- " \n",
- " self.basal_compartment = DendriticCompartment(self.Agent,\n",
- " params={\n",
- " 'soma':self,\n",
- " 'name':f\"{self.name}_basal\",\n",
- " 'n':self.n,\n",
- " 'color':self.color,\n",
- " })\n",
- " self.apical_compartment = DendriticCompartment(self.Agent,\n",
- " params={\n",
- " 'soma':self,\n",
- " 'name':f\"{self.name}_apical\",\n",
- " 'n':self.n,\n",
- " 'color':self.color\n",
- " })\n",
+ " self.history[\"loss\"] = []\n",
+ " self.error = None\n",
+ "\n",
+ " self.basal_compartment = DendriticCompartment(\n",
+ " self.Agent,\n",
+ " params={\n",
+ " \"soma\": self,\n",
+ " \"name\": f\"{self.name}_basal\",\n",
+ " \"n\": self.n,\n",
+ " \"color\": self.color,\n",
+ " },\n",
+ " )\n",
+ " self.apical_compartment = DendriticCompartment(\n",
+ " self.Agent,\n",
+ " params={\n",
+ " \"soma\": self,\n",
+ " \"name\": f\"{self.name}_apical\",\n",
+ " \"n\": self.n,\n",
+ " \"color\": self.color,\n",
+ " },\n",
+ " )\n",
"\n",
" def update(self):\n",
- " \"\"\"Updates the firing rate of the layer. Saves a loss (lpf difference between basal and apical). Also adds noise.\n",
- " \"\"\" \n",
- " super().update() #this sets and saves self.firingrate \n",
- "\n",
- " dt = self.Agent.dt \n",
- " tau_smooth = 10 \n",
- " #update a smoothed history of the loss\n",
- " fr_b, fr_a = self.basal_compartment.firingrate, self.apical_compartment.firingrate\n",
+ " \"\"\"Updates the firing rate of the layer. Saves a loss (lpf difference between basal and apical). Also adds noise.\"\"\"\n",
+ " super().update() # this sets and saves self.firingrate\n",
+ "\n",
+ " dt = self.Agent.dt\n",
+ " tau_smooth = 10\n",
+ " # update a smoothed history of the loss\n",
+ " fr_b, fr_a = (\n",
+ " self.basal_compartment.firingrate,\n",
+ " self.apical_compartment.firingrate,\n",
+ " )\n",
" error = np.mean(np.abs(fr_b - fr_a))\n",
- " if self.Agent.t < 2/self.theta_freq:\n",
+ " if self.Agent.t < 2 / self.theta_freq:\n",
" self.error = None\n",
" else:\n",
" # loss_smoothing_timescale = dt\n",
- " self.error = (dt / tau_smooth) * error + (\n",
- " 1 - dt / tau_smooth\n",
- " ) * (self.error or error) \n",
+ " self.error = (dt / tau_smooth) * error + (1 - dt / tau_smooth) * (\n",
+ " self.error or error\n",
+ " )\n",
" self.history[\"loss\"].append(self.error)\n",
- " return \n",
+ " return\n",
"\n",
" def update_dendritic_compartments(self):\n",
- " \"\"\"Individually updates teh basal and apical firing rates.\n",
- " \"\"\" \n",
+ " \"\"\"Individually updates teh basal and apical firing rates.\"\"\"\n",
" self.basal_compartment.update()\n",
" self.apical_compartment.update()\n",
" return\n",
"\n",
" def get_state(self, evaluate_at=\"last\", **kwargs):\n",
- " \"\"\"Returns the firing rate of the soma. This depends on the firing rates of the basal and apical compartments and the current theta phase. By default the theta is obtained from self.Agent.t but it can be passed manually as an kwarg to override this. \n",
+ " \"\"\"Returns the firing rate of the soma. This depends on the firing rates of the basal and apical compartments and the current theta phase. By default the theta is obtained from self.Agent.t but it can be passed manually as an kwarg to override this.\n",
"\n",
- " theta (or theta_gating) is a number between [0,1] controlling flow of information into soma from the two compartment.s 0 = entirely basal. 1 = entirely apical. Between equals weighted combination. he function theta_gating() takes a time and returns theta. \n",
+ " theta (or theta_gating) is a number between [0,1] controlling flow of information into soma from the two compartment.s 0 = entirely basal. 1 = entirely apical. Between equals weighted combination. he function theta_gating() takes a time and returns theta.\n",
" Args:\n",
" evaluate_at (str, optional): 'last','agent','all' or None (in which case pos can be passed directly as a kwarg). Defaults to \"last\".\n",
" Returns:\n",
" firingrate\n",
- " \"\"\" \n",
- " #theta can be passed in manually as a kwarg. If it isn't ithe time from the agent will be used to get theta. Theta determines how much basal and how much apical this neurons uses. \n",
- " if 'theta' in kwargs:\n",
- " theta = kwargs['theta']\n",
- " else: \n",
- " theta = theta_gating(t = self.Agent.t,\n",
- " freq=self.theta_freq,\n",
- " frac=self.theta_frac) \n",
+ " \"\"\"\n",
+ " # theta can be passed in manually as a kwarg. If it isn't ithe time from the agent will be used to get theta. Theta determines how much basal and how much apical this neurons uses.\n",
+ " if \"theta\" in kwargs:\n",
+ " theta = kwargs[\"theta\"]\n",
+ " else:\n",
+ " theta = theta_gating(\n",
+ " t=self.Agent.t, freq=self.theta_freq, frac=self.theta_frac\n",
+ " )\n",
" fr_basal, fr_apical = 0, 0\n",
- " #these are special cases, no need to even get their fr's if they aren't used\n",
- " if theta != 0: fr_apical = self.apical_compartment.get_state(evaluate_at, **kwargs)\n",
- " if theta != 1: fr_basal = self.basal_compartment.get_state(evaluate_at, **kwargs)\n",
- " firingrate = (1-theta)*fr_basal + (theta)*fr_apical\n",
+ " # these are special cases, no need to even get their fr's if they aren't used\n",
+ " if theta != 0:\n",
+ " fr_apical = self.apical_compartment.get_state(evaluate_at, **kwargs)\n",
+ " if theta != 1:\n",
+ " fr_basal = self.basal_compartment.get_state(evaluate_at, **kwargs)\n",
+ " firingrate = (1 - theta) * fr_basal + (theta) * fr_apical\n",
" return firingrate\n",
- " \n",
+ "\n",
" def update_weights(self):\n",
- " \"\"\"Trains the weights, this function actually defined in the dendrite class.\n",
- " \"\"\" \n",
- " if self.Agent.t > 2/self.theta_freq:\n",
+ " \"\"\"Trains the weights, this function actually defined in the dendrite class.\"\"\"\n",
+ " if self.Agent.t > 2 / self.theta_freq:\n",
" self.basal_compartment.update_weights()\n",
" self.apical_compartment.update_weights()\n",
- " return \n",
+ " return\n",
"\n",
" def plot_loss(self, fig=None, ax=None):\n",
- " \"\"\"Plots the loss against time to see if learning working\n",
- " \"\"\" \n",
- " if fig is None and ax is None: \n",
+ " \"\"\"Plots the loss against time to see if learning working\"\"\"\n",
+ " if fig is None and ax is None:\n",
" fig, ax = plt.subplots(figsize=(1.5, 1.5))\n",
- " ylim=0\n",
- " else: ylim = ax.get_ylim()[1]\n",
+ " ylim = 0\n",
+ " else:\n",
+ " ylim = ax.get_ylim()[1]\n",
" t = np.array(self.history[\"t\"]) / 60\n",
" loss = self.history[\"loss\"]\n",
" ax.plot(t, loss, color=self.color, label=self.name)\n",
- " ax.set_ylim(bottom=0, top=max(ylim, np.nanmax(np.array(loss, dtype=np.float64))))\n",
+ " ax.set_ylim(\n",
+ " bottom=0, top=max(ylim, np.nanmax(np.array(loss, dtype=np.float64)))\n",
+ " )\n",
" ax.set_xlim(left=0)\n",
" ax.legend(frameon=False)\n",
" ax.set_xlabel(\"Training time / min\")\n",
" ax.set_ylabel(\"Loss\")\n",
" return fig, ax\n",
- " \n",
- " def plot_rate_map(self,route='basal',**kwargs):\n",
- " \"\"\"This is a wrapper function for the general Neuron class function plot_rate_map. It takes the same arguments as Neurons.plot_rate_map() but, in addition, route can be set to basal or apical in which case theta is set correspondingly and teh soma with take its input from downstream or upstream sources entirely. \n",
"\n",
- " The arguments for the standard plottiong function plot_rate_map() can be passed as usual as kwargs. \n",
+ " def plot_rate_map(self, route=\"basal\", **kwargs):\n",
+ " \"\"\"This is a wrapper function for the general Neuron class function plot_rate_map. It takes the same arguments as Neurons.plot_rate_map() but, in addition, route can be set to basal or apical in which case theta is set correspondingly and teh soma with take its input from downstream or upstream sources entirely.\n",
+ "\n",
+ " The arguments for the standard plottiong function plot_rate_map() can be passed as usual as kwargs.\n",
"\n",
" Args:\n",
" route (str, optional): _description_. Defaults to 'basal'.\n",
- " \"\"\" \n",
- " if route=='basal':theta=0\n",
- " elif route=='apical':theta=1\n",
- " fig, ax = super().plot_rate_map(**kwargs,theta=theta)\n",
+ " \"\"\"\n",
+ " if route == \"basal\":\n",
+ " theta = 0\n",
+ " elif route == \"apical\":\n",
+ " theta = 1\n",
+ " fig, ax = super().plot_rate_map(**kwargs, theta=theta)\n",
" return fig, ax\n",
- " \n",
+ "\n",
"\n",
"class DendriticCompartment(Neurons):\n",
- " \"\"\"The DendriticCompartment class defines a layer of Neurons() whos firing rates are an activated linear combination of input layers. This class is a subclass of Neurons() and inherits it properties/plotting functions. \n",
+ " \"\"\"The DendriticCompartment class defines a layer of Neurons() whos firing rates are an activated linear combination of input layers. This class is a subclass of Neurons() and inherits it properties/plotting functions.\n",
"\n",
- " Must be initialised with an Agent and a 'params' dictionary. \n",
- " Input params dictionary must contain a list of input_layers which feed into these Neurons. This list looks like [Neurons1, Neurons2,...] where each is a Neurons() class. \n",
+ " Must be initialised with an Agent and a 'params' dictionary.\n",
+ " Input params dictionary must contain a list of input_layers which feed into these Neurons. This list looks like [Neurons1, Neurons2,...] where each is a Neurons() class.\n",
"\n",
- " Currently supported activations include 'sigmoid' (paramterised by max_fr, min_fr, mid_x, width), 'relu' (gain, threshold) and 'linear' specified with the \"activation_params\" dictionary in the inout params dictionary. See also activate() for full details. \n",
+ " Currently supported activations include 'sigmoid' (paramterised by max_fr, min_fr, mid_x, width), 'relu' (gain, threshold) and 'linear' specified with the \"activation_params\" dictionary in the inout params dictionary. See also activate() for full details.\n",
"\n",
- " Check that the input layers are all named differently. \n",
- " List of functions: \n",
+ " Check that the input layers are all named differently.\n",
+ " List of functions:\n",
" • get_state()\n",
" • add_input()\n",
" \"\"\"\n",
"\n",
" def __init__(self, Agent, params={}):\n",
" default_params = {\n",
- " \"soma\":None,\n",
+ " \"soma\": None,\n",
" \"activation_params\": {\n",
" \"activation\": \"sigmoid\",\n",
" \"max_fr\": 1,\n",
" \"min_fr\": 0,\n",
" \"mid_x\": 1,\n",
- " \"width_x\": 2,},\n",
+ " \"width_x\": 2,\n",
+ " },\n",
" }\n",
" self.Agent = Agent\n",
" default_params.update(params)\n",
@@ -300,28 +313,22 @@
" self.firingrate_prime_temp = None\n",
" self.inputs = {}\n",
"\n",
- " def add_input(self, \n",
- " input_layer,\n",
- " eta = 0.001,\n",
- " w_init = 0.1,\n",
- " L1 = 0.0001,\n",
- " L2 = 0.001,\n",
- " tau_PI = 100e-3):\n",
- " \"\"\"Adds an input layer to the class. Each input layer is stored in a dictionary of self.inputs. Each has an associated matrix of weights which are initialised randomly. \n",
+ " def add_input(\n",
+ " self, input_layer, eta=0.001, w_init=0.1, L1=0.0001, L2=0.001, tau_PI=100e-3\n",
+ " ):\n",
+ " \"\"\"Adds an input layer to the class. Each input layer is stored in a dictionary of self.inputs. Each has an associated matrix of weights which are initialised randomly.\n",
"\n",
" Args:\n",
" input_layer (_type_): the layer which feeds into this compartment\n",
- " eta: learning rate of the weights \n",
- " w_init: initialisation scale of the weights \n",
+ " eta: learning rate of the weights\n",
+ " w_init: initialisation scale of the weights\n",
" L1: how much L1 regularisation\n",
" L2: how much L2 regularisation\n",
" tau_PI: smoothing timescale of plasticity induction variable\n",
" \"\"\"\n",
" name = input_layer.name\n",
" n_in = input_layer.n\n",
- " w = np.random.normal(\n",
- " loc=0, scale=w_init / np.sqrt(n_in), size=(self.n, n_in)\n",
- " )\n",
+ " w = np.random.normal(loc=0, scale=w_init / np.sqrt(n_in), size=(self.n, n_in))\n",
" I = np.zeros(n_in)\n",
" PI = np.zeros(n_in)\n",
" if name in self.inputs.keys():\n",
@@ -332,29 +339,31 @@
" self.inputs[name][\"layer\"] = input_layer\n",
" self.inputs[name][\"w\"] = w\n",
" self.inputs[name][\"w_init\"] = w.copy()\n",
- " self.inputs[name][\"I\"] = I #input current\n",
- " self.inputs[name][\"I_temp\"] = None #input current\n",
- " self.inputs[name][\"PI\"] = PI #plasticity induction variable\n",
- " self.inputs[name][\"eta\"] = eta \n",
- " self.inputs[name][\"L2\"] = L2 \n",
- " self.inputs[name][\"L1\"] = L1 \n",
+ " self.inputs[name][\"I\"] = I # input current\n",
+ " self.inputs[name][\"I_temp\"] = None # input current\n",
+ " self.inputs[name][\"PI\"] = PI # plasticity induction variable\n",
+ " self.inputs[name][\"eta\"] = eta\n",
+ " self.inputs[name][\"L2\"] = L2\n",
+ " self.inputs[name][\"L1\"] = L1\n",
" self.inputs[name][\"tau_PI\"] = tau_PI\n",
"\n",
" def get_state(self, evaluate_at=\"last\", **kwargs):\n",
- " \"\"\"Returns the \"firing rate\" of the dendritic compartment. By default this layer uses the last saved firingrate from its input layers. Alternatively evaluate_at and kwargs can be set to be anything else which will just be passed to the input layer for evaluation. \n",
+ " \"\"\"Returns the \"firing rate\" of the dendritic compartment. By default this layer uses the last saved firingrate from its input layers. Alternatively evaluate_at and kwargs can be set to be anything else which will just be passed to the input layer for evaluation.\n",
" Once the firing rate of the inout layers is established these are multiplied by the weight matrices and then activated to obtain the firing rate of this FeedForwardLayer.\n",
"\n",
" Args:\n",
" evaluate_at (str, optional). Defaults to 'last'.\n",
" Returns:\n",
- " firingrate: array of firing rates \n",
+ " firingrate: array of firing rates\n",
" \"\"\"\n",
- " if evaluate_at == 'last':\n",
+ " if evaluate_at == \"last\":\n",
" V = np.zeros(self.n)\n",
- " elif evaluate_at == 'all': \n",
- " V = np.zeros((self.n,self.Agent.Environment.flattened_discrete_coords.shape[0]))\n",
+ " elif evaluate_at == \"all\":\n",
+ " V = np.zeros(\n",
+ " (self.n, self.Agent.Environment.flattened_discrete_coords.shape[0])\n",
+ " )\n",
" else:\n",
- " V = np.zeros((self.n,kwargs['pos'].shape[0]))\n",
+ " V = np.zeros((self.n, kwargs[\"pos\"].shape[0]))\n",
"\n",
" for inputlayer in self.inputs.values():\n",
" w = inputlayer[\"w\"]\n",
@@ -362,10 +371,12 @@
" I = inputlayer[\"layer\"].firingrate\n",
" else: # kick can down the road let input layer decide how to evaluate the firingrate\n",
" I = inputlayer[\"layer\"].get_state(evaluate_at, **kwargs)\n",
- " inputlayer['I_temp'] = I\n",
+ " inputlayer[\"I_temp\"] = I\n",
" V += np.matmul(w, I)\n",
" firingrate = utils.activate(V, other_args=self.activation_params)\n",
- " firingrate_prime = utils.activate(V, other_args=self.activation_params, deriv=True) \n",
+ " firingrate_prime = utils.activate(\n",
+ " V, other_args=self.activation_params, deriv=True\n",
+ " )\n",
"\n",
" self.firingrate_temp = firingrate\n",
" self.firingrate_prime_temp = firingrate_prime\n",
@@ -373,49 +384,47 @@
" return firingrate\n",
"\n",
" def update(self):\n",
- " \"\"\"Updates firingrate of this compartment and saves it to file\n",
- " \"\"\" \n",
+ " \"\"\"Updates firingrate of this compartment and saves it to file\"\"\"\n",
" self.get_state()\n",
" self.firingrate = self.firingrate_temp.reshape(-1)\n",
" self.firingrate_deriv = self.firingrate_prime_temp.reshape(-1)\n",
" for inputlayer in self.inputs.values():\n",
- " inputlayer['I'] = inputlayer['I_temp'].reshape(-1)\n",
+ " inputlayer[\"I\"] = inputlayer[\"I_temp\"].reshape(-1)\n",
" self.save_to_history()\n",
" return\n",
- " \n",
+ "\n",
" def update_weights(self):\n",
- " \"\"\"Implements the weight update: dendritic prediction of somatic activity. \n",
- " \"\"\" \n",
- " target = self.soma.firingrate \n",
+ " \"\"\"Implements the weight update: dendritic prediction of somatic activity.\"\"\"\n",
+ " target = self.soma.firingrate\n",
" delta = (target - self.firingrate) * (self.firingrate_deriv)\n",
" dt = self.Agent.dt\n",
" for inputlayer in self.inputs.values():\n",
- " eta = inputlayer['eta']\n",
- " if eta != 0: \n",
- " tau_PI = inputlayer['tau_PI']\n",
+ " eta = inputlayer[\"eta\"]\n",
+ " if eta != 0:\n",
+ " tau_PI = inputlayer[\"tau_PI\"]\n",
" assert (dt / tau_PI) < 0.2\n",
- " I = inputlayer['I']\n",
- " w = inputlayer['w']\n",
- " #first updates plasticity induction variable (smoothed delta error outer product with the input current for this input layer)\n",
- " PI_old = inputlayer['PI']\n",
+ " I = inputlayer[\"I\"]\n",
+ " w = inputlayer[\"w\"]\n",
+ " # first updates plasticity induction variable (smoothed delta error outer product with the input current for this input layer)\n",
+ " PI_old = inputlayer[\"PI\"]\n",
" PI_update = np.outer(delta, I)\n",
- " PI_new = (dt / tau_PI) * PI_update + (\n",
- " 1 - dt / tau_PI) * PI_old\n",
- " inputlayer['PI'] = PI_new\n",
- " #updates weights\n",
- " dw = eta * (PI_new - inputlayer['L2']*w - inputlayer['L1']*np.sign(w)) \n",
- " inputlayer['w'] = w + dw\n",
+ " PI_new = (dt / tau_PI) * PI_update + (1 - dt / tau_PI) * PI_old\n",
+ " inputlayer[\"PI\"] = PI_new\n",
+ " # updates weights\n",
+ " dw = eta * (\n",
+ " PI_new - inputlayer[\"L2\"] * w - inputlayer[\"L1\"] * np.sign(w)\n",
+ " )\n",
+ " inputlayer[\"w\"] = w + dw\n",
" return\n",
"\n",
- "def theta_gating(t,\n",
- " freq=10,\n",
- " frac=0.5):\n",
- " T = 1/freq\n",
- " phase = ((t/T) % 1) % 1\n",
- " if phase < frac:\n",
- " return 1\n",
- " elif phase >= frac:\n",
- " return 0"
+ "\n",
+ "def theta_gating(t, freq=10, frac=0.5):\n",
+ " T = 1 / freq\n",
+ " phase = ((t / T) % 1) % 1\n",
+ " if phase < frac:\n",
+ " return 1\n",
+ " elif phase >= frac:\n",
+ " return 0"
]
},
{
@@ -434,69 +443,77 @@
"metadata": {},
"outputs": [],
"source": [
- "#Initialise the 1D environment \n",
- "Env = Environment(params={'dimensionality':'1D',\n",
- " 'boundary_conditions':'periodic'})\n",
+ "# Initialise the 1D environment\n",
+ "Env = Environment(params={\"dimensionality\": \"1D\", \"boundary_conditions\": \"periodic\"})\n",
"\n",
- "#Put agent (who will move randomly under the ratinabox Ornstein Uhlenbeck random motion policy) inside the environement\n",
+ "# Put agent (who will move randomly under the ratinabox Ornstein Uhlenbeck random motion policy) inside the environement\n",
"Ag = Agent(Env)\n",
"Ag.speed_mean = 0\n",
- "Ag.speed_std=0.3\n",
+ "Ag.speed_std = 0.3\n",
"\n",
"n_cells = 50\n",
- "#Place cells provide the target signal \n",
- "PlaceCells_ = PlaceCells(Ag, params={'n':n_cells,\n",
- " 'widths':0.1,\n",
- " 'name':'PlaceCells'})\n",
- "\n",
- "#The key neuron class: Ring attractor at the centre of the network made from our bespoke, custom-define PyramidalNeurons class. \n",
- "RingAttractor = PyramidalNeurons(Ag,params={'n':n_cells,\n",
- " 'name':'RingAttractor'})\n",
- "\n",
- "#Velocity cells encode agent velocity\n",
- "VelocityCells_ = VelocityCells(Ag,params={'name':'VelocityCells'})\n",
- "\n",
- "#Conjuctive cells \n",
- "ConjunctiveCells_left = FeedForwardLayer(Ag,\n",
- " params={'n':n_cells,\n",
- " 'name':'ConjunctiveCells_left',\n",
- " })\n",
- "\n",
- "ConjunctiveCells_right = FeedForwardLayer(Ag,\n",
- " params={'n':n_cells,\n",
- " 'name':'ConjunctiveCells_right',\n",
- " })\n",
- "\n",
- "#Set inputs into ring attractor compartments\n",
- "#Make their activation functions linear \n",
- "#Set the fixed weights from place celles to Ring attractor to be fixed \n",
+ "# Place cells provide the target signal\n",
+ "PlaceCells_ = PlaceCells(Ag, params={\"n\": n_cells, \"widths\": 0.1, \"name\": \"PlaceCells\"})\n",
+ "\n",
+ "# The key neuron class: Ring attractor at the centre of the network made from our bespoke, custom-define PyramidalNeurons class.\n",
+ "RingAttractor = PyramidalNeurons(Ag, params={\"n\": n_cells, \"name\": \"RingAttractor\"})\n",
+ "\n",
+ "# Velocity cells encode agent velocity\n",
+ "VelocityCells_ = VelocityCells(Ag, params={\"name\": \"VelocityCells\"})\n",
+ "\n",
+ "# Conjuctive cells\n",
+ "ConjunctiveCells_left = FeedForwardLayer(\n",
+ " Ag,\n",
+ " params={\n",
+ " \"n\": n_cells,\n",
+ " \"name\": \"ConjunctiveCells_left\",\n",
+ " },\n",
+ ")\n",
+ "\n",
+ "ConjunctiveCells_right = FeedForwardLayer(\n",
+ " Ag,\n",
+ " params={\n",
+ " \"n\": n_cells,\n",
+ " \"name\": \"ConjunctiveCells_right\",\n",
+ " },\n",
+ ")\n",
+ "\n",
+ "# Set inputs into ring attractor compartments\n",
+ "# Make their activation functions linear\n",
+ "# Set the fixed weights from place celles to Ring attractor to be fixed\n",
"RingAttractor.apical_compartment.add_input(RingAttractor)\n",
"RingAttractor.apical_compartment.add_input(ConjunctiveCells_left)\n",
"RingAttractor.apical_compartment.add_input(ConjunctiveCells_right)\n",
"RingAttractor.apical_compartment.activation_params = {\"activation\": \"linear\"}\n",
"\n",
- "RingAttractor.basal_compartment.add_input(PlaceCells_,eta=0) #eta=0, these are fixed \n",
- "RingAttractor.basal_compartment.inputs['PlaceCells']['w'] = np.identity(n_cells)\n",
+ "RingAttractor.basal_compartment.add_input(PlaceCells_, eta=0) # eta=0, these are fixed\n",
+ "RingAttractor.basal_compartment.inputs[\"PlaceCells\"][\"w\"] = np.identity(n_cells)\n",
"RingAttractor.basal_compartment.activation_params = {\"activation\": \"linear\"}\n",
"\n",
- "#Set inputs into the conjuctive cells\n",
- "#Set the (fixed) weights into the conjunctive cells to be their correct values (identity or just 1's)\n",
+ "# Set inputs into the conjuctive cells\n",
+ "# Set the (fixed) weights into the conjunctive cells to be their correct values (identity or just 1's)\n",
"ConjunctiveCells_left.add_input(VelocityCells_)\n",
"ConjunctiveCells_left.add_input(RingAttractor)\n",
"ConjunctiveCells_right.add_input(VelocityCells_)\n",
"ConjunctiveCells_right.add_input(RingAttractor)\n",
- "ConjunctiveCells_left.inputs['VelocityCells']['w'] = np.ones((n_cells,2)) * np.array([1,-1]) #thus left velocity excites these cells and right velocity shuts them off\n",
- "ConjunctiveCells_right.inputs['VelocityCells']['w'] = np.ones((n_cells,2)) * np.array([-1,1])#thus right velocity excites these cells and rigleftht velocity shuts them off\n",
- "ConjunctiveCells_left.inputs['RingAttractor']['w'] = np.identity(n_cells)\n",
- "ConjunctiveCells_right.inputs['RingAttractor']['w'] = np.identity(n_cells)\n",
- "ConjunctiveCells_left.activation_params={\n",
- " \"activation\": \"relu\",\n",
- " \"threshold\": 1,\n",
- " \"width_x\": 2}\n",
- "ConjunctiveCells_right.activation_params={\n",
- " \"activation\": \"relu\",\n",
- " \"threshold\": 1,\n",
- " \"width_x\": 2}"
+ "ConjunctiveCells_left.inputs[\"VelocityCells\"][\"w\"] = np.ones((n_cells, 2)) * np.array(\n",
+ " [1, -1]\n",
+ ") # thus left velocity excites these cells and right velocity shuts them off\n",
+ "ConjunctiveCells_right.inputs[\"VelocityCells\"][\"w\"] = np.ones((n_cells, 2)) * np.array(\n",
+ " [-1, 1]\n",
+ ") # thus right velocity excites these cells and rigleftht velocity shuts them off\n",
+ "ConjunctiveCells_left.inputs[\"RingAttractor\"][\"w\"] = np.identity(n_cells)\n",
+ "ConjunctiveCells_right.inputs[\"RingAttractor\"][\"w\"] = np.identity(n_cells)\n",
+ "ConjunctiveCells_left.activation_params = {\n",
+ " \"activation\": \"relu\",\n",
+ " \"threshold\": 1,\n",
+ " \"width_x\": 2,\n",
+ "}\n",
+ "ConjunctiveCells_right.activation_params = {\n",
+ " \"activation\": \"relu\",\n",
+ " \"threshold\": 1,\n",
+ " \"width_x\": 2,\n",
+ "}"
]
},
{
@@ -522,18 +539,18 @@
}
],
"source": [
- "for i in tqdm(range(int(10*60/Ag.dt))):\n",
- " #update agent\n",
+ "for i in tqdm(range(int(10 * 60 / Ag.dt))):\n",
+ " # update agent\n",
" Ag.update()\n",
- " #update firing rates of all the cell layers\n",
+ " # update firing rates of all the cell layers\n",
" PlaceCells_.update()\n",
" VelocityCells_.update()\n",
" ConjunctiveCells_left.update()\n",
" ConjunctiveCells_right.update()\n",
" RingAttractor.update_dendritic_compartments()\n",
" RingAttractor.update()\n",
- " #finally, update the weights\n",
- " RingAttractor.update_weights()\n"
+ " # finally, update the weights\n",
+ " RingAttractor.update_weights()"
]
},
{
@@ -566,8 +583,8 @@
"source": [
"fig, ax = RingAttractor.plot_loss()\n",
"\n",
- "if save_plots == True: \n",
- " tpl.saveFigure(fig,\"PI_loss\")"
+ "if save_plots == True:\n",
+ " tpl.saveFigure(fig, \"PI_loss\")"
]
},
{
@@ -601,18 +618,18 @@
}
],
"source": [
- "#pull out the weight as they were at initialisation \n",
- "w_ccl_init = RingAttractor.apical_compartment.inputs['ConjunctiveCells_left']['w_init']\n",
- "w_ccr_init = RingAttractor.apical_compartment.inputs['ConjunctiveCells_right']['w_init']\n",
- "w_rec_init = RingAttractor.apical_compartment.inputs['RingAttractor']['w_init']\n",
- "\n",
- "#pull out the weights after training\n",
- "w_ccl = RingAttractor.apical_compartment.inputs['ConjunctiveCells_left']['w']\n",
- "w_ccr = RingAttractor.apical_compartment.inputs['ConjunctiveCells_right']['w']\n",
- "w_rec = RingAttractor.apical_compartment.inputs['RingAttractor']['w']\n",
- "\n",
- "#plot them \n",
- "fig, ax = plt.subplots(1,3,figsize=(12,4))\n",
+ "# pull out the weight as they were at initialisation\n",
+ "w_ccl_init = RingAttractor.apical_compartment.inputs[\"ConjunctiveCells_left\"][\"w_init\"]\n",
+ "w_ccr_init = RingAttractor.apical_compartment.inputs[\"ConjunctiveCells_right\"][\"w_init\"]\n",
+ "w_rec_init = RingAttractor.apical_compartment.inputs[\"RingAttractor\"][\"w_init\"]\n",
+ "\n",
+ "# pull out the weights after training\n",
+ "w_ccl = RingAttractor.apical_compartment.inputs[\"ConjunctiveCells_left\"][\"w\"]\n",
+ "w_ccr = RingAttractor.apical_compartment.inputs[\"ConjunctiveCells_right\"][\"w\"]\n",
+ "w_rec = RingAttractor.apical_compartment.inputs[\"RingAttractor\"][\"w\"]\n",
+ "\n",
+ "# plot them\n",
+ "fig, ax = plt.subplots(1, 3, figsize=(12, 4))\n",
"ax[0].imshow(w_rec_init)\n",
"ax[1].imshow(w_ccl_init)\n",
"ax[2].imshow(w_ccr_init)\n",
@@ -621,7 +638,7 @@
"ax[1].set_title(\"Left conjunctive velocity cells \\nto ring attractor\")\n",
"ax[2].set_title(\"Right conjunctive velocity cells \\nto ring attractor\")\n",
"\n",
- "fig1, ax1 = plt.subplots(1,3,figsize=(12,4))\n",
+ "fig1, ax1 = plt.subplots(1, 3, figsize=(12, 4))\n",
"ax1[0].imshow(w_rec)\n",
"ax1[1].imshow(w_ccl)\n",
"ax1[2].imshow(w_ccr)\n",
@@ -630,9 +647,9 @@
"ax1[1].set_title(\"Left conjunctive velocity cells \\nto ring attractor\")\n",
"ax1[2].set_title(\"Right conjunctive velocity cells \\nto ring attractor\")\n",
"\n",
- "if save_plots == True: \n",
- " tpl.saveFigure(fig,\"PIweights_beforelearning\") \n",
- " tpl.saveFigure(fig1,\"PIweights_afterlearning\")"
+ "if save_plots == True:\n",
+ " tpl.saveFigure(fig, \"PIweights_beforelearning\")\n",
+ " tpl.saveFigure(fig1, \"PIweights_afterlearning\")"
]
},
{
diff --git a/demos/readme_figures.ipynb b/demos/readme_figures.ipynb
index ce88f1b2..36dcfc36 100644
--- a/demos/readme_figures.ipynb
+++ b/demos/readme_figures.ipynb
@@ -9,7 +9,7 @@
},
{
"cell_type": "code",
- "execution_count": 1,
+ "execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
@@ -27,19 +27,21 @@
},
{
"cell_type": "code",
- "execution_count": 2,
+ "execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
- "#Leave this as False. \n",
- "#For paper/readme production I use a plotting library (tomplotlib) to format and save figures. Without this they will still show but not save. \n",
- "if True: \n",
+ "# Leave this as False.\n",
+ "# For paper/readme production I use a plotting library (tomplotlib) to format and save figures. Without this they will still show but not save.\n",
+ "if True:\n",
" import tomplotlib.tomplotlib as tpl\n",
+ "\n",
" tpl.figureDirectory = \"../figures/\"\n",
" tpl.setColorscheme(colorscheme=2)\n",
" save_plots = True\n",
" from matplotlib import rcParams, rc\n",
- " rcParams['figure.dpi']= 300\n",
+ "\n",
+ " rcParams[\"figure.dpi\"] = 300\n",
"else:\n",
" save_plots = False"
]
@@ -58,77 +60,84 @@
"outputs": [
{
"data": {
- "image/png": "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",
+ "image/png": "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",
"text/plain": [
- "