/* --------------------------------------------------------------------- * 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.util; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.fail; import static org.junit.Assert.assertTrue; import java.lang.reflect.Array; import java.util.Arrays; import java.util.Random; import org.junit.Test; import org.numenta.nupic.Parameters; import org.numenta.nupic.Parameters.KEY; import org.numenta.nupic.algorithms.SpatialPooler; import org.numenta.nupic.model.Connections; public class SparseBinaryMatrixTest { private int[] dimensions = new int[]{5, 10}; @Test public void testBackingStoreAndSliceAccess() { doTestBackingStoreAndSliceAccess(new SparseBinaryMatrix(this.dimensions)); doTestBackingStoreAndSliceAccess(new LowMemorySparseBinaryMatrix(this.dimensions)); doTestBackingStoreAndSliceAccess(new FastConnectionsMatrix(this.dimensions)); } private void doTestBackingStoreAndSliceAccess(AbstractSparseBinaryMatrix sm) { int[][] connectedSynapses = new int[][]{ {1, 0, 0, 0, 0, 1, 0, 0, 0, 0}, {0, 1, 0, 0, 0, 0, 1, 0, 0, 0}, {0, 0, 1, 0, 0, 0, 0, 1, 0, 0}, {0, 0, 0, 1, 0, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 1, 0, 0, 0, 0, 1}}; for (int i = 0; i < sm.getDimensions()[0]; i++) { for (int j = 0; j < sm.getDimensions()[1]; j++) { sm.set(connectedSynapses[i][j], i, j); } } for (int i = 0; i < 5; i++) { for (int j = 0; j < 10; j++) { assertEquals(connectedSynapses[i][j], sm.getIntValue(i, j)); } } for (int i = 0; i < connectedSynapses.length; i++) { for (int j = 0; j < connectedSynapses[i].length; j++) { assertEquals(connectedSynapses[i][j], ((int[])sm.getSlice(i))[j], 0); } } //Make sure warning is proper for exact access try { sm.getSlice(0, 4); fail(); } catch (Exception e) { assertEquals("This method only returns the array holding the specified maximum index: " + Arrays.toString(sm.getDimensions()), e.getMessage()); } } @Test public void testRightVecSumAtNZFast() { doTestRightVecSumAtNZFast(new SparseBinaryMatrix(this.dimensions)); doTestRightVecSumAtNZFast(new LowMemorySparseBinaryMatrix(this.dimensions)); doTestRightVecSumAtNZFast(new FastConnectionsMatrix(this.dimensions)); } private void doTestRightVecSumAtNZFast(AbstractSparseBinaryMatrix sm) { int[] dimensions = new int[]{5, 10}; int[][] connectedSynapses = new int[][]{ {1, 0, 0, 0, 0, 1, 0, 0, 0, 0}, {0, 1, 0, 0, 0, 0, 1, 0, 0, 0}, {0, 0, 1, 0, 0, 0, 0, 1, 0, 0}, {0, 0, 0, 1, 0, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 1, 0, 0, 0, 0, 1}}; for (int i = 0; i < sm.getDimensions()[0]; i++) { for (int j = 0; j < sm.getDimensions()[1]; j++) { sm.set(connectedSynapses[i][j], i, j); } } for (int i = 0; i < 5; i++) { for (int j = 0; j < 10; j++) { assertEquals(connectedSynapses[i][j], sm.getIntValue(i, j)); } } int[] inputVector = new int[]{1, 0, 1, 0, 1, 0, 1, 0, 1, 0}; int[] results = new int[5]; int[] trueResults = new int[]{1, 1, 1, 1, 1}; sm.rightVecSumAtNZ(inputVector, results); for (int i = 0; i < results.length; i++) { assertEquals(trueResults[i], results[i]); } /////////////////////// connectedSynapses = new int[][]{ {1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {0, 0, 1, 1, 1, 1, 1, 1, 1, 1}, {0, 0, 0, 0, 1, 1, 1, 1, 1, 1}, {0, 0, 0, 0, 0, 0, 1, 1, 1, 1}, {0, 0, 0, 0, 0, 0, 0, 0, 1, 1}}; sm = new SparseBinaryMatrix(dimensions); for (int i = 0; i < sm.getDimensions()[0]; i++) { for (int j = 0; j < sm.getDimensions()[1]; j++) { sm.set(connectedSynapses[i][j], i, j); } } for (int i = 0; i < 5; i++) { for (int j = 0; j < 10; j++) { assertEquals(connectedSynapses[i][j], sm.getIntValue(i, j)); } } inputVector = new int[]{1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; results = new int[5]; trueResults = new int[]{10, 8, 6, 4, 2}; sm.rightVecSumAtNZ(inputVector, results); for (int i = 0; i < results.length; i++) { assertEquals(trueResults[i], results[i]); } } @Test public void testSetTrueCount() { doTestSetTrueCount(new SparseBinaryMatrix(this.dimensions)); doTestSetTrueCount(new LowMemorySparseBinaryMatrix(this.dimensions)); doTestSetTrueCount(new FastConnectionsMatrix(this.dimensions)); } private void doTestSetTrueCount(AbstractSparseBinaryMatrix sm) { int[][] connectedSynapses = new int[][]{ {1, 0, 0, 0, 0, 1, 0, 0, 0, 0}, {0, 1, 0, 0, 0, 0, 1, 0, 0, 0}, {0, 0, 1, 0, 0, 0, 0, 1, 0, 0}, {0, 0, 0, 1, 0, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 1, 0, 0, 0, 0, 1}}; for (int i = 0; i < sm.getDimensions()[0]; i++) { for (int j = 0; j < sm.getDimensions()[1]; j++) { sm.set(connectedSynapses[i][j], i, j); } } for (int i = 0; i < 5; i++) { for (int j = 0; j < 10; j++) { assertEquals(connectedSynapses[i][j], sm.getIntValue(i, j)); } } for (int i = 0; i < 5; i++) { assertEquals(2, sm.getTrueCount(i)); } } public static void fillWithSomeRandomValues(Object array, Random r, int... sizes) { for (int i = 0; i < sizes[0]; i++) if (sizes.length == 1) { ((int[])array)[i] = r.nextInt(2); } else { fillWithSomeRandomValues(Array.get(array, i), r, ArrayUtils.tail(sizes)); } } @Test public void testBackingStoreAndSliceAccessManyDimensions() { /*Create 3 dimensional matrix*/ int[] dimensions = {5, 5, 5}; doTestBackingStoreAndSliceAccessManyDimensions(new SparseBinaryMatrix(dimensions)); doTestBackingStoreAndSliceAccessManyDimensions(new LowMemorySparseBinaryMatrix(dimensions)); } private void doTestBackingStoreAndSliceAccessManyDimensions(AbstractSparseBinaryMatrix sm) { /*set diagonal element to true*/ sm.set(1, 0, 0, 0); sm.set(1, 1, 1, 1); sm.set(1, 2, 2, 2); sm.set(1, 3, 3, 3); sm.set(1, 4, 4, 4); for (int i = 0; i < 5; i++) { for (int j = 0; j < 5; j++) { for (int k = 0; k < 5; k++) { if (k == j & j == i) { assertEquals(1, sm.getIntValue(i, j, k)); } } } } int[] slice = (int[])sm.getSlice(4, 4); for (int i = 0; i < 5; i++) { assertEquals(1, sm.getTrueCount(i)); } System.out.println("slice:" + ArrayUtils.intArrayToString(slice)); assertEquals(1, slice[4]); /*update first row to true, other to false*/ for (int i = 0; i < 5; i++) { for (int j = 0; j < 5; j++) { for (int k = 0; k < 5; k++) { if (0 == i) { sm.set(1, i, j, k); } else { sm.set(0, i, j, k); } } } } assertEquals(25, sm.getTrueCounts()[0]); assertEquals(0, sm.getTrueCounts()[1]); } @Test public void testArraySet() { int[] dimensions = { 5, 2 }; doTestArraySet(new SparseBinaryMatrix(dimensions)); doTestArraySet(new LowMemorySparseBinaryMatrix(dimensions)); doTestArraySet(new FastConnectionsMatrix(dimensions)); } private void doTestArraySet(AbstractSparseBinaryMatrix sm) { int[] expected = { 1, 0, 0, 0, 1, 0, 1, 1, 1, 0 }; int[] values = { 1, 1, 1, 1, 1 }; int[] indexes = { 0, 4, 6, 7, 8 }; sm.set(indexes, values); int[] dense = new int[sm.getMaxIndex() + 1]; for (int i = 0; i < sm.getMaxIndex() + 1; i++) { dense[i] = sm.getIntValue(i); } assertArrayEquals(expected, dense); } @Test public void testGetSparseIndices() { doTestGetSparseIndices(new SparseBinaryMatrix(this.dimensions)); doTestGetSparseIndices(new LowMemorySparseBinaryMatrix(this.dimensions)); doTestGetSparseIndices(new FastConnectionsMatrix(this.dimensions)); } private void doTestGetSparseIndices(AbstractSparseBinaryMatrix sm) { int[] expected = {0, 5, 11, 16, 22, 27, 33, 38, 44, 49}; int[][] values = new int[][]{ {1, 0, 0, 0, 0, 1, 0, 0, 0, 0}, {0, 1, 0, 0, 0, 0, 1, 0, 0, 0}, {0, 0, 1, 0, 0, 0, 0, 1, 0, 0}, {0, 0, 0, 1, 0, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 1, 0, 0, 0, 0, 1}}; for (int i = 0; i < 5; i++) { for (int j = 0; j < 10; j++) { sm.set(values[i][j], new int[] {i, j}); } } int[] sdr = sm.getSparseIndices(); assertArrayEquals(expected, sdr); } @Test public void testSliceIndexes() { SparseBinaryMatrix sm = new SparseBinaryMatrix(this.dimensions); int[][] expected = { {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, {10, 11, 12, 13, 14, 15, 16, 17, 18, 19}, {20, 21, 22, 23, 24, 25, 26, 27, 28, 29}, {30, 31, 32, 33, 34, 35, 36, 37, 38, 39}, {40, 41, 42, 43, 44, 45, 46, 47, 48, 49}}; for (int i = 0; i < this.dimensions[0]; i++) assertArrayEquals(expected[i], sm.getSliceIndexes(new int[] {i})); } @Test public void testOr() { SparseBinaryMatrix sm = createDefaultMatrix(); int[] orBits = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; int[] expected = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; sm.or(orBits); assertArrayEquals(expected, (int[]) sm.getSlice(new int[] {0})); } @Test public void testAll() { SparseBinaryMatrix sm = createDefaultMatrix(); int[] all = {0, 5, 11, 16, 22, 27, 33, 38, 44, 49}; assertTrue(sm.all(all)); } private SparseBinaryMatrix createDefaultMatrix() { SparseBinaryMatrix sm = new SparseBinaryMatrix(this.dimensions); int[][] values = { {1, 0, 0, 0, 0, 1, 0, 0, 0, 0}, {0, 1, 0, 0, 0, 0, 1, 0, 0, 0}, {0, 0, 1, 0, 0, 0, 0, 1, 0, 0}, {0, 0, 0, 1, 0, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 1, 0, 0, 0, 0, 1}}; for (int i = 0; i < 5; i++) { for (int j = 0; j < 10; j++) { sm.set(values[i][j], new int[] {i, j}); } } return sm; } @Test public void testCalculateOverlap() { doTestCalculateOverlap(new SparseBinaryMatrix(this.dimensions)); doTestCalculateOverlap(new LowMemorySparseBinaryMatrix(this.dimensions)); doTestCalculateOverlap(new FastConnectionsMatrix(this.dimensions)); } private void doTestCalculateOverlap(AbstractSparseBinaryMatrix sm) { setupParameters(); parameters.setInputDimensions(new int[] { 10 }); parameters.setColumnDimensions(new int[] { 5 }); initSP(); /////////////////// // test stimulsThreshold = 2 parameters.set(KEY.STIMULUS_THRESHOLD, 3.0); initSP(); int[][] connectedSynapses = new int[][] { {1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {0, 0, 1, 1, 1, 1, 1, 1, 1, 1}, {0, 0, 0, 0, 1, 1, 1, 1, 1, 1}, {0, 0, 0, 0, 0, 0, 1, 1, 1, 1}, {0, 0, 0, 0, 0, 0, 0, 0, 1, 1}}; for(int i = 0;i < sm.getDimensions()[0];i++) { for(int j = 0;j < sm.getDimensions()[1];j++) { sm.set(connectedSynapses[i][j], i, j); } } mem.setConnectedMatrix(sm); for(int i = 0;i < 5;i++) { for(int j = 0;j < 10;j++) { assertEquals(connectedSynapses[i][j], sm.getIntValue(i, j)); } } int[] inputVector = new int[] { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; int[] overlaps = sp.calculateOverlap(mem, inputVector); int[] trueOverlaps = new int[] { 10, 8, 6, 4, 0 }; // last gets squelched by stimulus threshold of 3 double[] overlapsPct = sp.calculateOverlapPct(mem, overlaps); double[] trueOverlapsPct = new double[] { 1, 1, 1, 1, 0 }; assertTrue(Arrays.equals(trueOverlaps, overlaps)); assertTrue(Arrays.equals(trueOverlapsPct, overlapsPct)); } private Parameters parameters; private SpatialPooler sp; private Connections mem; public void setupParameters() { parameters = Parameters.getAllDefaultParameters(); parameters.set(KEY.INPUT_DIMENSIONS, new int[] { 5 });//5 parameters.set(KEY.COLUMN_DIMENSIONS, new int[] { 5 });//5 parameters.set(KEY.POTENTIAL_RADIUS, 3);//3 parameters.set(KEY.POTENTIAL_PCT, 0.5);//0.5 parameters.set(KEY.GLOBAL_INHIBITION, false); parameters.set(KEY.LOCAL_AREA_DENSITY, -1.0); parameters.set(KEY.NUM_ACTIVE_COLUMNS_PER_INH_AREA, 3.0); parameters.set(KEY.STIMULUS_THRESHOLD, 1.0); parameters.set(KEY.SYN_PERM_INACTIVE_DEC, 0.01); parameters.set(KEY.SYN_PERM_ACTIVE_INC, 0.1); parameters.set(KEY.SYN_PERM_TRIM_THRESHOLD, 0.05); parameters.set(KEY.SYN_PERM_CONNECTED, 0.1); parameters.set(KEY.MIN_PCT_OVERLAP_DUTY_CYCLES, 0.1); parameters.set(KEY.MIN_PCT_ACTIVE_DUTY_CYCLES, 0.1); parameters.set(KEY.DUTY_CYCLE_PERIOD, 10); parameters.set(KEY.MAX_BOOST, 10.0); parameters.set(KEY.SEED, 42); } private void initSP() { sp = new SpatialPooler(); mem = new Connections(); parameters.apply(mem); sp.init(mem); } }