/* ---------------------------------------------------------------------
* Numenta Platform for Intelligent Computing (NuPIC)
* Copyright (C) 2014, Numenta, Inc. Unless you have an agreement
* with Numenta, Inc., for a separate license for this software code, the
* following terms and conditions apply:
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero Public License version 3 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Affero Public License for more details.
*
* You should have received a copy of the GNU Affero Public License
* along with this program. If not, see http://www.gnu.org/licenses.
*
* http://numenta.org/licenses/
* ---------------------------------------------------------------------
*/
package org.numenta.nupic.integration;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import org.junit.Test;
import org.numenta.nupic.Parameters;
import org.numenta.nupic.Parameters.KEY;
import org.numenta.nupic.datagen.PatternMachine;
import org.numenta.nupic.datagen.SequenceMachine;
import org.numenta.nupic.monitor.mixin.IndicesTrace;
import org.numenta.nupic.monitor.mixin.Metric;
/**
* ==============================================================================
* Basic First Order Sequences
* ==============================================================================
*
* These tests ensure the most basic (first order) sequence learning mechanism is
* working.
*
* Parameters: Use a "fast learning mode": initPerm should be greater than
* connectedPerm and permanenceDec should be zero. With these settings sequences
* should be learned in one pass:
*
* minThreshold = newSynapseCount
* initialPermanence = 0.8
* connectedPermanence = 0.7
* permanenceDecrement = 0
* permanenceIncrement = 0.4
*
* Other Parameters:
* columnDimensions = [100]
* cellsPerColumn = 1
* newSynapseCount = 11
* activationThreshold = 11
* Note: this is not a high order sequence, so one cell per column is fine.
*
* Input Sequence: We train with M input sequences, each consisting of N random
* patterns. Each pattern consists of a random number of bits on. The number of
* 1's in each pattern should be between 21 and 25 columns.
*
* Each input pattern can optionally have an amount of spatial noise represented
* by X, where X is the probability of switching an on bit with a random bit.
*
* Training: The TP is trained with P passes of the M sequences. There
* should be a reset between sequences. The total number of iterations during
* training is P*N*M.
*
* Testing: Run inference through the same set of sequences, with a reset before
* each sequence. For each sequence the system should accurately predict the
* pattern at the next time step up to and including the N-1'st pattern. The number
* of predicted inactive cells at each time step should be reasonably low.
*
* We can also calculate the number of synapses that should be
* learned. We raise an error if too many or too few were learned.
*
* B1) Basic sequence learner. M=1, N=100, P=1.
*
* B2) Same as above, except P=2. Test that permanences go up and that no
* additional synapses are learned. [TODO]
*
* B3) N=300, M=1, P=1. (See how high we can go with N)
*
* B4) N=100, M=3, P=1. (See how high we can go with N*M)
*
* B5) Like B1 but with cellsPerColumn = 4. First order sequences should still
* work just fine.
*
* B6) Like B4 but with cellsPerColumn = 4. First order sequences should still
* work just fine.
*
* B7) Like B1 but with slower learning. Set the following parameters differently:
*
* activationThreshold = newSynapseCount
* minThreshold = activationThreshold
* initialPerm = 0.2
* connectedPerm = 0.7
* permanenceInc = 0.2
*
* Now we train the TP with the B1 sequence 4 times (P=4). This will increment
* the permanences to be above 0.8 and at that point the inference will be correct.
* This test will ensure the basic match function and segment activation rules are
* working correctly.
*
* B8) Like B7 but with 4 cells per column. Should still work.
*
* B9) Like B7 but present the sequence less than 4 times: the inference should be
* incorrect.
*
* B10) Like B2, except that cells per column = 4. Should still add zero additional
* synapses. [TODO]
*
* B11) Like B5, but with activationThreshold = 8 and with each pattern
* corrupted by a small amount of spatial noise (X = 0.05).
*
*
* ==============================================================================
* High Order Sequences
* ==============================================================================
*
* These tests ensure that high order sequences can be learned in a multiple cells
* per column instantiation.
*
* Parameters: Same as Basic First Order Tests above, but with varying cells per
* column.
*
* Input Sequence: We train with M input sequences, each consisting of N random
* patterns. Each pattern consists of a random number of bits on. The number of
* 1's in each pattern should be between 21 and 25 columns. The sequences are
* constructed to contain shared subsequences, such as:
*
* A B C D E F G H I J
* K L M D E F N O P Q
*
* The position and length of shared subsequences are parameters in the tests.
*
* Each input pattern can optionally have an amount of spatial noise represented
* by X, where X is the probability of switching an on bit with a random bit.
*
* Training: Identical to basic first order tests above.
*
* Testing: Identical to basic first order tests above unless noted.
*
* We can also calculate the number of segments and synapses that should be
* learned. We raise an error if too many or too few were learned.
*
* H1) Learn two sequences with a shared subsequence in the middle. Parameters
* should be the same as B1. Since cellsPerColumn == 1, it should make more
* predictions than necessary.
*
* H2) Same as H1, but with cellsPerColumn == 4, and train multiple times.
* It should make just the right number of predictions.
*
* H3) Like H2, except the shared subsequence is in the beginning (e.g.
* "ABCDEF" and "ABCGHIJ"). At the point where the shared subsequence ends, all
* possible next patterns should be predicted. As soon as you see the first unique
* pattern, the predictions should collapse to be a perfect prediction.
*
* H4) Shared patterns. Similar to H2 except that patterns are shared between
* sequences. All sequences are different shufflings of the same set of N
* patterns (there is no shared subsequence).
*
* H5) Combination of H4) and H2). Shared patterns in different sequences, with a
* shared subsequence.
*
* H6) Stress test: every other pattern is shared. [TODO]
*
* H7) Start predicting in the middle of a sequence. [TODO]
*
* H8) Hub capacity. How many patterns can use that hub? [TODO]
*
* H9) Sensitivity to small amounts of spatial noise during inference (X = 0.05).
* Parameters the same as B11, and sequences like H2.
*
* H10) Higher order patterns with alternating elements.
*
* Create the following 4 sequences:
*
* A B A B A C
* A B A B D E
* A B F G H I
* A J K L M N
*
* After training we should verify that the expected transitions are in the
* model. Prediction accuracy should be perfect. In addition, during inference,
* after the first element is presented, the columns should not burst any more.
* Need to verify, for the first sequence, that the high order representation
* when presented with the second A and B is different from the representation
* in the first presentation. [TODO]
*
* @author Chetan Surpur
* @author David Ray
*
* @see AbstractTemporalMemoryTest
* @see BasicTemporalMemoryTest
*/
public class ExtensiveTemporalMemoryTest extends AbstractTemporalMemoryTest {
private static final PatternMachine PATTERN_MACHINE = new PatternMachine(100, 23, 300, true);
private static final int VERBOSITY = 1;
/**
* Basic sequence learner. M=1, N=100, P=1.
*/
@Test
public void testB1() {
init(null, PATTERN_MACHINE);
// Instead of implementing the Python "shuffle" method, just use the exact output
Integer[] shuffledNums = new Integer[] {
83, 53, 70, 45, 44, 39, 22, 80, 10, 0, 18, 30, 73, 33, 90, 4, 76, 77, 12, 31, 55, 88,
26, 42, 69, 15, 40, 96, 9, 72, 11, 47, 85, 28, 93, 5, 66, 65, 35, 16, 49, 34, 7, 95,
27, 19, 81, 25, 62, 13, 24, 3, 17, 38, 8, 78, 6, 64, 36, 89, 56, 99, 54, 43, 50, 67,
46, 68, 61, 97, 79, 41, 58, 48, 98, 57, 75, 32, 94, 59, 63, 84, 37, 29, 1, 52, 21, 2,
23, 87, 91, 74, 86, 82, 20, 60, 71, 14, 92, 51, -1
};
List<Integer> numberList = Arrays.asList(shuffledNums);
List<Set<Integer>> sequence = sequenceMachine.generateFromNumbers(numberList);
feedTM(sequence, "", true, 1);
testTM(sequence, "");
assertAllActiveWerePredicted();
assertAllInactiveWereUnpredicted();
}
/**
* N=300, M=1, P=1. (See how high we can go with N)
*/
@Test
public void testB3() {
init(null, PATTERN_MACHINE);
// Instead of implementing the Python "shuffle" method, just use the exact output
Integer[] shuffledNums = new Integer[] {
203, 266, 152, 9, 233, 226, 196, 109, 5, 175, 237, 57, 218, 45, 182, 221, 289, 211,
148, 165, 78, 113, 249, 250, 104, 42, 281, 295, 157, 238, 17, 164, 33, 24, 215, 119,
7, 90, 46, 73, 93, 76, 286, 60, 77, 63, 234, 229, 111, 231, 180, 144, 239, 75, 297, 278,
97, 92, 192, 25, 232, 59, 6, 185, 173, 30, 22, 256, 56, 186, 108, 126, 230, 193, 129,
282, 82, 84, 66, 288, 163, 154, 19, 124, 79, 114, 118, 72, 15, 10, 194, 101, 68, 224,
37, 16, 179, 147, 274, 67, 228, 69, 31, 183, 265, 225, 140, 18, 181, 96, 132, 262, 86,
248, 245, 116, 146, 292, 197, 206, 55, 172, 184, 167, 139, 253, 38, 125, 195, 283, 137,
112, 168, 117, 277, 271, 155, 176, 178, 2, 115, 143, 177, 120, 210, 260, 127, 74, 29, 83,
269, 107, 223, 158, 280, 246, 222, 65, 198, 85, 213, 159, 12, 35, 28, 142, 284, 254, 170,
51, 95, 208, 247, 41, 89, 244, 136, 26, 293, 141, 200, 0, 268, 272, 100, 259, 255, 171,
98, 36, 61, 150, 236, 202, 242, 11, 296, 267, 27, 219, 4, 122, 32, 204, 162, 209, 285,
138, 62, 135, 128, 290, 8, 70, 264, 64, 44, 279, 156, 40, 123, 275, 216, 153, 23, 261,
110, 81, 207, 212, 39, 240, 291, 258, 199, 14, 47, 94, 263, 227, 273, 201, 161, 43, 217,
145, 190, 220, 251, 3, 105, 53, 133, 1, 131, 103, 49, 80, 205, 34, 91, 52, 241, 13, 88,
166, 294, 134, 287, 243, 54, 50, 174, 189, 298, 187, 169, 58, 48, 235, 252, 21, 160, 276,
191, 257, 149, 130, 151, 99, 87, 214, 121, 299, 20, 188, 71, 106, 270, 102, -1
};
List<Integer> numberList = Arrays.asList(shuffledNums);
List<Set<Integer>> sequence = sequenceMachine.generateFromNumbers(numberList);
feedTM(sequence, "", true, 1);
testTM(sequence, "");
assertAllActiveWerePredicted();
assertAllInactiveWereUnpredicted();
}
/**
* Basic sequence learner. M=1, N=100, P=1.
*/
@Test
public void testB4() {
init(null, PATTERN_MACHINE);
// Instead of implementing the Python "shuffle" method, just use the exact output
Integer[] shuffledNums = new Integer[] {
83, 53, 70, 45, 44, 39, 22, 80, 10, 0, 18, 30, 73, 33, 90, 4, 76, 77, 12, 31, 55, 88,
26, 42, 69, 15, 40, 96, 9, 72, 11, 47, 85, 28, 93, 5, 66, 65, 35, 16, 49, 34, 7, 95,
27, 19, 81, 25, 62, 13, 24, 3, 17, 38, 8, 78, 6, 64, 36, 89, 56, 99, 54, 43, 50, 67,
46, 68, 61, 97, 79, 41, 58, 48, 98, 57, 75, 32, 94, 59, 63, 84, 37, 29, 1, 52, 21, 2,
23, 87, 91, 74, 86, 82, 20, 60, 71, 14, 92, 51, -1, 179, 137, 165, 154, 115, 120,
199, 125, 156, 117, 159, 193, 187, 166, 155, 173, 139, 130, 116, 149, 160, 153, 183,
123, 188, 109, 118, 182, 174, 189, 158, 198, 148, 176, 157, 190, 175, 186, 163, 124,
178, 110, 129, 119, 145, 181, 185, 152, 105, 167, 169, 101, 192, 121, 168, 191, 131,
112, 135, 128, 142, 170, 144, 138, 184, 103, 151, 162, 150, 141, 114, 108, 126, 113,
194, 100, 102, 177, 146, 164, 196, 143, 136, 161, 122, 147, 195, 133, 111, 171, 172,
106, 127, 140, 104, 132, 197, 134, 107, 180, -1, 248, 228, 285, 283, 214, 209, 242,
221, 275, 293, 244, 226, 260, 212, 220, 246, 245, 240, 213, 261, 253, 250, 271, 294,
257, 205, 203, 207, 284, 277, 262, 233, 265, 282, 288, 286, 247, 281, 287, 208, 292,
236, 204, 211, 259, 299, 225, 278, 206, 234, 241, 229, 230, 222, 249, 224, 256, 227,
279, 297, 264, 217, 238, 263, 239, 232, 251, 273, 270, 266, 298, 235, 276, 219, 202,
280, 258, 272, 215, 210, 290, 268, 223, 237, 216, 255, 274, 254, 267, 291, 269, 231,
289, 243, 252, 201, 218, 200, 296, 295, -1
};
List<Integer> numberList = Arrays.asList(shuffledNums);
List<Set<Integer>> sequence = sequenceMachine.generateFromNumbers(numberList);
feedTM(sequence, "", true, 3);
testTM(sequence, "");
assertAllActiveWerePredicted();
}
/**
* Like B1 but with cellsPerColumn = 4.
* First order sequences should still work just fine.
*/
@Test
public void testB5() {
Parameters p = Parameters.empty();
p.set(KEY.CELLS_PER_COLUMN, 4);
init(p, PATTERN_MACHINE);
assertTrue(tm.getConnections().getCellsPerColumn() == 4);
// Instead of implementing the Python "shuffle" method, just use the exact output
Integer[] shuffledNums = new Integer[] {
83, 53, 70, 45, 44, 39, 22, 80, 10, 0, 18, 30, 73, 33, 90, 4, 76, 77, 12, 31, 55, 88,
26, 42, 69, 15, 40, 96, 9, 72, 11, 47, 85, 28, 93, 5, 66, 65, 35, 16, 49, 34, 7, 95,
27, 19, 81, 25, 62, 13, 24, 3, 17, 38, 8, 78, 6, 64, 36, 89, 56, 99, 54, 43, 50, 67,
46, 68, 61, 97, 79, 41, 58, 48, 98, 57, 75, 32, 94, 59, 63, 84, 37, 29, 1, 52, 21, 2,
23, 87, 91, 74, 86, 82, 20, 60, 71, 14, 92, 51, -1
};
List<Integer> numberList = Arrays.asList(shuffledNums);
List<Set<Integer>> sequence = sequenceMachine.generateFromNumbers(numberList);
feedTM(sequence, "", true, 1);
testTM(sequence, "");
assertAllActiveWerePredicted();
assertAllInactiveWereUnpredicted();
}
/**
* Like B4 but with cellsPerColumn = 4.
* First order sequences should still work just fine.
*/
@Test
public void testB6() {
Parameters p = Parameters.empty();
p.set(KEY.CELLS_PER_COLUMN, 4);
init(p, PATTERN_MACHINE);
assertTrue(tm.getConnections().getCellsPerColumn() == 4);
// Instead of implementing the Python "shuffle" method, just use the exact output
Integer[] shuffledNums = new Integer[] {
83, 53, 70, 45, 44, 39, 22, 80, 10, 0, 18, 30, 73, 33, 90, 4, 76, 77, 12, 31, 55, 88,
26, 42, 69, 15, 40, 96, 9, 72, 11, 47, 85, 28, 93, 5, 66, 65, 35, 16, 49, 34, 7, 95,
27, 19, 81, 25, 62, 13, 24, 3, 17, 38, 8, 78, 6, 64, 36, 89, 56, 99, 54, 43, 50, 67,
46, 68, 61, 97, 79, 41, 58, 48, 98, 57, 75, 32, 94, 59, 63, 84, 37, 29, 1, 52, 21, 2,
23, 87, 91, 74, 86, 82, 20, 60, 71, 14, 92, 51, -1, 179, 137, 165, 154, 115, 120,
199, 125, 156, 117, 159, 193, 187, 166, 155, 173, 139, 130, 116, 149, 160, 153, 183,
123, 188, 109, 118, 182, 174, 189, 158, 198, 148, 176, 157, 190, 175, 186, 163, 124,
178, 110, 129, 119, 145, 181, 185, 152, 105, 167, 169, 101, 192, 121, 168, 191, 131,
112, 135, 128, 142, 170, 144, 138, 184, 103, 151, 162, 150, 141, 114, 108, 126, 113,
194, 100, 102, 177, 146, 164, 196, 143, 136, 161, 122, 147, 195, 133, 111, 171, 172,
106, 127, 140, 104, 132, 197, 134, 107, 180, -1, 248, 228, 285, 283, 214, 209, 242,
221, 275, 293, 244, 226, 260, 212, 220, 246, 245, 240, 213, 261, 253, 250, 271, 294,
257, 205, 203, 207, 284, 277, 262, 233, 265, 282, 288, 286, 247, 281, 287, 208, 292,
236, 204, 211, 259, 299, 225, 278, 206, 234, 241, 229, 230, 222, 249, 224, 256, 227,
279, 297, 264, 217, 238, 263, 239, 232, 251, 273, 270, 266, 298, 235, 276, 219, 202,
280, 258, 272, 215, 210, 290, 268, 223, 237, 216, 255, 274, 254, 267, 291, 269, 231,
289, 243, 252, 201, 218, 200, 296, 295, -1
};
List<Integer> numberList = Arrays.asList(shuffledNums);
List<Set<Integer>> sequence = sequenceMachine.generateFromNumbers(numberList);
feedTM(sequence, "", true, 3);
testTM(sequence, "");
assertAllActiveWerePredicted();
assertAllInactiveWereUnpredicted();
}
/**
* Like B1 but with slower learning.
*
* Set the following parameters differently:
*
* initialPermanence = 0.2
* connectedPermanence = 0.7
* permanenceIncrement = 0.2
*
* Now we train the TP with the B1 sequence 4 times (P=4). This will increment
* the permanences to be above 0.8 and at that point the inference will be correct.
* This test will ensure the basic match function and segment activation rules are
* working correctly.
*/
@Test
public void testB7() {
Parameters p = Parameters.empty();
p.set(KEY.INITIAL_PERMANENCE, 0.2);
p.set(KEY.CONNECTED_PERMANENCE, 0.7);
p.set(KEY.PERMANENCE_INCREMENT, 0.2);
init(p, PATTERN_MACHINE);
assertTrue(tm.getConnections().getInitialPermanence() == 0.2);
assertTrue(tm.getConnections().getConnectedPermanence() == 0.7);
assertTrue(tm.getConnections().getPermanenceIncrement() == 0.2);
// Instead of implementing the Python "shuffle" method, just use the exact output
Integer[] shuffledNums = new Integer[] {
83, 53, 70, 45, 44, 39, 22, 80, 10, 0, 18, 30, 73, 33, 90, 4, 76, 77, 12, 31, 55, 88,
26, 42, 69, 15, 40, 96, 9, 72, 11, 47, 85, 28, 93, 5, 66, 65, 35, 16, 49, 34, 7, 95,
27, 19, 81, 25, 62, 13, 24, 3, 17, 38, 8, 78, 6, 64, 36, 89, 56, 99, 54, 43, 50, 67,
46, 68, 61, 97, 79, 41, 58, 48, 98, 57, 75, 32, 94, 59, 63, 84, 37, 29, 1, 52, 21, 2,
23, 87, 91, 74, 86, 82, 20, 60, 71, 14, 92, 51, -1
};
List<Integer> numberList = Arrays.asList(shuffledNums);
List<Set<Integer>> sequence = sequenceMachine.generateFromNumbers(numberList);
for(int i = 0;i < 4;i++) {
feedTM(sequence, "", true, 1);
}
testTM(sequence, "");
assertAllActiveWerePredicted();
assertAllInactiveWereUnpredicted();
}
/**
* Like B7 but with 4 cells per column.
* Should still work.
*/
@Test
public void testB8() {
Parameters p = Parameters.empty();
p.set(KEY.INITIAL_PERMANENCE, 0.2);
p.set(KEY.CONNECTED_PERMANENCE, 0.7);
p.set(KEY.PERMANENCE_INCREMENT, 0.2);
p.set(KEY.CELLS_PER_COLUMN, 4);
init(p, PATTERN_MACHINE);
assertTrue(tm.getConnections().getInitialPermanence() == 0.2);
assertTrue(tm.getConnections().getConnectedPermanence() == 0.7);
assertTrue(tm.getConnections().getPermanenceIncrement() == 0.2);
assertTrue(tm.getConnections().getCellsPerColumn() == 4);
// Instead of implementing the Python "shuffle" method, just use the exact output
Integer[] shuffledNums = new Integer[] {
83, 53, 70, 45, 44, 39, 22, 80, 10, 0, 18, 30, 73, 33, 90, 4, 76, 77, 12, 31, 55, 88,
26, 42, 69, 15, 40, 96, 9, 72, 11, 47, 85, 28, 93, 5, 66, 65, 35, 16, 49, 34, 7, 95,
27, 19, 81, 25, 62, 13, 24, 3, 17, 38, 8, 78, 6, 64, 36, 89, 56, 99, 54, 43, 50, 67,
46, 68, 61, 97, 79, 41, 58, 48, 98, 57, 75, 32, 94, 59, 63, 84, 37, 29, 1, 52, 21, 2,
23, 87, 91, 74, 86, 82, 20, 60, 71, 14, 92, 51, -1
};
List<Integer> numberList = Arrays.asList(shuffledNums);
List<Set<Integer>> sequence = sequenceMachine.generateFromNumbers(numberList);
for(int i = 0;i < 4;i++) {
feedTM(sequence, "", true, 1);
}
testTM(sequence, "");
assertAllActiveWerePredicted();
assertAllInactiveWereUnpredicted();
}
/**
* Like B7 but present the sequence less than 4 times.
* The inference should be incorrect.
*/
@Test
public void testB9() {
Parameters p = Parameters.empty();
p.set(KEY.INITIAL_PERMANENCE, 0.2);
p.set(KEY.CONNECTED_PERMANENCE, 0.7);
p.set(KEY.PERMANENCE_INCREMENT, 0.2);
init(p, PATTERN_MACHINE);
assertTrue(tm.getConnections().getInitialPermanence() == 0.2);
assertTrue(tm.getConnections().getConnectedPermanence() == 0.7);
assertTrue(tm.getConnections().getPermanenceIncrement() == 0.2);
assertTrue(tm.getConnections().getCellsPerColumn() == 1);
// Instead of implementing the Python "shuffle" method, just use the exact output
Integer[] shuffledNums = new Integer[] {
83, 53, 70, 45, 44, 39, 22, 80, 10, 0, 18, 30, 73, 33, 90, 4, 76, 77, 12, 31, 55, 88,
26, 42, 69, 15, 40, 96, 9, 72, 11, 47, 85, 28, 93, 5, 66, 65, 35, 16, 49, 34, 7, 95,
27, 19, 81, 25, 62, 13, 24, 3, 17, 38, 8, 78, 6, 64, 36, 89, 56, 99, 54, 43, 50, 67,
46, 68, 61, 97, 79, 41, 58, 48, 98, 57, 75, 32, 94, 59, 63, 84, 37, 29, 1, 52, 21, 2,
23, 87, 91, 74, 86, 82, 20, 60, 71, 14, 92, 51, -1
};
List<Integer> numberList = Arrays.asList(shuffledNums);
List<Set<Integer>> sequence = sequenceMachine.generateFromNumbers(numberList);
for(int i = 0;i < 3;i++) {
feedTM(sequence, "", true, 1);
}
testTM(sequence, "");
assertAllActiveWereUnpredicted();
}
/**
* Like B5, but with activationThreshold = 8 and with each pattern
* corrupted by a small amount of spatial noise (X = 0.05).
* <pre>
* /////////////////////////////////////////////////////////////////
* // Warning: This test diverges from Python due to heavy random //
* // segment and synapse creation above the //
* // "activationThreshold" of 8 //
* /////////////////////////////////////////////////////////////////
* </pre>
* <b>Observations:</b>
* <ol>
* <li>The more activity beyond the point when Segments reach the "activationThreshold", the more the number of Synapses vary per Segment, and therefore make the statistics diverge.</li>
* <li>When setting the "activationThreshold" back from 8 to 11, the output was exactly the same.</li>
* <li>The total number of Synapses stay the same between the Python and Java versions regardless of other divergence! Which is a good thing.</li>
* <li>While the total number of Synapses stay the same, the predictedActive and predictedInactive Columns also stay the same.</li>
* </ol>
*/
@Test
public void testB11() {
Parameters p = Parameters.empty();
p.set(KEY.CELLS_PER_COLUMN, 4);
p.set(KEY.ACTIVATION_THRESHOLD, 8);
init(p, PATTERN_MACHINE);
assertTrue(tm.getConnections().getCellsPerColumn() == 4);
assertTrue(tm.getConnections().getActivationThreshold() == 8);
// Instead of implementing the Python "shuffle" method, just use the exact output
Integer[] shuffledNums = new Integer[] {
83, 53, 70, 45, 44, 39, 22, 80, 10, 0, 18, 30, 73, 33, 90, 4, 76, 77, 12, 31, 55, 88,
26, 42, 69, 15, 40, 96, 9, 72, 11, 47, 85, 28, 93, 5, 66, 65, 35, 16, 49, 34, 7, 95,
27, 19, 81, 25, 62, 13, 24, 3, 17, 38, 8, 78, 6, 64, 36, 89, 56, 99, 54, 43, 50, 67,
46, 68, 61, 97, 79, 41, 58, 48, 98, 57, 75, 32, 94, 59, 63, 84, 37, 29, 1, 52, 21, 2,
23, 87, 91, 74, 86, 82, 20, 60, 71, 14, 92, 51, -1
};
List<Integer> numberList = Arrays.asList(shuffledNums);
List<Set<Integer>> sequence = sequenceMachine.generateFromNumbers(numberList);
feedTM(sequence, "", true, 1);
sequence = translateSequence(
"set([96, 1, 66, 43, 48, 33, 72, 31, 10, 71, 44, 16, 49, 19, 20, 87, 89, 58, 63, 74, 52]), " +
"set([0, 1, 4, 10, 15, 16, 23, 28, 31, 40, 46, 48, 50, 58, 67, 71, 77, 79, 90, 92, 93, 97, 99]), " +
"set([5, 7, 10, 14, 17, 21, 29, 31, 39, 46, 53, 61, 67, 69, 71, 77, 80, 81, 85, 86, 87, 88, 93, 97]), " +
"set([5, 6, 10, 11, 14, 17, 26, 30, 33, 38, 39, 42, 45, 52, 53, 58, 59, 60, 64, 67, 74, 97, 98, 99]), " +
"set([5, 13, 19, 23, 25, 26, 29, 35, 36, 37, 42, 48, 57, 59, 60, 67, 73, 77, 92, 95, 96, 98]), " +
"set([3, 17, 18, 21, 24, 27, 34, 35, 37, 40, 44, 47, 50, 58, 59, 60, 62, 64, 66, 69, 77, 79, 82, 95, 98]), " +
"set([5, 15, 18, 24, 36, 37, 39, 40, 42, 45, 58, 59, 64, 68, 69, 77, 83, 87, 89, 90, 91, 98]), " +
"set([1, 12, 15, 22, 23, 24, 25, 36, 37, 38, 50, 55, 56, 63, 65, 66, 70, 72, 74, 81, 82, 87, 92, 95, 99]), " +
"set([4, 5, 7, 11, 14, 16, 40, 45, 46, 47, 53, 58, 60, 67, 69, 71, 73, 75, 77, 79, 85, 89, 95, 96]), " +
"set([0, 4, 10, 12, 18, 22, 26, 30, 31, 33, 39, 44, 45, 53, 70, 73, 76, 77, 80, 83, 88, 90]), " +
"set([4, 7, 11, 12, 14, 15, 20, 23, 26, 27, 31, 40, 50, 54, 56, 58, 61, 65, 75, 77, 81, 85, 86, 87, 97]), " +
"set([96, 65, 66, 68, 33, 12, 75, 76, 45, 78, 16, 56, 19, 1, 24, 89, 71, 63, 62, 53]), " +
"set([1, 3, 6, 7, 11, 12, 16, 19, 22, 29, 31, 33, 34, 37, 43, 50, 53, 61, 62, 72, 77, 80, 91]), " +
"set([1, 8, 10, 20, 22, 23, 27, 29, 30, 31, 41, 49, 51, 61, 71, 74, 76, 80, 81, 86, 93, 94]), " +
"set([1, 12, 19, 20, 21, 26, 31, 34, 35, 38, 46, 52, 57, 61, 64, 66, 75, 79, 80, 90, 92, 93, 94, 99]), " +
"set([0, 5, 7, 17, 19, 24, 25, 38, 40, 43, 44, 45, 46, 48, 54, 55, 66, 74, 77, 86, 90, 95]), " +
"set([3, 6, 19, 23, 31, 40, 44, 46, 47, 53, 55, 67, 68, 70, 73, 75, 76, 83, 88, 90, 97, 99]), " +
"set([4, 9, 16, 19, 21, 23, 37, 43, 45, 46, 47, 55, 62, 72, 74, 79, 81, 86, 89, 92, 93, 94]), " +
"set([1, 34, 59, 36, 38, 6, 71, 8, 2, 12, 66, 14, 45, 49, 82, 54, 56, 26, 47, 60, 61]), " +
"set([5, 6, 7, 11, 17, 23, 25, 27, 28, 30, 35, 38, 42, 52, 66, 67, 68, 75, 77, 78, 82, 87, 88, 93, 99]), " +
"set([64, 96, 35, 68, 70, 6, 71, 75, 76, 15, 80, 17, 51, 53, 23, 4, 58, 5, 93, 30, 19]), " +
"set([0, 11, 13, 14, 17, 18, 21, 23, 24, 26, 33, 37, 58, 62, 71, 77, 80, 81, 84, 89, 90, 93, 96]), " +
"set([0, 34, 99, 68, 69, 6, 39, 11, 44, 77, 81, 82, 83, 20, 22, 55, 23, 52, 91, 62, 31]), " +
"set([9, 19, 21, 24, 28, 29, 34, 39, 41, 42, 45, 54, 61, 62, 65, 66, 72, 74, 80, 91, 95, 97, 99]), " +
"set([4, 8, 9, 11, 13, 15, 16, 35, 36, 37, 39, 49, 50, 56, 66, 67, 68, 72, 78, 84, 91, 98, 99]), " +
"set([1, 10, 13, 17, 18, 19, 28, 29, 30, 31, 32, 35, 36, 37, 38, 44, 50, 66, 71, 75, 81, 90]), " +
"set([0, 64, 2, 99, 4, 38, 70, 39, 45, 78, 50, 18, 52, 85, 87, 57, 90, 95, 28, 93, 84]), " +
"set([6, 9, 11, 13, 15, 17, 26, 27, 29, 33, 35, 37, 39, 48, 54, 55, 57, 62, 63, 84, 89, 95, 98]), " +
"set([3, 4, 7, 8, 9, 10, 18, 21, 30, 36, 38, 47, 49, 51, 55, 56, 60, 67, 68, 71, 74, 80, 82, 91, 95]), " +
"set([2, 4, 9, 17, 19, 26, 29, 31, 34, 47, 49, 59, 61, 62, 63, 64, 65, 74, 80, 88, 91, 98]), " +
"set([4, 18, 20, 21, 25, 28, 35, 38, 44, 50, 51, 59, 60, 62, 63, 64, 70, 73, 75, 79, 85, 93, 97]), " +
"set([2, 3, 12, 17, 19, 22, 23, 26, 28, 35, 37, 41, 45, 47, 48, 50, 51, 64, 66, 82, 85, 86, 88, 94, 97]), " +
"set([5, 8, 12, 14, 18, 19, 22, 23, 27, 39, 40, 44, 48, 49, 50, 54, 60, 61, 68, 71, 79, 87, 91, 97]), " +
"set([10, 25, 29, 33, 34, 40, 43, 46, 57, 58, 65, 66, 75, 77, 78, 79, 84, 86, 91, 92, 93, 99]), " +
"set([4, 5, 6, 8, 12, 13, 18, 20, 25, 26, 29, 30, 35, 36, 47, 57, 60, 67, 76, 85, 91, 92, 94, 97]), " +
"set([0, 4, 5, 9, 12, 17, 19, 25, 34, 38, 41, 42, 44, 56, 65, 68, 74, 76, 77, 79, 83, 88, 93, 96, 99]), " +
"set([16, 67, 86, 35, 7, 41, 10, 11, 44, 77, 73, 48, 17, 75, 19, 56, 53, 76, 9, 24, 58]), " +
"set([0, 97, 66, 35, 33, 72, 73, 10, 11, 78, 60, 49, 50, 19, 54, 89, 83, 57, 28, 29, 63]), " +
"set([5, 6, 8, 12, 17, 25, 29, 31, 33, 34, 35, 46, 51, 56, 57, 62, 63, 67, 71, 78, 79, 87, 95]), " +
"set([3, 9, 17, 19, 23, 27, 29, 30, 32, 35, 36, 40, 52, 56, 58, 60, 63, 67, 83, 85, 88, 90, 95, 96]), " +
"set([32, 1, 66, 36, 6, 33, 64, 39, 44, 45, 14, 81, 82, 83, 52, 55, 18, 89, 59, 30, 31]), " +
"set([1, 17, 21, 24, 28, 31, 33, 34, 37, 39, 45, 48, 53, 54, 58, 59, 66, 74, 76, 80, 86, 88, 93, 97]), " +
"set([4, 15, 18, 20, 29, 32, 33, 35, 36, 39, 40, 42, 44, 53, 58, 69, 79, 80, 83, 86, 95, 97]), " +
"set([0, 65, 98, 67, 39, 40, 76, 75, 12, 66, 14, 45, 27, 81, 7, 25, 26, 79, 92, 44, 63]), " +
"set([0, 1, 2, 6, 7, 9, 20, 28, 29, 30, 38, 46, 47, 51, 55, 58, 60, 66, 69, 87, 94, 97]), " +
"set([5, 7, 12, 14, 16, 19, 22, 23, 27, 31, 32, 41, 43, 53, 57, 65, 68, 73, 75, 78, 82, 91, 92, 96, 99]), " +
"set([5, 9, 12, 15, 18, 19, 21, 26, 36, 37, 40, 49, 50, 51, 63, 66, 69, 76, 78, 81, 91, 93]), " +
"set([1, 2, 3, 4, 8, 9, 13, 22, 25, 32, 36, 37, 46, 49, 52, 53, 56, 61, 64, 84, 90, 91, 93, 96]), " +
"set([0, 1, 3, 5, 6, 7, 11, 12, 18, 21, 25, 30, 45, 50, 75, 76, 77, 78, 79, 84, 87, 91, 95]), " +
"set([1, 10, 12, 18, 26, 38, 40, 49, 50, 53, 55, 61, 65, 66, 68, 71, 77, 80, 89, 90, 92, 96]), " +
"set([3, 12, 13, 16, 18, 20, 24, 26, 30, 31, 35, 38, 42, 50, 52, 54, 55, 56, 59, 60, 61, 63, 77, 79, 98]), " +
"set([3, 4, 5, 9, 11, 13, 16, 19, 21, 22, 24, 48, 49, 60, 61, 65, 66, 69, 70, 78, 85, 87, 96]), " +
"set([19, 35, 4, 6, 41, 43, 76, 77, 78, 47, 48, 67, 21, 86, 55, 58, 27, 92, 60, 94, 95]), " +
"set([1, 3, 5, 10, 16, 23, 28, 30, 35, 39, 46, 47, 50, 52, 54, 60, 68, 72, 83, 84, 85, 88, 92, 99]), " +
"set([2, 6, 8, 13, 15, 16, 27, 33, 37, 41, 48, 52, 61, 62, 64, 68, 73, 74, 75, 79, 89, 91, 96, 98]), " +
"set([35, 2, 3, 4, 69, 71, 80, 73, 45, 14, 15, 48, 98, 19, 78, 56, 58, 31, 28, 90, 63]), " +
"set([1, 3, 14, 17, 19, 22, 25, 26, 28, 35, 36, 48, 49, 55, 58, 67, 69, 72, 80, 85, 91, 93, 98]), " +
"set([96, 97, 2, 99, 68, 7, 73, 74, 75, 12, 66, 81, 51, 84, 23, 89, 90, 60, 29, 63]), " +
"set([10, 14, 25, 30, 37, 40, 44, 52, 56, 58, 60, 63, 70, 72, 74, 80, 82, 84, 86, 93, 97, 99]), " +
"set([97, 67, 4, 71, 72, 80, 7, 12, 13, 46, 16, 49, 35, 84, 53, 89, 23, 88, 20, 31]), " +
"set([0, 97, 36, 6, 7, 96, 10, 43, 76, 12, 16, 18, 83, 84, 78, 54, 73, 23, 31, 29, 19]), " +
"set([97, 67, 68, 69, 7, 95, 11, 12, 77, 79, 33, 75, 84, 86, 23, 52, 25, 29, 61, 62, 5]), " +
"set([34, 3, 37, 63, 73, 42, 21, 13, 14, 47, 50, 49, 82, 19, 52, 46, 41, 25, 27, 92, 53]), " +
"set([0, 1, 7, 10, 11, 22, 30, 36, 44, 47, 53, 56, 57, 58, 59, 65, 66, 75, 76, 84, 85, 95, 98]), " +
"set([2, 10, 13, 15, 17, 19, 20, 24, 26, 38, 39, 40, 43, 46, 51, 58, 59, 61, 74, 78, 81, 82]), " +
"set([3, 4, 15, 21, 26, 45, 47, 48, 50, 52, 60, 62, 64, 72, 73, 77, 86, 90, 91, 94, 95, 98]), " +
"set([0, 2, 3, 5, 6, 8, 11, 23, 25, 30, 34, 37, 43, 49, 52, 57, 61, 63, 68, 79, 90, 98]), " +
"set([7, 10, 11, 12, 21, 22, 24, 35, 36, 41, 45, 46, 47, 49, 52, 55, 58, 60, 64, 74, 81, 85, 90, 94]), " +
"set([2, 3, 6, 8, 13, 24, 25, 29, 33, 44, 45, 53, 55, 60, 61, 64, 65, 76, 79, 80, 84, 90, 91, 94, 97]), " +
"set([0, 5, 10, 15, 22, 30, 31, 32, 33, 37, 43, 47, 48, 49, 58, 59, 60, 66, 67, 79, 93, 97]), " +
"set([8, 11, 15, 20, 23, 31, 37, 46, 47, 55, 56, 57, 58, 60, 68, 73, 74, 80, 82, 83, 94, 99]), " +
"set([0, 4, 6, 8, 10, 11, 18, 22, 30, 35, 37, 43, 44, 47, 53, 58, 60, 61, 66, 69, 74, 75, 80, 90, 94]), " +
"set([0, 2, 7, 11, 14, 19, 20, 21, 28, 33, 35, 37, 43, 53, 64, 68, 69, 72, 73, 79, 80, 83, 86, 98]), " +
"set([96, 11, 36, 69, 72, 37, 42, 75, 45, 78, 50, 55, 24, 57, 90, 59, 5, 74, 23, 95]), " +
"set([5, 19, 22, 26, 37, 38, 40, 41, 44, 45, 48, 49, 51, 55, 60, 64, 70, 74, 75, 78, 89, 92, 96]), " +
"set([7, 16, 18, 26, 30, 35, 41, 42, 43, 45, 48, 52, 53, 54, 74, 78, 84, 85, 86, 90, 94, 99]), " +
"set([2, 4, 11, 19, 30, 39, 44, 45, 46, 48, 51, 52, 56, 59, 60, 61, 70, 74, 80, 89, 94, 96, 98, 99]), " +
"set([96, 59, 5, 7, 40, 47, 42, 77, 15, 49, 82, 67, 22, 23, 56, 25, 58, 27, 61, 62]), " +
"set([4, 5, 12, 15, 17, 20, 21, 23, 29, 32, 37, 43, 54, 60, 70, 71, 74, 77, 83, 88, 93, 96]), " +
"set([0, 11, 13, 18, 19, 22, 29, 30, 31, 32, 36, 42, 44, 50, 52, 63, 72, 79, 91, 92, 96, 99]), " +
"set([7, 10, 18, 19, 22, 23, 28, 29, 34, 35, 37, 39, 50, 51, 55, 56, 67, 68, 70, 71, 72, 80, 89, 91, 95]), " +
"set([0, 1, 3, 9, 10, 12, 21, 32, 42, 48, 52, 53, 54, 62, 64, 67, 69, 73, 75, 78, 80, 81, 90, 98, 99]), " +
"set([1, 4, 8, 9, 12, 13, 15, 23, 24, 29, 35, 45, 49, 51, 55, 59, 64, 65, 67, 72, 78, 82, 86, 87, 91]), " +
"set([1, 7, 12, 21, 23, 25, 27, 28, 36, 38, 43, 50, 51, 61, 66, 70, 75, 83, 84, 94, 97, 99]), " +
"set([2, 4, 6, 8, 11, 13, 15, 16, 23, 27, 38, 42, 46, 48, 51, 55, 56, 59, 62, 65, 76, 77, 78, 82, 89]), " +
"set([0, 20, 22, 23, 24, 25, 26, 27, 28, 31, 32, 38, 41, 46, 47, 52, 56, 61, 73, 81, 82, 83, 94, 97, 98]), " +
"set([6, 10, 21, 24, 26, 30, 36, 45, 47, 59, 62, 67, 68, 70, 71, 72, 75, 80, 81, 83, 91, 92, 95]), " +
"set([2, 7, 10, 12, 19, 25, 38, 39, 44, 46, 49, 52, 56, 72, 77, 78, 79, 83, 84, 88, 94, 97, 98, 99]), " +
"set([1, 7, 12, 13, 18, 22, 25, 26, 31, 38, 40, 58, 61, 62, 67, 70, 71, 73, 84, 85, 92, 97, 98]), " +
"set([96, 1, 48, 5, 7, 9, 10, 39, 77, 14, 16, 49, 18, 22, 23, 56, 68, 87, 42, 30, 95]), " +
"set([0, 2, 3, 7, 10, 14, 16, 19, 21, 27, 28, 29, 31, 35, 38, 40, 43, 49, 52, 55, 62, 82, 93, 96]), " +
"set([4, 15, 19, 23, 33, 34, 35, 37, 44, 46, 57, 59, 62, 69, 70, 71, 73, 77, 81, 82, 86, 91]), " +
"set([0, 2, 67, 69, 71, 73, 23, 12, 45, 16, 76, 18, 83, 84, 24, 41, 56, 31, 28, 62, 5]), " +
"set([0, 11, 13, 17, 22, 26, 36, 38, 41, 44, 61, 62, 63, 74, 77, 80, 81, 85, 86, 88, 90, 94]), " +
"set([4, 10, 29, 36, 43, 44, 50, 53, 57, 58, 63, 64, 66, 72, 75, 78, 79, 80, 86, 91, 97, 99]), " +
"set([0, 1, 10, 12, 15, 17, 20, 24, 26, 28, 36, 39, 51, 52, 54, 55, 58, 63, 73, 79, 83, 85, 87, 89, 94]), " +
"set([15, 17, 19, 23, 26, 27, 33, 34, 45, 46, 47, 49, 51, 53, 62, 63, 68, 69, 72, 74, 80, 81, 84, 94]), " +
"set([96, 3, 4, 40, 12, 44, 78, 47, 16, 50, 51, 90, 46, 86, 87, 26, 28, 61, 30, 95]), " +
"set([1, 3, 5, 8, 14, 15, 22, 23, 26, 33, 35, 43, 48, 59, 61, 68, 74, 77, 79, 80, 90, 98]), " +
"set([1, 3, 13, 24, 25, 26, 30, 33, 37, 43, 49, 58, 63, 66, 67, 71, 76, 77, 78, 79, 90, 91, 97])");
testTM(sequence, "");
Metric unpredictedActiveColumnsMetric = tm.mmGetMetricFromTrace(
tm.mmGetTraceUnpredictedActiveColumns());
assertTrue(unpredictedActiveColumnsMetric.mean < 1);
}
/**
* Learn two sequences with a short shared pattern.
* Parameters should be the same as B1.
* Since cellsPerColumn == 1, it should make more predictions than necessary.
*/
@Test
public void testH1() {
init(null, PATTERN_MACHINE);
// Instead of implementing the Python "shuffle" method, just use the exact output
Integer[] shuffledNums = new Integer[] {
0, 17, 15, 1, 8, 5, 11, 3, 18, 16, 40, 41, 42, 43, 44, 12, 7, 10, 14, 6, -1,
39, 36, 35, 25, 24, 32, 34, 27, 23, 26, 40, 41, 42, 43, 44, 28, 37, 31, 20, 21, -1
};
List<Integer> numberList = Arrays.asList(shuffledNums);
List<Set<Integer>> sequence = sequenceMachine.generateFromNumbers(numberList);
feedTM(sequence, "", true, 1);
testTM(sequence, "");
assertAllActiveWerePredicted();
IndicesTrace predictedInactiveTrace = tm.mmGetTracePredictedInactiveColumns();
Metric predictedInactiveColumnsMetric = tm.mmGetMetricFromTrace(predictedInactiveTrace);
assertTrue(predictedInactiveColumnsMetric.mean > 0);
// At the end of both shared sequences, there should be
// predicted but inactive columns
assertTrue(predictedInactiveTrace.items.get(14).size() > 0);
assertTrue(predictedInactiveTrace.items.get(33).size() > 0);
}
/**
* Same as H1, but with cellsPerColumn == 4, and train multiple times.
* It should make just the right number of predictions.
*/
@Test
public void testH2() {
Parameters p = Parameters.empty();
p.set(KEY.CELLS_PER_COLUMN, 4);
init(p, PATTERN_MACHINE);
// Instead of implementing the Python "shuffle" method, just use the exact output
Integer[] shuffledNums = new Integer[] {
0, 17, 15, 1, 8, 5, 11, 3, 18, 16, 40, 41, 42, 43, 44, 12, 7, 10, 14, 6, -1,
39, 36, 35, 25, 24, 32, 34, 27, 23, 26, 40, 41, 42, 43, 44, 28, 37, 31, 20, 21, -1
};
List<Integer> numberList = Arrays.asList(shuffledNums);
List<Set<Integer>> sequence = sequenceMachine.generateFromNumbers(numberList);
for(int i = 0;i < 10;i++) {
feedTM(sequence, "", true, 1);
}
testTM(sequence, "");
assertAllActiveWerePredicted();
// Without some kind of decay, expect predicted inactive columns at the
// end of the first shared sequence
IndicesTrace predictedInactiveTrace = tm.mmGetTracePredictedInactiveColumns();
Metric predictedInactiveColumnsMetric = tm.mmGetMetricFromTrace(predictedInactiveTrace);
assertTrue(predictedInactiveColumnsMetric.mean > 0);
assertTrue(predictedInactiveColumnsMetric.sum < 26);
// At the end of the second shared sequence, there should be no
// predicted but inactive columns
assertTrue(tm.mmGetTracePredictedInactiveColumns().items.get(36).size() == 0);
}
/**
* Like H2, except the shared subsequence is in the beginning.
* (e.g. "ABCDEF" and "ABCGHIJ") At the point where the shared subsequence
* ends, all possible next patterns should be predicted. As soon as you see
* the first unique pattern, the predictions should collapse to be a perfect
* prediction.
*/
@Test
public void testH3() {
Parameters p = Parameters.empty();
p.set(KEY.CELLS_PER_COLUMN, 4);
init(p, PATTERN_MACHINE);
// Instead of implementing the Python "shuffle" method, just use the exact output
Integer[] shuffledNums = new Integer[] {
40, 41, 42, 43, 44, 5, 11, 3, 18, 16, 13, 2, 9, 19, 4, 12, 7, 10, 14, 6, -1,
40, 41, 42, 43, 44, 32, 34, 27, 23, 26, 22, 29, 33, 30, 38, 28, 37, 31, 20, 21, -1
};
List<Integer> numberList = Arrays.asList(shuffledNums);
List<Set<Integer>> sequence = sequenceMachine.generateFromNumbers(numberList);
feedTM(sequence, "", true, 1);
testTM(sequence, "");
assertAllActiveWerePredicted();
// Without some kind of decay, expect predicted inactive columns at the
// end of the first shared sequence
IndicesTrace predictedInactiveTrace = tm.mmGetTracePredictedInactiveColumns();
Metric predictedInactiveColumnsMetric = tm.mmGetMetricFromTrace(predictedInactiveTrace);
assertTrue(predictedInactiveColumnsMetric.mean > 0);
assertTrue(predictedInactiveColumnsMetric.sum < 26 * 2);
// At the end of the second shared sequence, there should be
// predicted but inactive columns
int i = 0;
for(Set<Integer> s : tm.mmGetTracePredictedInactiveColumns().items) {
System.out.println("" + (i++) + " " + s);
}
assertTrue(tm.mmGetTracePredictedInactiveColumns().items.get(4).size() > 0);
assertTrue(tm.mmGetTracePredictedInactiveColumns().items.get(23).size() > 0);
}
/**
* Shared patterns. Similar to H2 except that patterns are shared between
* sequences. All sequences are different shufflings of the same set of N
* patterns (there is no shared subsequence).
*/
@Test
public void testH4() {
Parameters p = Parameters.empty();
p.set(KEY.CELLS_PER_COLUMN, 4);
init(p, PATTERN_MACHINE);
// Instead of implementing the Python "shuffle" method, just use the exact output
Integer[] shuffledNums = new Integer[] {
0, 17, 15, 1, 8, 5, 11, 3, 18, 16, 13, 2, 9, 19, 4, 12, 7, 10, 14, 6, -1,
19, 16, 15, 5, 4, 12, 14, 7, 3, 6, 2, 9, 13, 10, 18, 8, 17, 11, 0, 1, -1
};
List<Integer> numberList = Arrays.asList(shuffledNums);
List<Set<Integer>> sequence = sequenceMachine.generateFromNumbers(numberList);
for(int i = 0;i < 20;i++) {
feedTM(sequence, "", true, 1);
}
testTM(sequence, "");
assertAllActiveWerePredicted();
IndicesTrace predictedInactiveTrace = tm.mmGetTracePredictedInactiveColumns();
Metric predictedInactiveColumnsMetric = tm.mmGetMetricFromTrace(predictedInactiveTrace);
assertTrue(predictedInactiveColumnsMetric.mean < 3);
}
/**
* Combination of H4) and H2).
* Shared patterns in different sequences, with a shared subsequence.
*/
@Test
public void testH5() {
Parameters p = Parameters.empty();
p.set(KEY.CELLS_PER_COLUMN, 4);
init(p, PATTERN_MACHINE);
// Instead of implementing the Python "shuffle" method, just use the exact output
Integer[] shuffledNums = new Integer[] {
8, 16, 18, 11, 9, 13, 14, 5, 12, 15, 1, 4, 2, 0, 3, 19, 17, 10, 6, 7, -1,
17, 5, 10, 7, 12, 8, 6, 13, 14, 18, 1, 4, 2, 0, 3, 15, 9, 16, 19, 11, -1
};
List<Integer> numberList = Arrays.asList(shuffledNums);
List<Set<Integer>> sequence = sequenceMachine.generateFromNumbers(numberList);
for(int i = 0;i < 20;i++) {
feedTM(sequence, "", true, 1);
}
testTM(sequence, "");
assertAllActiveWerePredicted();
IndicesTrace predictedInactiveTrace = tm.mmGetTracePredictedInactiveColumns();
Metric predictedInactiveColumnsMetric = tm.mmGetMetricFromTrace(predictedInactiveTrace);
assertTrue(predictedInactiveColumnsMetric.mean < 3);
}
/**
* Sensitivity to small amounts of spatial noise during inference
* (X = 0.05). Parameters the same as B11, and sequences like H2.
*/
@Test
public void testH9() {
Parameters p = Parameters.empty();
p.set(KEY.CELLS_PER_COLUMN, 4);
p.set(KEY.ACTIVATION_THRESHOLD, 8);
init(p, PATTERN_MACHINE);
// Instead of implementing the Python "shuffle" method, just use the exact output
Integer[] shuffledNums = new Integer[] {
0, 17, 15, 1, 8, 5, 11, 3, 18, 16, 40, 41, 42, 43, 44, 12, 7, 10, 14, 6, -1,
39, 36, 35, 25, 24, 32, 34, 27, 23, 26, 40, 41, 42, 43, 44, 28, 37, 31, 20, 21, -1
};
List<Integer> numberList = Arrays.asList(shuffledNums);
List<Set<Integer>> sequence = sequenceMachine.generateFromNumbers(numberList);
for(int i = 0;i < 10;i++) {
feedTM(sequence, "", true, 1);
}
sequence = translateSequence(
"set([0, 4, 10, 18, 22, 26, 30, 31, 33, 44, 45, 53, 55, 70, 72, 73, 76, 77, 80, 83, 87, 88, 90]), " +
"set([35, 4, 6, 41, 43, 76, 77, 78, 47, 48, 67, 21, 86, 23, 55, 58, 27, 92, 60, 94, 95]), " +
"set([1, 10, 13, 17, 18, 19, 28, 30, 31, 35, 36, 37, 44, 53, 66, 71, 75, 80, 81, 84, 86, 90]), " +
"set([2, 4, 6, 8, 11, 13, 15, 16, 23, 29, 38, 42, 46, 48, 51, 55, 56, 59, 65, 67, 76, 77, 78, 82, 89]), " +
"set([2, 6, 8, 13, 15, 16, 27, 33, 37, 41, 48, 52, 61, 62, 64, 68, 73, 74, 75, 79, 89, 91, 96, 98]), " +
"set([0, 4, 5, 9, 12, 17, 25, 34, 38, 39, 41, 42, 44, 56, 65, 68, 69, 74, 76, 77, 83, 88, 93, 96, 99]), " +
"set([4, 18, 20, 21, 25, 35, 36, 38, 44, 50, 51, 59, 60, 62, 63, 73, 75, 79, 85, 89, 93, 97]), " +
"set([3, 4, 5, 9, 11, 13, 16, 19, 21, 22, 24, 48, 49, 60, 61, 65, 66, 69, 70, 78, 85, 87, 96]), " +
"set([4, 7, 11, 12, 14, 19, 20, 23, 26, 27, 31, 35, 40, 50, 54, 56, 58, 65, 75, 81, 84, 85, 87, 97, 98]), " +
"set([3, 9, 17, 19, 23, 27, 29, 30, 32, 35, 36, 40, 52, 53, 56, 58, 63, 67, 79, 83, 85, 88, 90, 95]), " +
"set([0, 64, 2, 99, 4, 38, 39, 77, 45, 78, 93, 18, 52, 85, 86, 87, 57, 90, 84, 61, 63]), " +
"set([0, 1, 4, 6, 10, 11, 15, 18, 22, 30, 37, 43, 44, 47, 53, 58, 60, 61, 69, 74, 75, 78, 80, 90, 94]), " +
"set([3, 6, 9, 12, 18, 19, 21, 22, 29, 34, 39, 42, 45, 54, 61, 62, 65, 66, 74, 80, 91, 97, 99]), " +
"set([0, 1, 10, 11, 22, 30, 36, 44, 47, 49, 53, 56, 57, 58, 65, 66, 75, 76, 84, 85, 90, 95, 98]), " +
"set([96, 1, 67, 35, 36, 37, 73, 42, 77, 61, 80, 92, 19, 98, 25, 26, 59, 60, 29, 95]), " +
"set([1, 34, 59, 36, 38, 6, 71, 8, 2, 12, 66, 14, 45, 49, 82, 54, 56, 26, 47, 60, 61]), " +
"set([15, 18, 22, 23, 27, 29, 32, 33, 35, 39, 40, 42, 44, 53, 58, 69, 79, 80, 83, 86, 91, 95, 97]), " +
"set([4, 5, 7, 11, 14, 16, 40, 45, 46, 47, 53, 58, 60, 67, 69, 71, 73, 75, 77, 79, 85, 86, 89, 92]), " +
"set([96, 2, 3, 4, 40, 73, 12, 78, 47, 16, 50, 51, 90, 46, 86, 26, 44, 28, 61, 30, 95]), " +
"set([1, 3, 14, 17, 19, 25, 26, 27, 28, 35, 36, 48, 49, 55, 58, 67, 69, 72, 77, 80, 85, 93, 98]), None, " +
"set([3, 17, 18, 21, 24, 27, 34, 35, 37, 40, 44, 47, 50, 58, 59, 60, 62, 64, 66, 69, 77, 79, 82, 95, 98]), " +
"set([97, 99, 37, 70, 40, 74, 44, 14, 80, 56, 82, 84, 86, 72, 24, 25, 58, 60, 93, 30, 63]), " +
"set([5, 6, 8, 12, 17, 24, 25, 29, 31, 33, 35, 41, 46, 53, 56, 57, 63, 67, 68, 71, 78, 83, 88]), " +
"set([1, 2, 3, 4, 8, 9, 13, 22, 25, 32, 36, 37, 46, 49, 52, 53, 56, 61, 64, 84, 90, 91, 93, 96]), " +
"set([3, 12, 13, 16, 18, 20, 24, 26, 30, 31, 35, 38, 42, 50, 52, 54, 55, 56, 59, 60, 61, 67, 77, 79, 98]), " +
"set([5, 7, 15, 22, 23, 25, 27, 40, 42, 47, 49, 50, 56, 58, 59, 62, 69, 77, 82, 89, 92, 96]), " +
"set([1, 17, 21, 24, 28, 31, 33, 34, 37, 39, 45, 53, 54, 58, 59, 66, 74, 76, 80, 86, 88, 93, 97]), " +
"set([0, 1, 2, 6, 7, 9, 13, 20, 28, 29, 30, 46, 47, 51, 55, 58, 60, 66, 69, 87, 94, 97]), " +
"set([1, 7, 12, 13, 22, 25, 26, 31, 38, 40, 44, 58, 61, 62, 67, 70, 71, 73, 84, 85, 92, 97, 98]), " +
"set([0, 64, 34, 99, 69, 6, 39, 11, 44, 77, 78, 81, 82, 20, 22, 55, 23, 52, 95, 62, 31]), " +
"set([0, 64, 2, 99, 4, 38, 70, 39, 45, 78, 50, 18, 52, 85, 87, 57, 90, 95, 84, 93, 63]), " +
"set([4, 6, 8, 10, 11, 18, 22, 23, 30, 35, 37, 43, 44, 47, 53, 58, 60, 61, 66, 69, 74, 75, 80, 90, 94]), " +
"set([3, 9, 18, 19, 21, 28, 29, 34, 39, 42, 45, 54, 61, 62, 65, 66, 72, 74, 80, 91, 95, 97]), " +
"set([0, 1, 10, 11, 22, 25, 30, 44, 47, 53, 56, 57, 59, 65, 66, 75, 76, 84, 85, 86, 90, 95, 98]), " +
"set([5, 13, 18, 19, 23, 25, 26, 29, 35, 36, 37, 42, 48, 57, 59, 60, 67, 77, 92, 95, 96, 98]), " +
"set([6, 10, 19, 25, 29, 33, 34, 40, 41, 43, 46, 58, 65, 71, 75, 77, 78, 79, 84, 91, 92, 93, 99]), " +
"set([1, 4, 8, 9, 12, 13, 15, 24, 29, 35, 45, 49, 51, 55, 56, 58, 59, 64, 65, 67, 72, 78, 82, 86, 91]), " +
"set([5, 6, 7, 11, 17, 22, 23, 25, 27, 28, 30, 35, 38, 42, 52, 66, 67, 68, 75, 78, 82, 87, 88, 93, 99]), " +
"set([4, 10, 29, 36, 43, 44, 50, 51, 57, 58, 62, 63, 64, 66, 72, 75, 78, 80, 86, 91, 97, 99]), " +
"set([6, 10, 21, 24, 26, 30, 36, 45, 47, 59, 62, 67, 68, 70, 72, 75, 80, 81, 83, 91, 92, 95, 96])");
testTM(sequence, "");
IndicesTrace unpredictedActiveTrace = tm.mmGetTraceUnpredictedActiveColumns();
Metric predictedInactiveColumnsMetric = tm.mmGetMetricFromTrace(unpredictedActiveTrace);
assertTrue(predictedInactiveColumnsMetric.mean < 3);
}
/**
* Orphan Decay mechanism reduce predicted inactive cells (extra predictions).
* Test feeds in noisy sequences (X = 0.05) to TM with and without orphan decay.
* TM with orphan decay should has many fewer predicted inactive columns.
* Parameters the same as B11, and sequences like H9.
*/
@Test
public void testH10() {
// train TM on noisy sequences with orphan decay turned off
Parameters p = Parameters.empty();
p.set(KEY.CELLS_PER_COLUMN, 4);
p.set(KEY.ACTIVATION_THRESHOLD, 8);
init(p, PATTERN_MACHINE);
assertTrue(tm.getConnections().getPredictedSegmentDecrement() == 0);
// Instead of implementing the Python "shuffle" method, just use the exact output
Integer[] shuffledNums = new Integer[] {
0, 17, 15, 1, 8, 5, 11, 3, 18, 16, 40, 41, 42, 43, 44, 12, 7, 10, 14, 6, -1,
39, 36, 35, 25, 24, 32, 34, 27, 23, 26, 40, 41, 42, 43, 44, 28, 37, 31, 20, 21, -1
};
List<Integer> numberList = Arrays.asList(shuffledNums);
List<Set<Integer>> sequence = sequenceMachine.generateFromNumbers(numberList);
List<List<Set<Integer>>> sequenceNoisy = new ArrayList<>();
for(int i = 0;i < 10;i++) {
sequenceNoisy.add(sequenceMachine.addSpatialNoise(sequence, 0.05));
feedTM(sequenceNoisy.get(i), "", true, 1);
}
testTM(sequence, "");
IndicesTrace predictedInactiveTrace = tm.mmGetTracePredictedInactiveColumns();
Metric predictedInactiveColumnsMetric = tm.mmGetMetricFromTrace(predictedInactiveTrace);
double predictedInactiveColumnsMean1 = predictedInactiveColumnsMetric.mean;
p = Parameters.empty();
p.set(KEY.CELLS_PER_COLUMN, 4);
p.set(KEY.ACTIVATION_THRESHOLD, 8);
p.set(KEY.PREDICTED_SEGMENT_DECREMENT, 0.04);
init(p, PATTERN_MACHINE);
assertTrue(tm.getConnections().getPredictedSegmentDecrement() == 0.04);
for(int i = 0;i < 10;i++) {
feedTM(sequenceNoisy.get(0), "", true, 1);
}
testTM(sequence, "");
predictedInactiveTrace = tm.mmGetTracePredictedInactiveColumns();
predictedInactiveColumnsMetric = tm.mmGetMetricFromTrace(predictedInactiveTrace);
double predictedInactiveColumnsMean2 = predictedInactiveColumnsMetric.mean;
assertTrue(predictedInactiveColumnsMean1 > 0);
assertTrue(predictedInactiveColumnsMean1 > predictedInactiveColumnsMean2);
}
private List<Set<Integer>> translateSequence(String s) {
List<Set<Integer>> retVal = new ArrayList<>();
s = s.replaceAll("set\\(\\[", "").replaceAll("]", "").replaceAll("\\)$", "").trim();
String[] sa = s.split("[\\s]*\\)\\,[\\s]*");
for(String numList : sa) {
String[] nums = numList.split("[\\s]*\\,[\\s]*");
Set<Integer> set = Arrays.stream(nums).map(
n -> n.equals("None") ? -1 : Integer.parseInt(n)).collect(Collectors.toCollection(LinkedHashSet::new));
retVal.add(set);
}
List<Set<Integer>> copy = new ArrayList<>(retVal);
for(int i = 0;i < copy.size();i++) {
Set<Integer> set = copy.get(i);
if(set.contains(-1)) {
retVal.get(i).remove(-1);
retVal.add(i, SequenceMachine.NONE);
}
}
retVal.add(SequenceMachine.NONE);
return retVal;
}
@SuppressWarnings("unused")
public void feedTM(List<Set<Integer>> sequence, String label, boolean learn, int num) {
super.feedTM(sequence, label, learn, num);
if(VERBOSITY >= 2) {
System.out.println(tm.mmPrettyPrintTraces(
tm.mmGetDefaultTraces(VERBOSITY), null));
System.out.println("");
}
if(learn && VERBOSITY >= 3) {
System.out.println(tm.mmPrettyPrintConnections());
}
}
public void testTM(List<Set<Integer>> sequence, String label) {
feedTM(sequence, label, false, 1);
System.out.println(tm.mmPrettyPrintMetrics(tm.mmGetDefaultMetrics(1), 7));
}
public void assertAllActiveWerePredicted() {
Metric unpredictedActiveColumnsMetric = tm.mmGetMetricFromTrace(
tm.mmGetTraceUnpredictedActiveColumns());
Metric predictedActiveColumnsMetric = tm.mmGetMetricFromTrace(
tm.mmGetTracePredictedActiveColumns());
assertEquals(unpredictedActiveColumnsMetric.sum, 0, 0);
assertEquals(predictedActiveColumnsMetric.min, 21, 0);
assertEquals(predictedActiveColumnsMetric.max, 25, 0);
}
public void assertAllInactiveWereUnpredicted() {
Metric predictedInactiveColumnsMetric = tm.mmGetMetricFromTrace(
tm.mmGetTracePredictedInactiveColumns());
assertEquals(predictedInactiveColumnsMetric.sum, 0, 0);
}
public void assertAllActiveWereUnpredicted() {
Metric unpredictedActiveColumnsMetric = tm.mmGetMetricFromTrace(
tm.mmGetTraceUnpredictedActiveColumns());
Metric predictedActiveColumnsMetric = tm.mmGetMetricFromTrace(
tm.mmGetTracePredictedActiveColumns());
assertEquals(predictedActiveColumnsMetric.sum, 0, 0);
assertEquals(unpredictedActiveColumnsMetric.min, 21, 0);
assertEquals(unpredictedActiveColumnsMetric.max, 25, 0);
}
}