From 5d1aa6afba300be74500d125433f5daf67887222 Mon Sep 17 00:00:00 2001 From: Vitaly Kruglikov Date: Wed, 31 May 2017 15:02:25 -0700 Subject: [PATCH 1/3] NUP-2354 Upgrade nupic.bindings dependency to v0.6.2 to pick up the capnp buffer-passing fix needed by this commit. NUP-2354 Added capnp serialization/deserialization support in TMRegion and TMShimMixin classes. NUP-2354 Fixed parameter specs of boolean TMRegion parameters to use "Bool" type instead of "UInt32". --- requirements.txt | 2 +- src/nupic/algorithms/backtracking_tm_shim.py | 19 +++- src/nupic/regions/tm_region.capnp | 19 ++++ src/nupic/regions/tm_region.py | 95 +++++++++++++++---- .../nupic/engine/network_checkpoint_test.py | 23 ++++- 5 files changed, 137 insertions(+), 21 deletions(-) create mode 100644 src/nupic/regions/tm_region.capnp diff --git a/requirements.txt b/requirements.txt index baf0f310fc..6b0524faa7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -18,5 +18,5 @@ prettytable==0.7.2 # When updating nupic.bindings, also update any shared dependencies to keep # versions in sync. -nupic.bindings==0.6.1 +nupic.bindings==0.6.2 numpy==1.11.2 diff --git a/src/nupic/algorithms/backtracking_tm_shim.py b/src/nupic/algorithms/backtracking_tm_shim.py index 580619697b..c9f49602b1 100644 --- a/src/nupic/algorithms/backtracking_tm_shim.py +++ b/src/nupic/algorithms/backtracking_tm_shim.py @@ -52,7 +52,7 @@ def __init__(self, permanenceDec=0.10, permanenceMax=1.0, activationThreshold=12, - predictedSegmentDecrement=0, + predictedSegmentDecrement=0.0, maxSegmentsPerCell=255, maxSynapsesPerSegment=255, globalDecay=0.10, @@ -82,6 +82,21 @@ def __init__(self, self.infActiveState = {"t": None} + @classmethod + def read(cls, proto): + """ + Intercepts TemporalMemory deserialization request in order to initialize + `self.infActiveState` + + @param proto (DynamicStructBuilder) Proto object + + @return (TemporalMemory) TemporalMemory shim instance + """ + tm = super(TMShimMixin, cls).read(proto) + tm.infActiveState = {"t": None} + return tm + + def compute(self, bottomUpInput, enableLearn, computeInfOutput=None): """ (From `backtracking_tm.py`) @@ -171,7 +186,7 @@ def __init__(self, permanenceDec=0.10, permanenceMax=1.0, activationThreshold=12, - predictedSegmentDecrement=0, + predictedSegmentDecrement=0.0, maxSegmentsPerCell=255, maxSynapsesPerSegment=255, globalDecay=0.10, diff --git a/src/nupic/regions/tm_region.capnp b/src/nupic/regions/tm_region.capnp new file mode 100644 index 0000000000..43fd76f5b3 --- /dev/null +++ b/src/nupic/regions/tm_region.capnp @@ -0,0 +1,19 @@ +@0xb9d11462f08c1dee; + +using import "/nupic/proto/TemporalMemoryProto.capnp".TemporalMemoryProto; + +# Next ID: 11 +struct TMRegionProto { + temporalImp @0 :Text; + temporalMemory @1 :TemporalMemoryProto; + columnCount @2 :UInt32; + inputWidth @3 :UInt32; + cellsPerColumn @4 :UInt32; + learningMode @5 :Bool; + inferenceMode @6 :Bool; + anomalyMode @7 :Bool; + topDownMode @8 :Bool; + computePredictedActiveCellIndices @9 :Bool; + orColumnOutputs @10 :Bool; +} + diff --git a/src/nupic/regions/tm_region.py b/src/nupic/regions/tm_region.py index 40ce08d80d..c3010fa117 100644 --- a/src/nupic/regions/tm_region.py +++ b/src/nupic/regions/tm_region.py @@ -21,12 +21,23 @@ import numpy import os + +try: + import capnp +except ImportError: + capnp = None + from nupic.bindings.regions.PyRegion import PyRegion from nupic.algorithms import (anomaly, backtracking_tm, backtracking_tm_cpp, backtracking_tm_shim) +if capnp: + from nupic.regions.tm_region_capnp import TMRegionProto + from nupic.support import getArgumentDescriptions + + gDefaultTemporalImp = 'py' @@ -183,7 +194,7 @@ def getConstraints(arg): cells per column must also be specified and the output size of the region should be set the same as columnCount""", accessMode='Read', - dataType='UInt32', + dataType='Bool', count=1, constraints='bool'), @@ -208,41 +219,44 @@ def getConstraints(arg): # The last group is for parameters that aren't strictly spatial or temporal otherSpec = dict( learningMode=dict( - description='1 if the node is learning (default 1).', + description='True if the node is learning (default True).', accessMode='ReadWrite', - dataType='UInt32', + dataType='Bool', count=1, + defaultValue=True, constraints='bool'), inferenceMode=dict( - description='1 if the node is inferring (default 0).', + description='True if the node is inferring (default False).', accessMode='ReadWrite', - dataType='UInt32', + dataType='Bool', count=1, + defaultValue=False, constraints='bool'), computePredictedActiveCellIndices=dict( - description='1 if active and predicted active indices should be computed', + description='True if active and predicted active indices should be computed', accessMode='Create', - dataType='UInt32', + dataType='Bool', count=1, - defaultValue=0, + defaultValue=False, constraints='bool'), anomalyMode=dict( - description='1 if an anomaly score is being computed', + description='True if an anomaly score is being computed', accessMode='Create', - dataType='UInt32', + dataType='Bool', count=1, - defaultValue=0, + defaultValue=False, constraints='bool'), topDownMode=dict( - description='1 if the node should do top down compute on the next call ' - 'to compute into topDownOut (default 0).', + description='True if the node should do top down compute on the next call ' + 'to compute into topDownOut (default False).', accessMode='ReadWrite', - dataType='UInt32', + dataType='Bool', count=1, + defaultValue=False, constraints='bool'), activeOutputCount=dict( @@ -319,7 +333,6 @@ def __init__(self, computePredictedActiveCellIndices=False, **kwargs): - # Which Temporal implementation? TemporalClass = _getTPClass(temporalImp) @@ -362,7 +375,7 @@ def __init__(self, self._fpLogTPOutput = None # Variables set up in initInNetwork() - self._tfdr = None # FDRTemporal instance + self._tfdr = None # FDRTemporal instance ############################################################################# @@ -716,7 +729,6 @@ def setParameter(self, parameterName, index, parameterValue): automatically by PyRegion's parameter set mechanism. The ones that need special treatment are explicitly handled here. """ - if parameterName in self._temporalArgNames: setattr(self._tfdr, parameterName, parameterValue) @@ -737,6 +749,7 @@ def setParameter(self, parameterName, index, parameterValue): else: raise Exception('Unknown parameter: ' + parameterName) + ############################################################################# # # Commands @@ -773,6 +786,54 @@ def finishLearning(self): ############################################################################# + @staticmethod + def getProtoType(): + """Return the pycapnp proto type that the class uses for serialization.""" + return TMRegionProto + + + def writeToProto(self, proto): + """Write state to proto object. + + proto: TMRegionProto capnproto object + """ + proto.temporalImp = self.temporalImp + proto.columnCount = self.columnCount + proto.inputWidth = self.inputWidth + proto.cellsPerColumn = self.cellsPerColumn + proto.learningMode = self.learningMode + proto.inferenceMode = self.inferenceMode + proto.anomalyMode = self.anomalyMode + proto.topDownMode = self.topDownMode + proto.computePredictedActiveCellIndices = ( + self.computePredictedActiveCellIndices) + proto.orColumnOutputs = self.orColumnOutputs + + self._tfdr.write(proto.temporalMemory) + + + @classmethod + def readFromProto(cls, proto): + """Read state from proto object. + + proto: TMRegionProto capnproto object + """ + instance = cls(proto.columnCount, proto.inputWidth, proto.cellsPerColumn) + + instance.temporalImp = proto.temporalImp + instance.learningMode = proto.learningMode + instance.inferenceMode = proto.inferenceMode + instance.anomalyMode = proto.anomalyMode + instance.topDownMode = proto.topDownMode + instance.computePredictedActiveCellIndices = ( + proto.computePredictedActiveCellIndices) + instance.orColumnOutputs = proto.orColumnOutputs + + instance._tfdr = _getTPClass(proto.temporalImp).read(proto.temporalMemory) + + return instance + + def __getstate__(self): """ Return serializable state. This function will return a version of the diff --git a/tests/integration/nupic/engine/network_checkpoint_test.py b/tests/integration/nupic/engine/network_checkpoint_test.py index 16602894d2..5d5e079e37 100755 --- a/tests/integration/nupic/engine/network_checkpoint_test.py +++ b/tests/integration/nupic/engine/network_checkpoint_test.py @@ -22,8 +22,9 @@ import unittest import numpy -from nupic.regions.sp_region import SPRegion from nupic.regions.record_sensor import RecordSensor +from nupic.regions.sp_region import SPRegion +from nupic.regions.tm_region import TMRegion from network_creation_common import createAndRunNetwork @@ -66,6 +67,26 @@ def testSPRegion(self): "Row {0} not equal: {1} vs. {2}".format(i, result1, result2)) + @unittest.skipUnless( + capnp, "pycapnp is not installed, skipping serialization test.") + def testTMRegion(self): + results1 = createAndRunNetwork(TMRegion, "bottomUpOut", + checkpointMidway=False, + temporalImp="tm_py") + + results2 = createAndRunNetwork(TMRegion, "bottomUpOut", + checkpointMidway=True, + temporalImp="tm_py") + + self.assertEqual(len(results1), len(results2)) + + for i in xrange(len(results1)): + result1 = list(results1[i].nonzero()[0]) + result2 = list(results2[i].nonzero()[0]) + self.assertEqual(result1, result2, + "Row {0} not equal: {1} vs. {2}".format(i, result1, result2)) + + def compareArrayResults(self, results1, results2): self.assertEqual(len(results1), len(results2)) From 611363dd997590a0d86fe1a45563e5770fad9e9b Mon Sep 17 00:00:00 2001 From: Vitaly Kruglikov Date: Wed, 31 May 2017 15:37:30 -0700 Subject: [PATCH 2/3] NUP-2354 Upgrade pytest dependency version to match the one in nupic.bindings v0.6.2 to work around version conflict "pkg_resources.VersionConflict: (pytest-cov 1.6 (/usr/local/lib/python2.7/dist-packages), Requirement.parse('pytest-cov==2.5.0'))" --- requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 6b0524faa7..436eb1cc17 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,8 +5,8 @@ mock==1.0.1 ordereddict==1.1 psutil==1.0.1 pytest==2.5.1 -pytest-cov==1.6 -pytest-xdist==1.8 +pytest-cov==2.5.0 +pytest-xdist==1.16.0 python-dateutil==2.1 PyYAML==3.10 unittest2==0.5.1 From d3552bda89263eb039b29ee780c92f9220d5e8dc Mon Sep 17 00:00:00 2001 From: Vitaly Kruglikov Date: Wed, 31 May 2017 15:54:24 -0700 Subject: [PATCH 3/3] Revert "NUP-2354 Upgrade pytest dependency version to match the one in nupic.bindings v0.6.2 to work around version" This reverts commit 611363dd997590a0d86fe1a45563e5770fad9e9b. We should have a separate PR to deal with all the python package incompatibilities brought about by dependency upgrades in nupic.core. --- requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 436eb1cc17..6b0524faa7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,8 +5,8 @@ mock==1.0.1 ordereddict==1.1 psutil==1.0.1 pytest==2.5.1 -pytest-cov==2.5.0 -pytest-xdist==1.16.0 +pytest-cov==1.6 +pytest-xdist==1.8 python-dateutil==2.1 PyYAML==3.10 unittest2==0.5.1