Skip to content

Commit

Permalink
Merge pull request #3654 from mrcslws/connections-is-a-data-structure
Browse files Browse the repository at this point in the history
Move segment/synapse cleanup out of Connections
  • Loading branch information
mrcslws authored Jun 2, 2017
2 parents 778788d + 4f370b0 commit b1f35fe
Show file tree
Hide file tree
Showing 8 changed files with 324 additions and 352 deletions.
10 changes: 5 additions & 5 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ coverage==3.7.1
mock==1.0.1
ordereddict==1.1
psutil==1.0.1
pytest==2.5.1
pytest-cov==1.6
pytest-xdist==1.8
pytest==3.0.7
pytest-cov==2.5.0
pytest-xdist==1.16.0
python-dateutil==2.1
PyYAML==3.10
unittest2==0.5.1
Expand All @@ -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
numpy==1.11.2
nupic.bindings==0.6.3
numpy==1.12.1
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ def findRequirements():
extras_require = {
# Default requirement based on system type
":platform_system=='Linux' or platform_system=='Darwin'":
["pycapnp==0.5.8"],
["pycapnp==0.5.12"],

# Superseded by platform_system-conditional requirement, but keeping
# empty extra for compatibility as recommended by setuptools doc.
Expand Down
120 changes: 5 additions & 115 deletions src/nupic/algorithms/connections.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@
class Segment(object):
""" Class containing minimal information to identify a unique segment """

__slots__ = ["cell", "flatIdx", "_synapses", "_lastUsedIteration", "_ordinal"]
__slots__ = ["cell", "flatIdx", "_synapses", "_ordinal"]

def __init__(self, cell, flatIdx, lastUsedIteration, ordinal):
def __init__(self, cell, flatIdx, ordinal):
"""
@param cell (int)
Index of the cell that this segment is on.
Expand All @@ -48,7 +48,6 @@ def __init__(self, cell, flatIdx, lastUsedIteration, ordinal):
self.cell = cell
self.flatIdx = flatIdx
self._synapses = set()
self._lastUsedIteration = lastUsedIteration
self._ordinal = ordinal


Expand All @@ -60,7 +59,6 @@ def __eq__(self, other):
"""

return (self.cell == other.cell and
self._lastUsedIteration == other._lastUsedIteration and
(sorted(self._synapses, key=lambda x: x._ordinal) ==
sorted(other._synapses, key=lambda x: x._ordinal)))

Expand Down Expand Up @@ -134,18 +132,11 @@ class Connections(object):
""" Class to hold data representing the connectivity of a
collection of cells. """

def __init__(self,
numCells,
maxSegmentsPerCell=255,
maxSynapsesPerSegment=255):
def __init__(self, numCells):
""" @param numCells (int) Number of cells in collection """

# Save member variables
self.numCells = numCells
assert maxSegmentsPerCell > 0
assert maxSynapsesPerSegment > 0
self.maxSegmentsPerCell = maxSegmentsPerCell
self.maxSynapsesPerSegment = maxSynapsesPerSegment

self._cells = [CellData() for _ in xrange(numCells)]
self._synapsesForPresynapticCell = defaultdict(set)
Expand All @@ -154,7 +145,6 @@ def __init__(self,
self._numSynapses = 0
self._freeFlatIdxs = []
self._nextFlatIdx = 0
self._iteration = 0

# Whenever creating a new Synapse or Segment, give it a unique ordinal.
# These can be used to sort synapses or segments by age.
Expand Down Expand Up @@ -226,57 +216,6 @@ def getSegment(self, cell, idx):
return self._cells[cell]._segments[idx]


def _leastRecentlyUsedSegment(self, cell):
""" Find this cell's segment that was least recently used.
Implement this explicitly to make sure that tie-breaking is consistent.
When there's a tie, choose the oldest segment.
@param cell (int) Cell to query.
@return (Object) Least recently used segment.
"""
minSegment = None
minIteration = float("inf")

for segment in self.segmentsForCell(cell):
if segment._lastUsedIteration < minIteration:
minSegment = segment
minIteration = segment._lastUsedIteration

assert minSegment is not None

return minSegment


def _minPermanenceSynapse(self, segment):
""" Find this segment's synapse with the smallest permanence.
This method is NOT equivalent to a simple min() call. It uses an EPSILON to
account for floating point differences between C++ and Python.
@param segment (Object) Segment to query.
@return (Object) Synapse with the minimal permanence
Note: On ties it will choose the first occurrence of the minimum permanence.
"""
minSynapse = None
minPermanence = float("inf")

for synapse in sorted(self.synapsesForSegment(segment),
key=lambda s: s._ordinal):
if synapse.permanence < minPermanence - EPSILON:
minSynapse = synapse
minPermanence = synapse.permanence

assert minSynapse is not None

return minSynapse


def segmentForFlatIdx(self, flatIdx):
""" Get the segment with the specified flatIdx.
Expand Down Expand Up @@ -313,9 +252,6 @@ def createSegment(self, cell):
@return (int) New segment index
"""
while self.numSegments(cell) >= self.maxSegmentsPerCell:
self.destroySegment(self._leastRecentlyUsedSegment(cell))

cellData = self._cells[cell]

if len(self._freeFlatIdxs) > 0:
Expand All @@ -328,7 +264,7 @@ def createSegment(self, cell):
ordinal = self._nextSegmentOrdinal
self._nextSegmentOrdinal += 1

segment = Segment(cell, flatIdx, self._iteration, ordinal)
segment = Segment(cell, flatIdx, ordinal)
cellData._segments.append(segment)
self._segmentForFlatIdx[flatIdx] = segment

Expand Down Expand Up @@ -366,10 +302,6 @@ def createSynapse(self, segment, presynapticCell, permanence):
@return (Object) created Synapse object
"""

while self.numSynapses(segment) >= self.maxSynapsesPerSegment:
self.destroySynapse(self._minPermanenceSynapse(segment))

idx = len(segment._synapses)
synapse = Synapse(segment, presynapticCell, permanence,
self._nextSynapseOrdinal)
Expand Down Expand Up @@ -444,22 +376,6 @@ def computeActivity(self, activePresynapticCells, connectedPermanence):
numActivePotentialSynapsesForSegment)


def recordSegmentActivity(self, segment):
""" Record the fact that a segment had some activity. This information is
used during segment cleanup.
@param segment The segment that had some activity.
"""
segment._lastUsedIteration = self._iteration


def startNewIteration(self):
""" Mark the passage of time. This information is used during segment
cleanup.
"""
self._iteration += 1


def numSegments(self, cell=None):
""" Returns the number of segments.
Expand Down Expand Up @@ -515,17 +431,10 @@ def write(self, proto):
for j, segment in enumerate(segments):
synapses = segment._synapses
protoSynapses = protoSegments[j].init('synapses', len(synapses))
protoSegments[j].destroyed = False
protoSegments[j].lastUsedIteration = segment._lastUsedIteration

for k, synapse in enumerate(sorted(synapses, key=lambda s: s._ordinal)):
protoSynapses[k].presynapticCell = synapse.presynapticCell
protoSynapses[k].permanence = synapse.permanence
protoSynapses[k].destroyed = False

proto.maxSegmentsPerCell = self.maxSegmentsPerCell
proto.maxSynapsesPerSegment = self.maxSynapsesPerSegment
proto.iteration = self._iteration


@classmethod
Expand All @@ -538,9 +447,7 @@ def read(cls, proto):
"""
#pylint: disable=W0212
protoCells = proto.cells
connections = cls(len(protoCells),
proto.maxSegmentsPerCell,
proto.maxSynapsesPerSegment)
connections = cls(len(protoCells))

for cellIdx, protoCell in enumerate(protoCells):
protoCell = protoCells[cellIdx]
Expand All @@ -549,11 +456,7 @@ def read(cls, proto):
segments = connections._cells[cellIdx]._segments

for segmentIdx, protoSegment in enumerate(protoSegments):
if protoSegment.destroyed:
continue

segment = Segment(cellIdx, connections._nextFlatIdx,
protoSegment.lastUsedIteration,
connections._nextSegmentOrdinal)

segments.append(segment)
Expand All @@ -565,9 +468,6 @@ def read(cls, proto):
protoSynapses = protoSegment.synapses

for synapseIdx, protoSynapse in enumerate(protoSynapses):
if protoSynapse.destroyed:
continue

presynapticCell = protoSynapse.presynapticCell
synapse = Synapse(segment, presynapticCell, protoSynapse.permanence,
ordinal=connections._nextSynapseOrdinal)
Expand All @@ -577,7 +477,6 @@ def read(cls, proto):

connections._numSynapses += 1

connections._iteration = proto.iteration
#pylint: enable=W0212
return connections

Expand All @@ -589,11 +488,6 @@ def __eq__(self, other):
@param other (Connections) Connections instance to compare to
"""
#pylint: disable=W0212
if self.maxSegmentsPerCell != other.maxSegmentsPerCell:
return False
if self.maxSynapsesPerSegment != other.maxSynapsesPerSegment:
return False

for i in xrange(self.numCells):
segments = self._cells[i]._segments
otherSegments = other._cells[i]._segments
Expand All @@ -607,8 +501,6 @@ def __eq__(self, other):
synapses = segment._synapses
otherSynapses = otherSegment._synapses

if segment._lastUsedIteration != otherSegment._lastUsedIteration:
return False
if len(synapses) != len(otherSynapses):
return False

Expand Down Expand Up @@ -644,8 +536,6 @@ def __eq__(self, other):

if self._numSynapses != other._numSynapses:
return False
if self._iteration != other._iteration:
return False

#pylint: enable=W0212
return True
Expand Down
Loading

0 comments on commit b1f35fe

Please sign in to comment.