GithubHelp home page GithubHelp logo

nengo / nengo-spa Goto Github PK

View Code? Open in Web Editor NEW
20.0 20.0 5.0 27.91 MB

Implementation of the Semantic Pointer Architecture for Nengo

Home Page: https://www.nengo.ai/nengo-spa/

License: Other

Python 100.00%
cognitive-architecture cognitive-science nengo neural-networks neuroscience python

nengo-spa's Introduction

Latest PyPI version Python versions

Nengo: Large-scale brain modelling in Python

An illustration of the three principles of the NEF

Nengo is a Python library for building and simulating large-scale neural models. Nengo can create sophisticated spiking and non-spiking neural simulations with sensible defaults in a few lines of code. Yet, Nengo is highly extensible and flexible. You can define your own neuron types and learning rules, get input directly from hardware, build and run deep neural networks, drive robots, and even simulate your model on a completely different neural simulator or neuromorphic hardware.

Installation

Nengo depends on NumPy, and we recommend that you install NumPy before installing Nengo. If you're not sure how to do this, we recommend using Anaconda.

To install Nengo:

pip install nengo

If you have difficulty installing Nengo or NumPy, please read the more detailed Nengo installation instructions first.

If you'd like to install Nengo from source, please read the developer installation instructions.

Nengo is tested to work on Python 3.6 and above. Python 2.7 and Python 3.4 were supported up to and including Nengo 2.8.0. Python 3.5 was supported up to and including Nengo 3.1.

Examples

Here are six of many examples showing how Nengo enables the creation and simulation of large-scale neural models in few lines of code.

  1. 100 LIF neurons representing a sine wave
  2. Computing the square across a neural connection
  3. Controlled oscillatory dynamics with a recurrent connection
  4. Learning a communication channel with the PES rule
  5. Simple question answering with the Semantic Pointer Architecture
  6. A summary of the principles underlying all of these examples

Documentation

Usage and API documentation can be found at https://www.nengo.ai/nengo/.

To build the documentation yourself, run the following command:

python setup.py build_sphinx

This requires Pandoc to be installed, as well as some additional Python packages. For more details, see the Developer Guide.

Development

Information for current or prospective developers can be found at https://www.nengo.ai/contributing/.

Getting Help

Questions relating to Nengo, whether it's use or it's development, should be asked on the Nengo forum at https://forum.nengo.ai.

nengo-spa's People

Contributors

arvoelke avatar bmorcos avatar celiasmith avatar drasmuss avatar e2crawfo avatar gabriel-doriath-dohler avatar hunse avatar ikajic avatar jaberg avatar jgosmann avatar mundya avatar petersuma avatar s72sue avatar seanny123 avatar tbekolay avatar tcstewar avatar xchoo avatar youssefzaky avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

nengo-spa's Issues

Build actions automatically

This solves #45 and one rarely wants to instantiate the spa.Actions without building the actions. But the build function should be kept and there should be a constructor argument to disable the automatic build to restore the current behavior.

The automatic build should store the return values of build as attributes on the action object. build should continue to return these objects because the function could be called multiple times and should not overwrite the object's attributes each time.

Potential arguments against this change:

  • Gives magic side effects during instantiation. But this is similar to standard Nengo objects and build has those magic side effects too.
  • Makes spa.Actions behave like a standard Nengo object without being one (e.g. it doesn't get added nengo.Network.all_objects). But it creates Nengo objects that behave the proper way.
  • No separation of pure description of the rules and actual implementation (but with the constructor argument that can still be achieved when relevant).

If anyone has any other arguments for or against this change, please comment.

Synapses for different thalamic channels

In Jelmer's model, he's finding that he would like to adjust the synapse times between areas separately for different thalamic actions.

Right now, the thalamus has a single parameter synapse_channel that sets it for all of them. He's added a hack that adds this parameter:

synapse_channel_dict : dict, optional (Default: None, uses synapse_channel for all channels)
        Set synaptic filter per state or channel between two states. 
        E.g. dict(vision=.01, goal_memory=.05, memory=.1) means:
        1) synaptic filters *to* the object 'vision' are set to .01
        2) the synaptic filter from 'goal' to 'memory' are set to .05
        3) synaptic filters to 'memory' not originating in 'goal' are set to .1
        4) all other synaptic filters on channels are set to “synapse_channel"

