/* --------------------------------------------------------------------- * 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.network; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import java.util.Arrays; import java.util.Map; import org.junit.AfterClass; import org.junit.Test; import org.numenta.nupic.Parameters; import org.numenta.nupic.Parameters.KEY; import org.numenta.nupic.algorithms.TemporalMemory; import org.numenta.nupic.model.Cell; import org.numenta.nupic.model.Column; import org.numenta.nupic.model.ComputeCycle; import org.numenta.nupic.model.DistalDendrite; import org.numenta.nupic.model.Connections; import org.numenta.nupic.model.ProximalDendrite; import org.numenta.nupic.model.SDR; import org.numenta.nupic.model.Segment; import org.numenta.nupic.network.sensor.ObservableSensor; import org.numenta.nupic.network.sensor.Publisher; import org.numenta.nupic.network.sensor.Sensor; import org.numenta.nupic.network.sensor.SensorParams; import org.numenta.nupic.network.sensor.SensorParams.Keys; import org.numenta.nupic.util.ArrayUtils; import org.numenta.nupic.util.MersenneTwister; import rx.Observer; import rx.Subscriber; /** * <p> * Tests which makes sure that indeterminacy never creeps in to the codebase. * This is verified by running the {@link TemporalMemory} using the same * configuration parameters, inputs and random number generator in the following * 3 modes:</p><p> * <ol> * <li>Straight through the TM algorithm class</li> * <li>Using a Layer with synchronous calls</li> * <li>Using the full NAPI and starting the Layer's thread</li> * </ol> * <p> * The result should be the same output regardless of input method * or algorithm configuration. * </p> * @author cogmission * */ public class AlgorithmDeterminacyTest { private static final int[][] TEST_AGGREGATION = new int[3][]; private static final int TM_EXPL = 0; private static final int TM_LYR = 1; private static final int TM_NAPI = 2; public static Parameters getParameters() { Parameters parameters = Parameters.getAllDefaultParameters(); parameters.set(KEY.INPUT_DIMENSIONS, new int[] { 8 }); parameters.set(KEY.COLUMN_DIMENSIONS, new int[] { 20 }); parameters.set(KEY.CELLS_PER_COLUMN, 6); //SpatialPooler specific parameters.set(KEY.POTENTIAL_RADIUS, 12);//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, 5.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); //Temporal Memory specific parameters.set(KEY.INITIAL_PERMANENCE, 0.2); parameters.set(KEY.CONNECTED_PERMANENCE, 0.8); parameters.set(KEY.MIN_THRESHOLD, 5); parameters.set(KEY.MAX_NEW_SYNAPSE_COUNT, 6); parameters.set(KEY.PERMANENCE_INCREMENT, 0.05); parameters.set(KEY.PERMANENCE_DECREMENT, 0.05); parameters.set(KEY.ACTIVATION_THRESHOLD, 4); parameters.set(KEY.RANDOM, new MersenneTwister(42)); return parameters; } @AfterClass public static void doTest() { System.out.println(Arrays.toString(TEST_AGGREGATION[TM_EXPL])); System.out.println(Arrays.toString(TEST_AGGREGATION[TM_LYR])); System.out.println(Arrays.toString(TEST_AGGREGATION[TM_NAPI])); assertTrue(Arrays.equals(TEST_AGGREGATION[TM_EXPL], TEST_AGGREGATION[TM_LYR])); assertTrue(Arrays.equals(TEST_AGGREGATION[TM_EXPL], TEST_AGGREGATION[TM_NAPI])); } @Test public void testTemporalMemoryExplicit() { final int[] input1 = new int[] { 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0 }; final int[] input2 = new int[] { 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0 }; final int[] input3 = new int[] { 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0 }; final int[] input4 = new int[] { 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0 }; final int[] input5 = new int[] { 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0 }; final int[] input6 = new int[] { 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1 }; final int[] input7 = new int[] { 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0 }; final int[][] inputs = { input1, input2, input3, input4, input5, input6, input7 }; Parameters p = getParameters(); Connections con = new Connections(); p.apply(con); TemporalMemory tm = new TemporalMemory(); TemporalMemory.init(con); ComputeCycle cc = null; for(int x = 0;x < 602;x++) { for(int[] i : inputs) { cc = tm.compute(con, ArrayUtils.where(i, ArrayUtils.WHERE_1), true); } } TEST_AGGREGATION[TM_EXPL] = SDR.asCellIndices(cc.activeCells); } /** * Temporary test to test basic sequence mechanisms */ @Test public void testTemporalMemoryThroughLayer() { Parameters p = getParameters(); final int[] input1 = new int[] { 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0 }; final int[] input2 = new int[] { 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0 }; final int[] input3 = new int[] { 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0 }; final int[] input4 = new int[] { 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0 }; final int[] input5 = new int[] { 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0 }; final int[] input6 = new int[] { 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1 }; final int[] input7 = new int[] { 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0 }; final int[][] inputs = { input1, input2, input3, input4, input5, input6, input7 }; Layer<int[]> l = new Layer<>(p, null, null, new TemporalMemory(), null, null); int timeUntilStable = 600; l.subscribe(new Observer<Inference>() { @Override public void onCompleted() {} @Override public void onError(Throwable e) { e.printStackTrace(); } @Override public void onNext(Inference output) {} }); // Now push some warm up data through so that "onNext" is called above for(int j = 0;j < timeUntilStable;j++) { for(int i = 0;i < inputs.length;i++) { l.compute(inputs[i]); } } for(int j = 0;j < 2;j++) { for(int i = 0;i < inputs.length;i++) { l.compute(inputs[i]); } } ComputeCycle cc = l.getInference().getComputeCycle(); TEST_AGGREGATION[TM_LYR] = SDR.asCellIndices(cc.activeCells); } @Test public void testThreadedPublisher() { Publisher manual = Publisher.builder() .addHeader("dayOfWeek") .addHeader("darr") .addHeader("B").build(); Sensor<ObservableSensor<String[]>> sensor = Sensor.create( ObservableSensor::create, SensorParams.create(Keys::obs, new Object[] {"name", manual})); Parameters p = getParameters(); Map<String, Map<String, Object>> settings = NetworkTestHarness.setupMap( null, // map 20, // n 0, // w 0, // min 0, // max 0, // radius 0, // resolution null, // periodic null, // clip Boolean.TRUE, // forced "dayOfWeek", // fieldName "darr", // fieldType (dense array as opposed to sparse array or "sarr") "SDRPassThroughEncoder"); // encoderType p.set(KEY.FIELD_ENCODING_MAP, settings); Network network = Network.create("test network", p) .add(Network.createRegion("r1") .add(Network.createLayer("1", p) .add(new TemporalMemory()) .add(sensor))); network.start(); network.observe().subscribe(new Subscriber<Inference>() { @Override public void onCompleted() {} @Override public void onError(Throwable e) { e.printStackTrace(); } @Override public void onNext(Inference i) {} }); final int[] input1 = new int[] { 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0 }; final int[] input2 = new int[] { 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0 }; final int[] input3 = new int[] { 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0 }; final int[] input4 = new int[] { 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0 }; final int[] input5 = new int[] { 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0 }; final int[] input6 = new int[] { 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1 }; final int[] input7 = new int[] { 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0 }; final int[][] inputs = { input1, input2, input3, input4, input5, input6, input7 }; // Now push some warm up data through so that "onNext" is called above int timeUntilStable = 602; for(int j = 0;j < timeUntilStable;j++) { for(int i = 0;i < inputs.length;i++) { manual.onNext(Arrays.toString(inputs[i])); } } manual.onComplete(); Layer<?> l = network.lookup("r1").lookup("1"); try { l.getLayerThread().join(); ComputeCycle cc = l.getInference().getComputeCycle(); TEST_AGGREGATION[TM_NAPI] = SDR.asCellIndices(cc.activeCells); }catch(Exception e) { assertEquals(InterruptedException.class, e.getClass()); } } @Test public void testModelClasses() { //Test Segment equality Column column1 = new Column(2, 0); Cell cell1 = new Cell(column1, 0); Segment s1 = new DistalDendrite(cell1, 0, 1, 0); assertTrue(s1.equals(s1)); // test == assertFalse(s1.equals(null)); Segment s2 = new DistalDendrite(cell1, 0, 1, 0); assertTrue(s1.equals(s2)); Cell cell2 = new Cell(column1, 0); Segment s3 = new DistalDendrite(cell2, 0, 1, 0); assertTrue(s1.equals(s3)); //Segment's Cell has different index Cell cell3 = new Cell(column1, 1); Segment s4 = new DistalDendrite(cell3, 0, 1, 0); assertFalse(s1.equals(s4)); //Segment has different index Segment s5 = new DistalDendrite(cell3, 1, 1, 0); assertFalse(s4.equals(s5)); assertTrue(s5.toString().equals("1")); assertEquals(-1, s4.compareTo(s5)); assertEquals(1, s5.compareTo(s4)); //Different type of segment Segment s6 = new ProximalDendrite(0); assertFalse(s5.equals(s6)); System.out.println(s4.compareTo(s5)); } }