Lab 6: Krichmar Path Planning using Nengo Neurons#

This Lab uses Nengo to find the optimal path of a \(3 \times 3\) map using Krichmar’s spike-wave propagation algorithm. The objectives of this lab are to:

  • Implement Krichmar’s spike-wave propagation algorithm using Nengo neurons

  • Ensure the spike wave works for any rectangular or square map

  • Utilize an “off-chip” AER to compute the fastest route to get from any starting point (location) to any ending point (location)

Specifications#

You will quickly notice that this notebook is merely an outline. You know everything you need to know to write this algorithm on your own! You will find specs for each associated coding block in the markdown prior to said block. Otherwise, you have the usuals.

Overall Specs:
◻ Filename changed to reflect last name of person submitting assignment
◻ Code runs error free
◻ Jupyter notebook is saved such that outputs appear upon opening file (and therefore on gradescope)
Comment your code To ensure you earn the score you deserve and/or to ensure I can provide valuable feedback, please help me understand your thought process by commenting as you go!
◻ (On your honor) All markdown and resources read thoroughly and fully understood


Set up#

Ensure you are using your 495 Virtual Environment before you begin!

Code Block Specs:
◻ Import Nengo and other supporting libraries into your program to get started

Input Map#

You will implement path planning such that your spike-wave propagation and AER generate the fastest route for the following \(3\times 3\) map, with start location 0 (top left) and end location 8 (bottom right): \(\begin{bmatrix} 1 & 2 & 1 \\ 1 & 3 & 5 \\ 7 & 1 & 1 \end{bmatrix}\)

Note that you can work in 2 dimensions (for your 2D map) or 1 dimension (flatten your map) when indexing everything. For example: you can either end at end_loc = [2,2] or at end_loc=8.

Code Block Specs:
◻ Create an array for your map
◻ Define your start and end locations

Spike-wave propagation Nengo model#

Start off by setting up your input to the map, which should be a simple spike. This looks a little different in Nengo. Instead, let’s send in a 1 for a short time, then 0 for the remainder into only the start location. The spike wave will do the rest to flow through the map.

While we are here, let’s build our neuron ensembles to represent each map location.

Code Block Specs:
◻ Create a piecewise function to kick off the spike wave.
◻ Be sure your input connects to your start location.
◻ Use a Nengo Ensemble for each map location (you can use an Ensemble array, a multidimensional ensemble, or a list of ensembles).

Each map location should have a delay equal to the weight (difficulty of traversal) of the location. Once a map location is traversed, we want to turn it off forever to make it easier than the inhibition implemented in Krichmar’s algorithm. We will use a node (starting with the delay node we discussed during the path planning tutorial) and add inhibition. See the Path Planning Tutorial for notes on how you might achieve inhibition within this node.

Code Block Specs:
◻ Create a class with a function (outside of your model) to emulate an axonal delay and emulate inhibition from Krichmar’s algorithm.
◻ Create a Nengo Node for each map location (recommend a list of ensembles) to implement the function you bulit to generate a delay and inhibition.

Now, it’s time to connect the rest. This gets complex! Every map location has anywhere from 3 (corner locations) to 8 (inside locations) neighbors to which it can send a spike. This is tough to generalize, so I did it for you! So you can understand what this code did, I assumed each map location had 8 possible locations, but for corners and edges I deleted any locations that were “off-map”.

You now have an array called connections. You will want to view your variables to dive into this array.

  • [i,j] is the current map location

  • k are the 8 neighbors associated with the current map location

  • The neighbors are values -1 to 8

  • Only positive values of k are the neighbors (i.e. ignore any -1s)

  • Neighbor at location k[0]=8 corresponds to map location [2,2] (python indexing)

  • Neighbor at location k[1]=4 corresponds to map location [1,0]

  • The k values were determined by (i*y_len)+j

  • If you’d rather work in 2D, you can use x = int(np.floor(k[0]/y_len)) and y = k[0]-x*y_len

If you do not like my connections array, you do not have to use it! You are welcome to build your own.

Code Block Specs:
◻ Connect your map location ensembles to your delay/inhibition nodes

connections = np.zeros((x_len,y_len,8)) # 8 connections per node, some valid, some not
for i in range(x_len):
    for j in range(y_len):
        connections_temp = np.array([[i-1,j-1],[i-1,j],[i-1,j+1],[i,j-1],[i,j+1],[i+1,j-1],[i+1,j],[i+1,j+1]])
        for k in range(8):
            if (connections_temp[k][0] <= (x_len-1) and connections_temp[k][0] >= 0) and (connections_temp[k][1] <= (y_len-1) and connections_temp[k][1] >= 0):
                connections[i,j,k] = connections_temp[k,0]*y_len + connections_temp[k,1]
            else:
                connections[i,j,k] = -1 # not a valid location on the map

Add probes and run your model

Code Block Specs:
◻ Probe your map location ensembles to use for your AER later
◻ Run your simulation for an appropriate amount of time. Account for the size of your map and duration of your piecewise input.

Create AER off-neuron#

Pay attention to how Krichmar says he uses the AER to determine the path:

  • The AER contains a location and a time of first spike.

  • Start at the end location and work backward!

  • I found that by looking at the first spike times of all possible neighboring map locations and choosing the smallest time allowed me to work from end node to start node for my optimal path.

Code Block Specs:
◻ Create your AER that contains a location and time of first spike
◻ Determine your optimal path by working backward from the end location

Visualize the results!#

You can print your map locations as x- and y- coordinates (examples: start_loc = [0,0], end_loc = [2,2]) or flattened locations (examples: start_loc = 0, end_loc=8). If you’d like to get super fancy, you can create a picture of a map (like in Krichmar’s paper) where you draw out your final path based on the results of your AER.

Code Block Specs:
◻ Print your optimal path.
◻ Plot the outputs of your final path to confirm the results of your AER. We should see a piecewise function flowing through as a spike-wave.