Skip to content

Commit

Permalink
Merge pull request #601 from htm-community/fix_conn_stimulusThreshold
Browse files Browse the repository at this point in the history
Fix Connections segment pruning
  • Loading branch information
breznak authored Jun 4, 2020
2 parents fa7f5a8 + 690047c commit c50a669
Show file tree
Hide file tree
Showing 9 changed files with 270 additions and 272 deletions.
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v2.1.1
v2.1.15
9 changes: 8 additions & 1 deletion bindings/py/cpp_src/bindings/algorithms/py_Connections.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,14 @@ R"(Returns pair of:
numActiveConnectedSynapsesForSegment
numActivePotentialSynapsesForSegment)");

py_Connections.def("adaptSegment", &Connections::adaptSegment);
py_Connections.def("adaptSegment", &Connections::adaptSegment,
py::arg("segment"),
py::arg("inputs"),
py::arg("increment"),
py::arg("decrement"),
py::arg("pruneZeroSynapses") = false,
py::arg("segmentThreshold") = 0
);

py_Connections.def("raisePermanencesToThreshold", &Connections::raisePermanencesToThreshold);

Expand Down
113 changes: 109 additions & 4 deletions bindings/py/tests/algorithms/connections_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,15 +78,17 @@ def testAdaptShouldRemoveSegments(self):

connections = Connections(NUM_CELLS, 0.51)
for i in range(NUM_CELLS):
seg = connections.createSegment(i, 1)
seg = connections.createSegment(i, 2)
seg = connections.createSegment(i, 2) #create 2 segments on each cell

for cell in active_cells:
segments = connections.segmentsForCell(cell)
self.assertEqual(len(segments), 1, "Segments were prematurely destroyed.")
self.assertEqual(len(segments), 2, "Segments were prematurely destroyed.")
segment = segments[0]
connections.adaptSegment(segment, inputSDR, 0.1, 0.001, True)
numSynapsesOnSegment = len(segments)
connections.adaptSegment(segment, inputSDR, 0.1, 0.001, pruneZeroSynapses=True, segmentThreshold=1) #set to =1 so that segments get always deleted in this test
segments = connections.segmentsForCell(cell)
self.assertEqual(len(segments), 0, "Segments were not destroyed.")
self.assertEqual(len(segments), 1, "Segments were not destroyed.")

def testAdaptShouldIncrementSynapses(self):
"""
Expand Down Expand Up @@ -163,6 +165,66 @@ def testAdaptShouldDecrementSynapses(self):
self.assertEqual(presynamptic_cells, presynaptic_input_set, "Missing synapses")



def testCreateSynapse(self):
# empty connections, create segment and a synapse
co = Connections(NUM_CELLS, 0.51)
self.assertEqual(co.numSynapses(), 0)
self.assertEqual(co.numSegments(), 0)

# 1st, create a segment
seg = co.createSegment(NUM_CELLS-1, 1)
self.assertEqual(co.numSegments(), 1)

#1. create a synapse on that segment
syn1 = co.createSynapse(seg, NUM_CELLS-1, 0.52)
self.assertEqual(pytest.approx(co.permanenceForSynapse(syn1)), 0.52)
self.assertEqual(co.numSynapses(), 1)

#2. creating a duplicit synapse should not crash!
syn2 = co.createSynapse(seg, NUM_CELLS-1, 0.52)
self.assertEqual( syn1, syn2, "creating duplicate synapses should return the same")
self.assertEqual(co.numSynapses(), 1, "Duplicate synapse, number should not increase")

#3. create a different synapse
syn3 = co.createSynapse(seg, 1, 0.52)
self.assertNotEqual( syn1, syn3, "creating a different synapse must create a new one")
self.assertEqual(co.numSynapses(), 2, "New synapse should increase the number")

#4. create existing synapse with a new value -> should update permanence
#4.a lower permanence -> keep max()
syn4 = co.createSynapse(seg, NUM_CELLS-1, 0.11) #all the same just permanence is a lower val
self.assertEqual( syn1, syn4, "just updating existing syn")
self.assertEqual(co.numSynapses(), 2, "Duplicate synapse, number should not increase")
self.assertEqual(pytest.approx(co.permanenceForSynapse(syn1)), 0.52, "update keeps the larger value")

#4.b higher permanence -> update
syn5 = co.createSynapse(seg, NUM_CELLS-1, 0.99) #all the same just permanence is a higher val
self.assertEqual( syn1, syn5, "just updating existing syn")
self.assertEqual(co.numSynapses(), 2, "Duplicate synapse, number should not increase")
self.assertEqual(pytest.approx(co.permanenceForSynapse(syn1)), 0.99, "updated to the larger permanence value")



def testDestroySynapse(self):
# empty connections, create segment seg and a synapse syn
co = Connections(NUM_CELLS, 0.51)
seg = co.createSegment(NUM_CELLS-1, 1)
syn1 = co.createSynapse(seg, NUM_CELLS-1, 0.52)

# destroy the synapse
co.destroySynapse(syn1)
self.assertEqual(co.numSynapses(), 0)

with pytest.raises(RuntimeError): # NTA_CHECK, data for removed synapse must not be accessible!
permRemoved = co.permanenceForSynapse(syn1)
assert permRemoved == perm1

# double remove should be just ignored
co.destroySynapse(syn1)



def testNumSynapses(self):
"""
Test that connections are generated on predefined segments.
Expand Down Expand Up @@ -346,6 +408,49 @@ def testConnectedThreshold(self):
conn = Connections(1024, 0.123, False)
self.assertAlmostEqual(conn.connectedThreshold, 0.123, places=4)

