Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move segment/synapse cleanup out of Connections (Python) #3654

Merged
merged 4 commits into from
Jun 2, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you I hate seeing assert outside test code.

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