I think I'd prefer some slightly different notation (maybe something like: synapse_channel_dict={model.vision:.01, (model.goal, model.memory):.05, model.memory:0.1}, or maybe some other completely diffeerent approach, but this sort of feature would be nice to have.

Better error on failure to infer types

I'm re-implementing Spaun's counting network using nengo_spa. When I run python count.py from the linked repository, I get the following error:

Traceback (most recent call last):
  File "count.py", line 148, in <module>
    """)
  File "/home/saubin/git/nengo_spa/nengo_spa/actions.py", line 80, in Actions
    top_node.infer_types(None)
  File "/home/saubin/git/nengo_spa/nengo_spa/compiler/ast_nodes.py", line 992, in infer_types
    action.infer_types(context_type)
  File "/home/saubin/git/nengo_spa/nengo_spa/compiler/ast_nodes.py", line 915, in infer_types
    self.condition.infer_types(context_type)
  File "/home/saubin/git/nengo_spa/nengo_spa/compiler/ast_nodes.py", line 444, in infer_types
    context_type = infer_vocab(self.lhs, self.rhs)
  File "/home/saubin/git/nengo_spa/nengo_spa/compiler/ast_nodes.py", line 62, in infer_vocab
    node.infer_types(None)
  File "/home/saubin/git/nengo_spa/nengo_spa/compiler/ast_nodes.py", line 444, in infer_types
    context_type = infer_vocab(self.lhs, self.rhs)
  File "/home/saubin/git/nengo_spa/nengo_spa/compiler/ast_nodes.py", line 62, in infer_vocab
    node.infer_types(None)
  File "/home/saubin/git/nengo_spa/nengo_spa/compiler/ast_nodes.py", line 444, in infer_types
    context_type = infer_vocab(self.lhs, self.rhs)
  File "/home/saubin/git/nengo_spa/nengo_spa/compiler/ast_nodes.py", line 62, in infer_vocab
    node.infer_types(None)
  File "/home/saubin/git/nengo_spa/nengo_spa/compiler/ast_nodes.py", line 360, in infer_types
    vocab = Network.get_output_vocab(self.obj)
  File "/home/saubin/git/nengo_spa/nengo_spa/network.py", line 85, in get_output_vocab
    return cls._output_vocabs[obj]
  File "/usr/lib/python3.6/weakref.py", line 394, in __getitem__
    return self.data[ref(key)]
KeyError: <weakref at 0x7fa55aab4ef8; to 'Node' at 0x7fa55aae55c0>

I don't expect anyone to clone my repository and run it themselves, but I was hoping to get some tips for isolating the error better, so I can report a better bug. How can I isolate what line of giant spa.Actions is causing this error?

Indent error when inserting comments

Run the following:

import nengo
import nengo_spa as spa

sequence = 'WRITE ONE NONE WRITE TWO NONE THREE WRITE NONE'.split()

def input_vision(t):
    index = int(t / 0.5) % len(sequence)
    return sequence[index]

# Number of dimensions for the SPs
dimensions = 64

# Make a model object with the SPA network
model = spa.Network(label='Parser')
n_per_dim = 100

with model:
    # Specify the modules to be used
    model.vision = spa.Transcode(input_vision, output_vocab=dimensions)

    model.phrase = spa.State(dimensions, neurons_per_dimension=n_per_dim)
    model.motor = spa.State(dimensions, neurons_per_dimension=n_per_dim)
    model.noun = spa.State(dimensions, feedback=1., neurons_per_dimension=n_per_dim)
    model.verb = spa.State(dimensions, feedback=1., neurons_per_dimension=n_per_dim)

    spa.Actions("""
        # this is a comment
        ifmax dot(model.vision, WRITE+READ) as 'write':
            model.vision -> model.verb
        ifmax dot(model.vision, ONE+TWO+THREE) as 'vision':
            model.vision -> model.noun
        ifmax 0.5*(dot(NONE-WRITE-ONE-TWO-THREE, model.vision) + dot(model.phrase, WRITE*VERB)):
            model.phrase*~NOUN -> model.motor
        model.noun*NOUN + model.verb*VERB -> model.phrase
    """)

Gives the error:


Traceback (most recent call last):
  File "ifmax_label.py", line 35, in <module>
    """)
  File "/home/saubin/git/nengo_spa/nengo_spa/actions.py", line 73, in Actions
    raise _rule_mismatch_err_to_syntax_err(actions, err)