def testCreateSegment(self):
co = Connections(NUM_CELLS, 0.51)
self.assertEqual(co.numSegments(), 0, "there are zero segments yet")

# create segment
co.createSegment(NUM_CELLS-1, 20)
self.assertEqual(co.numSegments(), 1, "created 1 new segment")

# wrong param
with pytest.raises(RuntimeError):
co.createSegment(1, 0) #wrong param maxSegmentsPerCell "0"

# wrong param - OOB cell
with pytest.raises(RuntimeError):
co.createSegment(NUM_CELLS+22, 1)

# another segment
co.createSegment(NUM_CELLS-1, 20)
self.assertEqual(co.numSegments(), 2)

# segment pruning -> reduce to 1 seg per cell
co.createSegment(NUM_CELLS-1, 1)
self.assertEqual(co.numSegments(), 1)


def testDestroySegment(self):
co = Connections(NUM_CELLS, 0.51)
self.assertEqual(co.numSegments(), 0, "there are zero segments yet")

# removing while no segments exist
co.destroySegment(1)

# successfully remove
seg = co.createSegment(1, 20)
self.assertEqual(co.numSegments(), 1)
n = co.numConnectedSynapses(seg) #uses dataForSegment()
co.destroySegment(seg)
self.assertEqual(co.numSegments(), 0, "segment should have been removed")
with pytest.raises(RuntimeError):
n2 = co.numConnectedSynapses(seg)




if __name__ == "__main__":
unittest.main()
201 changes: 0 additions & 201 deletions py/tests/advanced/algorithms/advanced_connections_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,207 +38,6 @@ def _getPresynapticCells(self, connections, segment, threshold):
return set([connections.presynapticCellForSynapse(synapse) for synapse in connections.synapsesForSegment(segment)
if connections.permanenceForSynapse(synapse) >= threshold])

def testAdaptShouldNotRemoveSegments(self):
"""
Test that connections are generated on predefined segments.
"""
random = Random(1981)
active_cells = np.array(random.sample(np.arange(0, NUM_CELLS, 1, dtype="uint32"), 40), dtype="uint32")
active_cells.sort()

presynaptic_input = list(range(0, 10))
inputSDR = SDR(1024)
inputSDR.sparse = presynaptic_input

connections = Connections(NUM_CELLS, 0.51)
for i in range(NUM_CELLS):
connections.createSegment(i, 1)

for cell in active_cells:
segments = connections.segmentsForCell(cell)
self.assertEqual(len(segments), 1, "Segments were destroyed.")
segment = segments[0]
connections.adaptSegment(segment, inputSDR, 0.1, 0.001, False)

segments = connections.segmentsForCell(cell)
self.assertEqual(len(segments), 1, "Segments were destroyed.")
segment = segments[0]

