{ "metadata": { "name": "" }, "nbformat": 3, "nbformat_minor": 0, "worksheets": [ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "## SPA Cheatsheet\n", "\n", "Here is a quick summary of the components and their uses that are available in the SPA system" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Making a SPA Model\n", "\n", "You always have to start like this:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "import nengo\n", "import nengo.spa as spa\n", "\n", "model = spa.SPA(label=\"My Model Name\")\n", "with model:" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Making neurons to represent a Semantic Pointer\n", "\n", "If I want to have a group of neurons whose value is a semantic pointer, make a ```Buffer```.\n", "\n" ] }, { "cell_type": "code", "collapsed": false, "input": [ "import nengo\n", "import nengo.spa as spa\n", "\n", "model = spa.SPA(label=\"My Model Name\")\n", "with model:\n", " model.value = spa.Buffer(dimensions=32)\n", " " ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To probe data from the buffer, use ```nengo.Probe(model..state.output)```" ] }, { "cell_type": "code", "collapsed": false, "input": [ "import nengo\n", "import nengo.spa as spa\n", "\n", "model = spa.SPA(label=\"My Model Name\")\n", "with model:\n", " model.value = spa.Buffer(dimensions=32)\n", " nengo.Probe(model.value.state.output)" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The number of dimensions must be a multiple of 16. If you want to change this, you can set the parameter ```subdimensions```" ] }, { "cell_type": "code", "collapsed": false, "input": [ "import nengo\n", "import nengo.spa as spa\n", "\n", "model = spa.SPA(label=\"My Model Name\")\n", "with model:\n", " model.value = spa.Buffer(dimensions=30, subdimensions=10)\n", " nengo.Probe(model.value.state.output)" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can also set the number of neurons per dimension (the default is 50)" ] }, { "cell_type": "code", "collapsed": false, "input": [ "import nengo\n", "import nengo.spa as spa\n", "\n", "model = spa.SPA(label=\"My Model Name\")\n", "with model:\n", " model.value = spa.Buffer(dimensions=32, neurons_per_dimension=10)\n", " nengo.Probe(model.value.state.output)" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Providing input\n", "\n", "In addition to providing input using the JavaViz visualizer, you can also manually specify inputs. Do this by defining a function that returns the semantic pointer that will be used as input. Then create a ```spa.Input``` object and indicate which Buffer (or other SPA object) should receive that input." ] }, { "cell_type": "code", "collapsed": false, "input": [ "import nengo\n", "import nengo.spa as spa\n", "\n", "model = spa.SPA(label=\"My Model Name\")\n", "with model:\n", " model.value = spa.Buffer(dimensions=32)\n", " nengo.Probe(model.value.state.output)\n", " \n", " def my_input(t):\n", " if t < 0.1:\n", " return 'CAT'\n", " elif 0.1 < t < 0.2:\n", " return 'DOG'\n", " else:\n", " return '0'\n", " model.input = spa.Input(value=my_input)" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Making a Memory\n", "\n", "A ```Memory``` is the same as a ```Buffer```, except that it should continue to store a value even after you remove the input. This is implemented as an Integrator" ] }, { "cell_type": "code", "collapsed": false, "input": [ "import nengo\n", "import nengo.spa as spa\n", "\n", "model = spa.SPA(label=\"My Model Name\")\n", "with model:\n", " model.memory = spa.Memory(dimensions=32)" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can probe it in the same way as the ```Buffer```." ] }, { "cell_type": "code", "collapsed": false, "input": [ "import nengo\n", "import nengo.spa as spa\n", "\n", "model = spa.SPA(label=\"My Model Name\")\n", "with model:\n", " model.memory = spa.Memory(dimensions=32)\n", " nengo.Probe(model.memory.state.output)" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The ```Memory``` has the same parameters as the ```Buffer```. In addition, you can set the time constant of the neurotransmitter bit setting ```synapse``` (default of 0.01 seconds). Larger values should give a more stable memory." ] }, { "cell_type": "code", "collapsed": false, "input": [ "import nengo\n", "import nengo.spa as spa\n", "\n", "model = spa.SPA(label=\"My Model Name\")\n", "with model:\n", " model.memory = spa.Memory(dimensions=32, synapse=0.1)\n", " nengo.Probe(model.memory.state.output)" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Finally, you can set the time constant of the memory. With ```tau=None``` (which is the default), it will attempt to store the memory forever. For other values, the memory should decay over that time. " ] }, { "cell_type": "code", "collapsed": false, "input": [ "import nengo\n", "import nengo.spa as spa\n", "\n", "model = spa.SPA(label=\"My Model Name\")\n", "with model:\n", " model.memory = spa.Memory(dimensions=32, synapse=0.1, tau=1.0)\n", " nengo.Probe(model.memory.state.output)" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Action Selection\n", "\n", "To combine these components into a model, we can define actions, build a Basal Ganglia, and a Thalamus.\n", "\n", "Actions have two parts: utility of the action (how good it is given the current state) and the effect of the action (what should happen when the action is selected).\n", "\n", "Action utility is often of the form ```dot(buffer, CAT)```, which says that the utility is the dot product of the value stored in ```buffer``` with the semantic pointer for ```CAT```.\n", "\n", "Action effects are often of the form ```buffer=DOG```, which says to send the value ```DOG``` to the SPA component ```buffer```. Another common type of action is ```buffer=other_buffer```, which takes the value from one system and sends it to another." ] }, { "cell_type": "code", "collapsed": false, "input": [ "import nengo\n", "import nengo.spa as spa\n", "\n", "model = spa.SPA(label=\"My Model Name\")\n", "with model:\n", " model.buffer1 = spa.Buffer(dimensions=32)\n", " model.buffer2 = spa.Buffer(dimensions=32)\n", " \n", " nengo.Probe(model.buffer1.state.output)\n", " nengo.Probe(model.buffer2.state.output)\n", " \n", " actions = spa.Actions(\n", " 'dot(buffer1, CAT) --> buffer2=DOG',\n", " 'dot(buffer1, DOG) --> buffer2=HORSE',\n", " 'dot(buffer1, HORSE) --> buffer2=buffer1',\n", " )\n", " model.bg = spa.BasalGanglia(actions)\n", " model.thalamus = spa.Thalamus(model.bg)\n", " " ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Two useful things to probe in this system are ```model.bg.input```, which is the $Q$ values being fed into the basal ganglia, and ```model.thalamus.actions.output```, which indicates the selected action." ] }, { "cell_type": "code", "collapsed": false, "input": [ "import nengo\n", "import nengo.spa as spa\n", "\n", "model = spa.SPA(label=\"My Model Name\")\n", "with model:\n", " model.buffer1 = spa.Buffer(dimensions=32)\n", " model.buffer2 = spa.Buffer(dimensions=32)\n", " \n", " nengo.Probe(model.buffer1.state.output)\n", " nengo.Probe(model.buffer2.state.output)\n", " \n", " actions = spa.Actions(\n", " 'dot(buffer1, CAT) --> buffer2=DOG',\n", " 'dot(buffer1, DOG) --> buffer2=HORSE',\n", " 'dot(buffer1, HORSE) --> buffer2=buffer1',\n", " )\n", " model.bg = spa.BasalGanglia(actions)\n", " model.thalamus = spa.Thalamus(model.bg)\n", " \n", " nengo.Probe(model.bg.input)\n", " nengo.Probe(model.thalamus.actions.output)" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can add together multiple parts for the utility, and you can also multiply by scalars.\n", "\n", "Importantly, try to make sure that the largest utility value is around 1.0, since the neurons don't do a good job representing above that." ] }, { "cell_type": "code", "collapsed": false, "input": [ "import nengo\n", "import nengo.spa as spa\n", "\n", "model = spa.SPA(label=\"My Model Name\")\n", "with model:\n", " model.buffer1 = spa.Buffer(dimensions=32)\n", " model.buffer2 = spa.Buffer(dimensions=32)\n", " \n", " nengo.Probe(model.buffer1.state.output)\n", " nengo.Probe(model.buffer2.state.output)\n", " \n", " actions = spa.Actions(\n", " '(dot(buffer1, CAT) + dot(buffer2, DOG))*0.5 --> buffer2=DOG',\n", " )\n", " model.bg = spa.BasalGanglia(actions)\n", " model.thalamus = spa.Thalamus(model.bg)\n", " \n", " nengo.Probe(model.bg.input)\n", " nengo.Probe(model.thalamus.actions.output)" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can also use complex semantic pointers" ] }, { "cell_type": "code", "collapsed": false, "input": [ "import nengo\n", "import nengo.spa as spa\n", "\n", "model = spa.SPA(label=\"My Model Name\")\n", "with model:\n", " model.buffer1 = spa.Buffer(dimensions=32)\n", " model.buffer2 = spa.Buffer(dimensions=32)\n", " \n", " nengo.Probe(model.buffer1.state.output)\n", " nengo.Probe(model.buffer2.state.output)\n", " \n", " actions = spa.Actions(\n", " 'dot(buffer1, CAT+DOG*FURRY+~BLUE) --> buffer2=DOG',\n", " )\n", " model.bg = spa.BasalGanglia(actions)\n", " model.thalamus = spa.Thalamus(model.bg)\n", " \n", " nengo.Probe(model.bg.input)\n", " nengo.Probe(model.thalamus.actions.output)" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can even just have a fixed value for the utility" ] }, { "cell_type": "code", "collapsed": false, "input": [ "import nengo\n", "import nengo.spa as spa\n", "\n", "model = spa.SPA(label=\"My Model Name\")\n", "with model:\n", " model.buffer1 = spa.Buffer(dimensions=32)\n", " model.buffer2 = spa.Buffer(dimensions=32)\n", " \n", " nengo.Probe(model.buffer1.state.output)\n", " nengo.Probe(model.buffer2.state.output)\n", " \n", " actions = spa.Actions(\n", " '0.5 --> buffer2=DOG',\n", " )\n", " model.bg = spa.BasalGanglia(actions)\n", " model.thalamus = spa.Thalamus(model.bg)\n", " \n", " nengo.Probe(model.bg.input)\n", " nengo.Probe(model.thalamus.actions.output)" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For actions, you can have multiple effects by listing them" ] }, { "cell_type": "code", "collapsed": false, "input": [ "import nengo\n", "import nengo.spa as spa\n", "\n", "model = spa.SPA(label=\"My Model Name\")\n", "with model:\n", " model.buffer1 = spa.Buffer(dimensions=32)\n", " model.buffer2 = spa.Buffer(dimensions=32)\n", " model.buffer3 = spa.Buffer(dimensions=32)\n", " model.buffer4 = spa.Buffer(dimensions=32)\n", " \n", " nengo.Probe(model.buffer1.state.output)\n", " nengo.Probe(model.buffer2.state.output)\n", " \n", " actions = spa.Actions(\n", " 'dot(buffer1, CAT) --> buffer2=DOG, buffer3=HOUSE, buffer4=CAR',\n", " 'dot(buffer1, DOG) --> buffer2=HORSE',\n", " 'dot(buffer1, HORSE) --> buffer2=buffer1',\n", " )\n", " model.bg = spa.BasalGanglia(actions)\n", " model.thalamus = spa.Thalamus(model.bg)\n", " " ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can also transform the semantic pointer while routing it" ] }, { "cell_type": "code", "collapsed": false, "input": [ "import nengo\n", "import nengo.spa as spa\n", "\n", "model = spa.SPA(label=\"My Model Name\")\n", "with model:\n", " model.buffer1 = spa.Buffer(dimensions=32)\n", " model.buffer2 = spa.Buffer(dimensions=32)\n", " \n", " nengo.Probe(model.buffer1.state.output)\n", " nengo.Probe(model.buffer2.state.output)\n", " \n", " actions = spa.Actions(\n", " 'dot(buffer1, CAT) --> buffer2=buffer1*ANIMAL',\n", " 'dot(buffer1, DOG) --> buffer2=buffer1*ANIMAL',\n", " 'dot(buffer1, HORSE) --> buffer2=buffer1*buffer2',\n", " )\n", " model.bg = spa.BasalGanglia(actions)\n", " model.thalamus = spa.Thalamus(model.bg)\n", " " ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Cortical Effects\n", "\n", "You can also implement effects that are constant. That is, these are actions that should happen all the time, so we call these ```Cortical``` actions. This forms a direct hard-wird connection between two SPA components. For example, we can set one buffer to always take the value from another buffer convolved with a particular semantic pointer" ] }, { "cell_type": "code", "collapsed": false, "input": [ "import nengo\n", "import nengo.spa as spa\n", "\n", "model = spa.SPA(label=\"My Model Name\")\n", "with model:\n", " model.buffer1 = spa.Buffer(dimensions=32)\n", " model.buffer2 = spa.Buffer(dimensions=32)\n", " \n", " nengo.Probe(model.buffer1.state.output)\n", " nengo.Probe(model.buffer2.state.output)\n", " \n", " cortical_actions = spa.Actions(\n", " 'buffer2=buffer1*ANIMAL'\n", " )\n", " model.cortical = spa.Cortical(cortical_actions)" ], "language": "python", "metadata": {}, "outputs": [] } ], "metadata": {} } ] }