How to create and register a new kind of Region?

I am trying to create a new kind of region called an AccumulatorRegion. This is a very simple and widely applicable region which:

  • takes a sequence of inputs each of which is a sparse list of cells arranged into mini-columns (e.g. the output of a TM)
  • as each input is presented using the AccmulatorRegion’s “compute()” method, this increments a counter on each column which then holds the number of times times this column has appeared in the inputs
  • at any time the regions “getWinnerColumns()” method can be called which returns a sparse list of column indices whose accumulated count is above (or equal to) a threshold. This threshold is determined dynamically by taking the current highest count across all the columns and multiplying this by a configuration parameter called “winnerPercentThreshold” (which is an integer 0-100, so we then divide the result by 100). (It is intended this threshold would normally be set fairly high.) This returns the “most popular” columns.

I have created and tested an implementation of the algorithm that does this, which works fine (it is very simple).

I am now trying to make this work using the Network API. I have created the code for an AccumulatorRegion following the example of existing nupic regions. Where I am stuck is getting the Network API to load the implementation of my new region. I have tried “registering” the new region as per the code below but this does not seem to work:

from nupic.engine import Network
import yaml
import json

# A network that will hold the regions.
network = Network()

# register my implementation (module accumulator.py and class Accumulator) of my new Region: AccumulatorRegion
module = __import__("accumulator")
accRegionClass = getattr(module, "Accumulator")
network.registerRegion(accRegionClass)


_PARAMS_PATH = "accModel.yaml" 
# this model includes:
'''
accParams:
    regionName: AccumulatorRegion
    implementation: accumulator
'''


with open(_PARAMS_PATH, "r") as f:
  modelParams = yaml.safe_load(f)["modelParams"]


accParams=modelParams["accParams"]
network.addRegion("ACC", "py.AccumulatorRegion", json.dumps(accParams))


tmParams = modelParams["tmParams"]
network.addRegion("TM", "py.TMRegion", json.dumps(tmParams))

The error message I get from this is:

ERR:  Matching Python module for AccumulatorRegion not found. [/root/bamboo-agent-home/xml-data/build-dir/NUP-CORE-NCRM/src/nupic/engine/RegionImplFactory.cpp line 377]
Traceback (most recent call last):
  File "testAccRegion.py", line 24, in <module>
    network.addRegion("ACC", "py.AccumulatorRegion", json.dumps(accParams))
  File "/usr/local/lib/python2.7/site-packages/nupic/engine/__init__.py", line 645, in addRegion
    engine_internal.Network.addRegion(self, name, nodeType, nodeParams)
  File "/usr/local/lib/python2.7/site-packages/nupic/bindings/engine_internal.py", line 1218, in addRegion
    return _engine_internal.Network_addRegion(self, *args, **kwargs)
RuntimeError: Matching Python module for AccumulatorRegion not found.

What is the correct way to add a new kind of region?

If I manage to get it all working I would be happy to share the code. Is there somewhere in particular that I should put it?

1 Like

You must first register your new AccumulatorRegion region type with the Network. Then you can create a region using it.

1 Like

Yes but how? My first post provided the code I had used to do the registration but it does not work – can you correct it?

1 Like

Yes but how?

Oh, sorry, I see you DID have a register in there. What you have; network.registerRegion( class) matches the documented API but that call is for registering a C++ class. Try network.registerPyRegion(module, className) Both arguments are strings. So something like

network.registerPyRegion("testAccRegion.py", "accumulator")

You can also try this with the HTM Community nupic.cpp repository. I would be interested in how well it works.

Thanks for your suggestion. I note you chose network.registerPyRegion whereas I chose network.registerRegion (without the Py). I have tried your suggestion, both literally as you posted it and what I believe is the correct rendering of it for my case, which is:

network.registerPyRegion("accumulator.py", "Accumulator")

In my set-up. accumulator.py is the name of the module which contains the class Accumulator, which contains the algorithmic implementation of the Network API module AccumulatorRegion.py. This is then all tested in my program testAccRegion.py whose code is in post#1 – the test code is unfinished because there is no point completing it until I can load my accumulator.py module as the implementation of the AccumulatorRegion — note the linkage between these is defined in the parameters of the “model” with is loaded. The problem seems to be that the arguments of networkPyRegion do not include AccumulatorRegion and for some reason that linkage is not being picked up from the parameters of the model.

By the way, note that my original code does not fail at the lines:

module = __import__("accumulator")
accRegionClass = getattr(module, "Accumulator")

which proves that the code for accumulator is capable of being found by the python class loader – it just does not know which code to load that relates to AccumulatorRegion (as it says in the error message provided in post #1.

Ok, let me try to understand this.

  • the class “Accumulator” in module “accumulator.py” is the algorithm
  • the class “AccumulatorRegion” in module “AccumulatorRegion.py” is the region wrapper.

If this is true then you want to register “AccumulatorRegion” with Network. “AccumulatorRegion” then will be responsible for calling “Accumulator” when Network calls “AccumulatorRegion.compute()”. It must also handle initialize(), getOutputElementCount, and getSpec( ). If you also get or set parameters you will also need to implement those as well. See http://nupic.docs.numenta.org/stable/api/network/regions.html

Network does not need to know anything about the class “Accumulator”.

network.registerPyRegion( "AccumulatorRegion.py", "AccumulatorRegion")

That makes it a lot clearer, thanks. That does fix the error message that I originally posted. It does now find module Accumulator.Region.py and run it (I know this because at first it reported a bug in it - which I have now fixed). And yes my AccumulatorRegion.py code as an initialize method (_init_) which has an argument “implementation” which is set to the correct name of the python module (accumulator). However, I now get a different error message (at the same line as before):

python testAccRegion.py
WARN:   PyRegion::createSpec failed: 0x7ffc5355f110
Traceback (most recent call last):
  File "testAccRegion.py", line 30, in <module>
    network.addRegion("ACC", "py.AccumulatorRegion", json.dumps(accParams))
  File "/usr/local/lib/python2.7/site-packages/nupic/engine/__init__.py", line 645, in addRegion
    engine_internal.Network.addRegion(self, name, nodeType, nodeParams)
  File "/usr/local/lib/python2.7/site-packages/nupic/bindings/engine_internal.py", line 1218, in addRegion
    return _engine_internal.Network_addRegion(self, *args, **kwargs)
ImportError: No module named py

I have no idea what this means. Nowhere in my code have I used py as the name as a module. Any ideas?

Does your region implement getSpec? This is a function that returns a static structure that describes your region to the Network API. It should contain descriptions for parameters and inputs/outputs. The docs are not much help but you can look at some other regions to get an idea as to how to format it.

ImportError: No module named py

I have no idea what this means either. From the traceback it looks like you got past the registration.
Originally there was a requirement to prepend “py.” to the class name…that requirement may have been removed. Try your addRegion call without the “py.” on the class name.

Removing the .py from AccumulatorRegion.py did the trick! - many thanks for your help.

(I still have a few other problems but I will try to figure them out myself before asking for any more help. )