def testAdaptShouldRemoveSegments(self):
"""
Test that connections are generated on predefined segments.
"""
random = Random(1981)
active_cells = np.array(random.sample(np.arange(0, NUM_CELLS, 1, dtype="uint32"), 40), dtype="uint32")
active_cells.sort()

presynaptic_input = list(range(0, 10))
inputSDR = SDR(1024)
inputSDR.sparse = presynaptic_input

connections = Connections(NUM_CELLS, 0.51)
for i in range(NUM_CELLS):
connections.createSegment(i, 1)

for cell in active_cells:
segments = connections.segmentsForCell(cell)
self.assertEqual(len(segments), 1, "Segments were prematurely destroyed.")
segment = segments[0]
connections.adaptSegment(segment, inputSDR, 0.1, 0.001, True)
segments = connections.segmentsForCell(cell)
self.assertEqual(len(segments), 0, "Segments were not destroyed.")

def testAdaptShouldIncrementSynapses(self):
"""
Test that connections are generated on predefined segments.
"""
random = Random(1981)
active_cells = np.array(random.sample(np.arange(0, NUM_CELLS, 1, dtype="uint32"), 40), dtype="uint32")
active_cells.sort()

presynaptic_input = list(range(0, 10))
presynaptic_input_set = set(presynaptic_input)
inputSDR = SDR(1024)
inputSDR.sparse = presynaptic_input

connections = Connections(NUM_CELLS, 0.51)
for i in range(NUM_CELLS):
connections.createSegment(i, 1)

for cell in active_cells:
segments = connections.segmentsForCell(cell)
segment = segments[0]
for c in presynaptic_input:
connections.createSynapse(segment, c, 0.1)
connections.adaptSegment(segment, inputSDR, 0.1, 0.001, True)

presynamptic_cells = self._getPresynapticCells(connections, segment, 0.2)
self.assertEqual(presynamptic_cells, presynaptic_input_set, "Missing synapses")

presynamptic_cells = self._getPresynapticCells(connections, segment, 0.3)
self.assertEqual(presynamptic_cells, set(), "Too many synapses")

def testAdaptShouldDecrementSynapses(self):
"""
Test that connections are generated on predefined segments.
"""
random = Random(1981)
active_cells = np.array(random.sample(np.arange(0, NUM_CELLS, 1, dtype="uint32"), 40), dtype="uint32")
active_cells.sort()

presynaptic_input = list(range(0, 10))
presynaptic_input_set = set(presynaptic_input)
inputSDR = SDR(1024)
inputSDR.sparse = presynaptic_input

connections = Connections(NUM_CELLS, 0.51)
for i in range(NUM_CELLS):
connections.createSegment(i, 1)

for cell in active_cells:
segments = connections.segmentsForCell(cell)
segment = segments[0]
for c in presynaptic_input:
connections.createSynapse(segment, c, 0.1)

connections.adaptSegment(segment, inputSDR, 0.1, 0.0, False)

presynamptic_cells = self._getPresynapticCells(connections, segment, 0.2)
self.assertEqual(presynamptic_cells, presynaptic_input_set, "Missing synapses")

presynaptic_input1 = list(range(0, 5))
presynaptic_input_set1 = set(presynaptic_input1)
inputSDR.sparse = presynaptic_input1

for cell in active_cells:
segments = connections.segmentsForCell(cell)
segment = segments[0]
connections.adaptSegment(segment, inputSDR, 0.0, 0.1, False)


presynamptic_cells = self._getPresynapticCells(connections, segment, 0.2)
self.assertEqual(presynamptic_cells, presynaptic_input_set1, "Too many synapses")

presynamptic_cells = self._getPresynapticCells(connections, segment, 0.1)
self.assertEqual(presynamptic_cells, presynaptic_input_set, "Missing synapses")


def testNumSynapses(self):
"""
Test that connections are generated on predefined segments.
"""
random = Random(1981)
active_cells = np.array(random.sample(np.arange(0, NUM_CELLS, 1, dtype="uint32"), 40), dtype="uint32")
active_cells.sort()

presynaptic_input = list(range(0, 10))
inputSDR = SDR(1024)
inputSDR.sparse = presynaptic_input

connections = Connections(NUM_CELLS, 0.3)
for i in range(NUM_CELLS):
connections.createSegment(i, 1)