IndentationError: (3, 0) unexpected indent
        ifmax dot(model.vision, WRITE+READ) as 'write':
^

Add attribute to ASTAccessor to access BG and Thal

Given an ASTAccessor can have (0, N] basal ganglias and thalamus, would it be helpful to provide a list of them? It comes up a lot for plotting in the examples. I'd imagine something like:

actions = spa.Actions('...')
bgs = actions.bg_loops()
thals = actions.thal_loops()

Basal Ganglia networks are not labeled

Running this code in the Nengo GUI:

import nengo
import nengo_spa as spa

num_vocab = spa.Vocabulary(16)
num_vocab.populate("ZERO; ONE")

with spa.Network() as model:

    model.motor_mag = spa.Scalar()

    model.running = spa.Transcode(input_vocab=num_vocab, output_vocab=num_vocab)
    model.scal = spa.Scalar()

    spa.Actions("""
        ifmax model.motor_mag as 'motor':
            ZERO -> model.running
        ifmax model.scal as 'scal':
            ONE -> model.running
    """)

Gives the following network:

selection_007

This is because the Basal Ganglia networks are not labeled, even though the Thalamus networks are:

In [3]: af = actions[0] 

In [6]: af.bg.label is None
Out[6]: True

In [8]: af.thalamus.label
Out[8]: 'Thalamus'

Additionally, I think the Basal Ganglia and Thalamus networks should incorporate the label given by the as keyword? Something like Thalamus (scal) from the previous example?

Support for different binding methods

Circular convolution is only one possible choice for binding and binding operations might have some advantages.

  • We should consider to make easy to switch out the circular convolution default.
  • We should consider implementing vector-derived transformation binding (in the linked document called vector-derived projection binding, but I came to the conclusion that “transformation” is more correct and will change it in the document with the next update)

No line number on spa no attribute error

(Copied from nengo/nengo#907.)

When there is a module is created in spa.SPA() and not assigned to attributes cause an error to be thrown. However, there is no indication of where the error occurred. Would it be possible to identify the line of code?

Allow to use solver for translate/transform_to

Allow to provide a solver for the translation between vocabularies. That can give better results than the simple outer product matrix.

v1 = spa.Vocabulary(d)
v2 = spa.Vocabulary(d)

s = 'A; B; C; D; E; F; G; H; I; J'
v1.populate(s)
v2.populate(s)

U1 = np.asarray([p.v for p in v1.parse_n(*s.split(';'))])
U2 = np.asarray([p.v for p in v2.parse_n(*s.split(';'))])

solver = nengo.solvers.Lstsq()
Ta = solver(U1, U2)[0].T
Tb = v1.transform_to(v2)

Va = Ta.dot(U1.T).T
Vb = Tb.dot(U1.T).T

Sa = spa.similarity(Va, v2)
Sb = spa.similarity(Vb, v2)

plt.title("Lstsq (%s)" % nengo.utils.numpy.rmse(Sa, np.eye(len(U1))))
plt.imshow(Sa, vmin=0, vmax=1)
plt.colorbar()
plt.show()

plt.title("transform_to (%s)" % nengo.utils.numpy.rmse(Sb, np.eye(len(U1))))
plt.imshow(Sb, vmin=0, vmax=1)
plt.colorbar()
plt.show()

Better error handling

The get_spa_network, get_network_input, and get_network_output methods on spa.Network contain code that is intended to produce an error message that shows the full name, but it doesn't work because the KeyError is immediately catched in converted ot the SpaNetworkError instead of being reraised to the outermost invocation. This should be fixed. In addition, it would be could to catch the error in the Actions object and attach information about which exact rule failed.

Allow action rules to create states

It might make sense to autmatically create a spa.State state in an action rule of the the style state = .... The vocabulary probably needs to be derived from the expression in that case and it probably would not allow for setting other parameters on the state. But it might make it easier to construct large models because not each intermediary state needs to be defined beforehand.

Improve AST tests

The AST tests could be improved by splitting them up into smaller tests.

Consolidate Encode, Transcode, and Decode in a single module?

That might be less confusing. However, it is not clear how to do the parametrization of input/output vocabs and size in/size out and it is likely to increase the code complexity internally.

For both input and output there needs to be a vocab and a size argument. It is not possible to use a single argument because an integer assigned as a vocab will be interpreted as the default vocab of that dimensionality. It must also be ensured that only one of vocab and size is set. Furthermore, the size_out/size_in arguments might not be set, but on the internal nengo.Node they will be set which might be confusing.

Depending on the vocab and size arguments, the valid function signatures and return types will change which affects the parameter validation.

Replace associative memory input_keys and output_keys with single mapping parameter.

This came up originally in nengo/nengo#891 and this comment, partially reproduced here, describes the actions to take:

Replace AssociativeMemory(input_vocab, output_vocab=None, input_keys=None, output_keys=None, ...) with AssociativeMemory(input_vocab, output_vocab=None, mapping=None, ...).

  • This does not allow to pass in a set of keys to cleanup a subset of a vocab, but this can still be done by using Vocabulary.create_subset() and passing that in (which is what I did so far anyways because input_keys is undocumented).
  • If output_vocab is set, but mapping is not, raise an exception to force the user to be explicit about what should happen.
  • If mapping='by-key' (or something similar) do the mapping according to the keys (transform-style memory).
  • To do a hetero-associative memory one passes in a dictionary with a mapping from keys in input_vocab to keys in output_vocab. (To me this seems to make it more clear how the mapping happens than two lists named input_keys and output_keys.)

There was some concern about unintuitive behaviour of the Vocabulary.create_subset method, but these problems should be resolved in nengo_spa.

Absorbing elements

Zero is the only real absorbing element for circular convolution. For AbsorbingElement AbsorbingElement() * pointer == AbsorbingElement() holds only under normalization. This should be made clear. Also, there might be multiple absorbing elements given normalization.

Warn about unbuilt Action objects

It can happen that one forgets to call the build function on an Action instance and then wonders why the action rules are not working. Creating an action object without building it in the network should produce a warning.

Vocabulary.parse gives misleading exception in strict mode

d = 64
v1 = spa.Vocabulary(dimensions=d)
v1.parse('A')

will give SpaParseError: Semantic pointers must start with a capital letter. which is misleading because the problem here isn't a non-capital letter, but that the vocabulary is in strict mode and A is not yet in the vocabulary.

Intercept and eval_point distributions need more validation

I only have demonstrated CosineSimilarity(d + 2) to be better for 50 neurons per dimension for the circular convolution. It seems to be worse for 200 neurons per dimension. Better in this case also only applies to the RMSE, but the the dot-product similarity might be worse (but that also depends on normalization).

I should double check State and Compare, but these might still be fine as no Fourier transform is involved.

AST nodes uses infer_types unexpectedly

infer_types can use either the root network or the context type to infer a type from. However, certain classes can only use one of these options. Consequently, would it be reasonable to break the function into infer_types_from_root(root_network) and infer_types_from_context(context_type) to be explicit?

Action rule syntax

It was noted during the last lab meeting that the current action rule syntax might be misleading with its use of the equals sign. Currently, action rules use this syntax: utility --> sink = source. Please use this issue to suggest alternatives to the --> and = symbols.

Make the SPA unit tests reliable

(original issue nengo/nengo#512)

Currently, a lot of the SPA unit tests break easily (e.g. working memory, thalamus) from changes which should improve the network. This defeats the purpose of the unit tests as they do not tell whether the code is working or not and one needs quite some background knowledge to evaluate whether they fail because something is not working or because the tests need an adjustment.

Each test should define somehow a bound on the permitted error and any improvement in accuracy should leave the tests passing. I assume figuring out how to determine that error in each case is the major difficulty.

It would be cool if those error values would be saved for every commit, so that we can track improvements and regressions.

Update before `intro_from_legacy_spa` before release

#46 has a few things missing from it that should be added before a new release is made.

  • Make "Introduction to SPA" tutorial and link to it.
  • Link to Independent Accumulator publication once it's published.
  • Update the Associative Memory documentation and link to it.

All caps vs. camel case debate

Currently many SPA examples use ALL_CAPS for semantic pointers. I think nengo_spa gives us the opportunity to rethink this choice and potentially switch to CamelCase which I tend to prefer.

Contra ALL_CAPS:

  • Feels like old 90's HTML. ;) (maybe that's a pro for some? :P)
  • In my opinion worse readability for long (single) words. (But most semantic pointer names aren't long.)
  • Names consisting out of multiple words are not as easily recognized as a single entity (they appear almost as multiple entities/pointers as the underscore is almost like a space).
  • Slightly annoying to type.

Pro ALL_CAPS:

  • The difference to all lowercase modules is more apparent and less easily missed which might matter for users learning nengo_spa.
  • Camel case has worse readability for compounds of many words (but most semantic pointers are just on word).

I suppose this could end up to be a very opinionated discussion as most arguments will not be absolute facts, but might still be worth having as it shapes how nengo_spa code is perceived. Personally I prefer camel case, but to me the first pro all caps point is also a strong argument.

Action rule complexity limited by recursion limit

The current implementation of the construct function of the AST nodes uses recursion which limits the maximum complexity of action rules. This is probably not a problem for most actions rules, but maybe we should consider rewriting this to use a different approach (there are non-recursive tree-traversal algorithms using a stack separate from the call stack, also see the end of this mind-bending talk).

When creating SPA node inputs, automatically convert to array

(Copied from nengo/nengo#661.)

At this point, when you want to use SPA as an input to a network, you have to call the somewhat awkward .v attribute as demonstrated in the code below.

import nengo

model = nengo.Network()
with model:
    a = nengo.networks.EnsembleArray(n_neurons=100, n_ensembles=16, ens_dimensions=1)
    def my_input(t):
        if t < 0.1:
            return vocab.parse('HELLO').v
        else:
            return vocab.parse('BYE').v

    nengo.Connection(nengo.Node(my_input), a.input)

Is there any way to automatically convert to a vector so this doesn't need to be called? The reason I believe this is possible is that numpy seems to transparently convert between arrays and python lists all the time.

Node output of spa.Network needs size_in?

When I run the following code:

import nengo
import nengo_spa as spa


class MemNet(spa.Network):
    def __init__(self, mem_vocab):
        super(MemNet, self).__init__()

        self.output = nengo.Node(size_out=mem_vocab.dimensions)

        self.declare_output(self.output, mem_vocab)

num_vocab = spa.Vocabulary(16)

with spa.Network() as model:

    model.res_mem = MemNet(num_vocab)
    model.motor = spa.State(num_vocab)

    spa.Actions("""
        model.res_mem -> model.motor
    """)

I get the error:

Traceback (most recent call last):
  File "super_bad_out_fail.py", line 22, in <module>
    """)
  File "/home/saubin/git/nengo_spa/nengo_spa/actions.py", line 83, in Actions
    top_node.construct(construction_context)
  File "/home/saubin/git/nengo_spa/nengo_spa/compiler/ast_nodes.py", line 807, in construct
    artifact.nengo_source, target, transform=artifact.transform))
  File "/home/saubin/git/nengo/nengo/base.py", line 35, in __call__
    inst.__init__(*args, **kwargs)
  File "/home/saubin/git/nengo/nengo/connection.py", line 419, in __init__
    self.pre = pre
  File "/home/saubin/git/nengo/nengo/base.py", line 106, in __setattr__
    super(NengoObject, self).__setattr__(name, val)
  File "/home/saubin/git/nengo/nengo/config.py", line 456, in __setattr__
    reraise(exc_info[0], exc_info[1], None)
  File "/home/saubin/git/nengo/nengo/utils/compat.py", line 97, in reraise
    raise value.with_traceback(tb)
nengo.exceptions.ValidationError: Connection.pre: '<Node (unlabeled) at 0x7f2ef762d278>' must have size_out > 0.

I honestly don't know if:

  1. Is this a valid error?
  2. If this is a valid error, is there a better way of displaying it?

Change example to use examine.similarity

Certain examples use something like vocab.parse(pointer).dot(sim.data[conv].T) when they should be using examine.similarity? I'm not sure about this and I'm just making a note as I review some pull requests.

More powerful methods for joining vocabs

It would be nice to be able to merge to vocabularies to build larger vocabs from smaller ones. Of course this will require that the vocabs have the same dimensionality. This should (probably) fail if there are any keys in both vocabularies.

Make file organisation consistent

  • Thalamus and BasalGanglia should be nengo.Network, not nengo_spa.Network I believe, also move to nengo_spa.networks. They should be nengo_spa.Network to allow to configure their parameters with configs. Given that they are also automatically created, they belong in nengo_spa.modules.
  • Move selection networks to nengo_spa.networks.
  • Move nengo_spa.Networks to nengo_spa.modules (alternate possible names nengo_spa.units and nengo_spa.components, though components is already used by Nengo GUI)
  • Where should IdentityEnsembleArray go (if included)? It technically belongs into nengo_spa.networks, but is closely related to State, so maybe it should stay in the same file.
  • Are there better places for the functions in the utils module?

Give spa.Inputs ability to act like nodes

(Copied from nengo/nengo#418.)

Currently, the spa.Input can only output predefined values given the timestamp of the simulation. It would be nice for the spa.Inputs to be able to take in other inputs (like the nengo.Nodes) as well.

e.g. Current method:

import nengo
from nengo import spa as spa

vocab = spa.Vocabulary(16)
vocab.parse('A+B')

model = spa.SPA(label='model')
with model:
    # Some input function driven by t or something
    input_node = nengo.Node(output=lambda t: int(t) % 2)

    # Node driven by x (environment? system state? etc?)
    def drive_func(t, x):
        if x == 1:
            return vocab.parse('A').v
        else:
            return vocab.parse('B').v
    drive_node = nengo.Node(size_in=1, output=drive_func)

    nengo.Connection(input_node, drive_node, synapse=0)

    model.buff = spa.Buffer(16)
    nengo.Connection(drive_node, model.buff.state.input)

    buff_probe = nengo.Probe(model.buff.state.output, synapse=0.03)

Proposed method:

import nengo
from nengo import spa as spa

model = spa.SPA(label='model')
with model:
    input_node = nengo.Node(output=lambda t: int(t) % 2)
    model.buff = spa.Buffer(16)    

    def drive_func(t, x):
        if x == 1:
            return 'A'
        else:
            return 'B'

    model.drive = spa.Input(buff=drive_func)
    nengo.Connection(input_node, model.drive, synapse=0)

    buff_probe = nengo.Probe(model.buff.state.output, synapse=0.03)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.