for cell in active_cells:
segments = connections.segmentsForCell(cell)
segment = segments[0]
for c in presynaptic_input:
connections.createSynapse(segment, c, 0.1)

connections.adaptSegment(segment, inputSDR, 0.1, 0.0, False)

num_synapses = connections.numSynapses(segment)
self.assertEqual(num_synapses, len(presynaptic_input), "Missing synapses")

self.assertEqual(connections.numSynapses(), len(presynaptic_input) * 40, "Missing synapses")


def testNumConnectedSynapses(self):
"""
Test that connections are generated on predefined segments.
"""
random = Random(1981)
active_cells = np.array(random.sample(np.arange(0, NUM_CELLS, 1, dtype="uint32"), 40), dtype="uint32")
active_cells.sort()

presynaptic_input = list(range(0, 10))
inputSDR = SDR(1024)
inputSDR.sparse = presynaptic_input

connections = Connections(NUM_CELLS, 0.2)
for i in range(NUM_CELLS):
connections.createSegment(i, 1)

for cell in active_cells:
segments = connections.segmentsForCell(cell)
segment = segments[0]
for c in presynaptic_input:
connections.createSynapse(segment, c, 0.1)

connections.adaptSegment(segment, inputSDR, 0.1, 0.0, False)

connected_synapses = connections.numConnectedSynapses(segment)
self.assertEqual(connected_synapses, len(presynaptic_input), "Missing synapses")

presynaptic_input1 = list(range(0, 5))
inputSDR.sparse = presynaptic_input1

total_connected = 0

for cell in active_cells:
segments = connections.segmentsForCell(cell)
segment = segments[0]
connections.adaptSegment(segment, inputSDR, 0.0, 0.1, False)

connected_synapses = connections.numConnectedSynapses(segment)
self.assertEqual(connected_synapses, len(presynaptic_input1), "Missing synapses")

total_connected += connected_synapses

connected_synapses = connections.numSynapses(segment)
self.assertEqual(connected_synapses, len(presynaptic_input), "Missing synapses")

self.assertEqual(total_connected, len(presynaptic_input1) * 40, "Missing synapses")

def testComputeActivity(self):
"""
Expand Down
6 changes: 3 additions & 3 deletions src/examples/hello/HelloSPTP.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -213,12 +213,12 @@ EPOCHS = 2; // make test faster in Debug

SDR goldTM({COLS});
const SDR_sparse_t deterministicTM{
87, 93, 102, 282, 303, 308, 337, 340, 502, 542, 952, 1115, 1502, 1518, 1626, 1691, 1694, 1711, 1727, 1760, 1775, 1781, 1804, 1805, 1827, 1831, 1832, 1844, 1851, 1858, 1859, 1918, 1929, 1931, 1941, 1943, 1945, 1952, 1953, 1955, 1956, 1958, 1960, 1961, 1965, 1973, 1975, 1976, 1979, 1980, 1984, 1985, 1986, 1987, 1994, 1996, 1998, 2002, 2006, 2013, 2042
72, 85, 102, 114, 122, 126, 287, 308, 337, 339, 542, 920, 939, 952, 1268, 1507, 1508, 1518, 1546, 1547, 1626, 1627, 1633, 1668, 1727, 1804, 1805, 1827, 1832, 1844, 1859, 1862, 1918, 1920, 1924, 1931, 1933, 1945, 1961, 1965, 1966, 1968, 1970, 1973, 1975, 1976, 1977, 1979, 1986, 1987, 1991, 1992, 1996, 1998, 2002, 2006, 2008, 2012, 2042, 2045
};
goldTM.setSparse(deterministicTM);

const float goldAn = 0.715686f; //Note: this value is for a (randomly picked) datapoint, it does not have to improve (decrease) with better algorithms
const float goldAnAvg = 0.412719f; // ...the averaged value, on the other hand, should improve/decrease.
const float goldAn = 0.637255f; //Note: this value is for a (randomly picked) datapoint, it does not have to improve (decrease) with better algorithms
const float goldAnAvg = 0.40804f; // ...the averaged value, on the other hand, should improve/decrease.

#ifdef _ARCH_DETERMINISTIC
if(e+1 == 5000) {
Expand Down
Loading

0 comments on commit c50a669

Please sign in to comment.