Skip to content
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
6 changes: 5 additions & 1 deletion src/main/java/org/numenta/nupic/FieldMetaType.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import org.numenta.nupic.encoders.SDRCategoryEncoder;
import org.numenta.nupic.encoders.SDRPassThroughEncoder;
import org.numenta.nupic.encoders.ScalarEncoder;
import org.numenta.nupic.util.Tuple;

/**
* Public values for the field data types
Expand Down Expand Up @@ -97,7 +98,10 @@ public <T> T decodeType(String input, Encoder<?> enc) {
case DATETIME : return (T)((DateEncoder)enc).parse(input);
case BOOLEAN : return (T)(Boolean.valueOf(input) == true ? new Integer(1) : new Integer(0));
case COORD :
case GEO : return (T)new double[] { Double.parseDouble(input.split("\\;")[0]), Double.parseDouble(input.split("\\;")[1]) };
case GEO : {
String[] parts = input.split("[\\s]*\\;[\\s]*");
return (T)new Tuple(Double.parseDouble(parts[0]), Double.parseDouble(parts[1]), Double.parseDouble(parts[2]));
}
case INTEGER :
case FLOAT : return (T)new Double(input);
case SARR :
Expand Down
7 changes: 5 additions & 2 deletions src/main/java/org/numenta/nupic/network/sensor/HTMSensor.java
Original file line number Diff line number Diff line change
Expand Up @@ -307,12 +307,16 @@ public Stream<int[]> getOutputStream() {
}
}

// NOTE: The "inputMap" here is a special local implementation
// of the "Map" interface, overridden so that we can access
// the keys directly (without hashing). This map is only used
// for this use case so it is ok to use this optimization as
// a convenience.
if(inputMap == null) {
inputMap = new InputMap();
inputMap.fTypes = fieldTypes;
}


final boolean isParallel = delegate.getInputStream().isParallel();

output = new ArrayList<>();
Expand Down Expand Up @@ -791,7 +795,6 @@ private void configureGeoBuilder(Map<String, Map<String, Object>> encoderSetting
* @param m the map containing the values
* @param key the key to be set.
*/
@SuppressWarnings("unchecked")
private void setGeoFieldBits(GeospatialCoordinateEncoder.Builder b, Map<String, Object> m, String key) {
String t = (String)m.get(key);
switch(key) {
Expand Down
30 changes: 30 additions & 0 deletions src/test/java/org/numenta/nupic/network/NetworkTestHarness.java
Original file line number Diff line number Diff line change
Expand Up @@ -247,5 +247,35 @@ public static Parameters getParameters() {
return parameters;
}

/**
* Parameters and meta information for the "Geospatial Test" encoder
* @return
*/
public static Map<String, Map<String, Object>> getGeospatialFieldEncodingMap() {
Map<String, Map<String, Object>> fieldEncodings = setupMap(null, 0, 0, 0.0D, 0.0D, 0.0D, 0.0D, (Boolean)null, (Boolean)null, (Boolean)null, "timestamp", "datetime", "DateEncoder");
fieldEncodings = setupMap(fieldEncodings, 50, 21, 0.0D, 100.0D, 0.0D, 0.1D, (Boolean)null, Boolean.TRUE, (Boolean)null, "consumption", "float", "ScalarEncoder");
fieldEncodings = setupMap(fieldEncodings, 999, 25, 0.0D, 100.0D, 0.0D, 0.1D, (Boolean)null, Boolean.TRUE, (Boolean)null, "location", "geo", "GeospatialCoordinateEncoder");

fieldEncodings.get("timestamp").put(Parameters.KEY.DATEFIELD_TOFD.getFieldName(), new Tuple(new Object[]{Integer.valueOf(21), Double.valueOf(9.5D)}));
fieldEncodings.get("timestamp").put(Parameters.KEY.DATEFIELD_PATTERN.getFieldName(), "MM/dd/YY HH:mm");

fieldEncodings.get("location").put("timestep", "60");
fieldEncodings.get("location").put("scale", "30");

return fieldEncodings;
}

/**
* Parameters and meta information for the "Geospatial Test" encoder
* @return
*/
public static Parameters getGeospatialTestEncoderParams() {
Map<String, Map<String, Object>> fieldEncodings = getGeospatialFieldEncodingMap();

Parameters p = Parameters.getEncoderDefaultParameters();
p.setParameterByKey(KEY.FIELD_ENCODING_MAP, fieldEncodings);

return p;
}

}
128 changes: 128 additions & 0 deletions src/test/java/org/numenta/nupic/network/sensor/HTMSensorTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import java.util.Map;
import java.util.stream.Stream;

import org.junit.Ignore;
import org.junit.Test;
import org.numenta.nupic.FieldMetaType;
import org.numenta.nupic.Parameters;
Expand All @@ -46,7 +47,9 @@
import org.numenta.nupic.encoders.MultiEncoder;
import org.numenta.nupic.encoders.RandomDistributedScalarEncoder;
import org.numenta.nupic.encoders.SDRCategoryEncoder;
import org.numenta.nupic.network.NetworkTestHarness;
import org.numenta.nupic.network.sensor.SensorParams.Keys;
import org.numenta.nupic.util.MersenneTwister;
import org.numenta.nupic.util.Tuple;

/**
Expand Down Expand Up @@ -618,5 +621,130 @@ public void testInputIntegerArray() {
Stream<int[]> outputStream = htmSensor.getOutputStream();
assertEquals(884, ((int[])outputStream.findFirst().get()).length);
}

@Test
public void testWithGeospatialEncoder() {
Publisher manual = Publisher.builder()
.addHeader("timestamp,consumption,location")
.addHeader("datetime,float,geo")
.addHeader("T,,").build();

Sensor<ObservableSensor<String[]>> sensor = Sensor.create(
ObservableSensor::create, SensorParams.create(Keys::obs, "", manual));

Parameters p = NetworkTestHarness.getParameters().copy();
p = p.union(NetworkTestHarness.getGeospatialTestEncoderParams());
p.setParameterByKey(KEY.RANDOM, new MersenneTwister(42));
p.setParameterByKey(KEY.AUTO_CLASSIFY, Boolean.TRUE);

HTMSensor<ObservableSensor<String[]>> htmSensor = (HTMSensor<ObservableSensor<String[]>>)sensor;


//////////////////////////////////////////////////////////////
// Test Header Configuration //
//////////////////////////////////////////////////////////////

// Cast the ValueList to the more complex type (Header)
Header meta = (Header)htmSensor.getMetaInfo();
assertTrue(meta.getFieldTypes().stream().allMatch(
l -> l.equals(FieldMetaType.DATETIME) || l.equals(FieldMetaType.FLOAT) || l.equals(FieldMetaType.GEO)));

// Negative test (Make sure "GEO" is configured and expected
assertFalse(meta.getFieldTypes().stream().allMatch(
l -> l.equals(FieldMetaType.DATETIME) || l.equals(FieldMetaType.FLOAT)));


assertTrue(meta.getFieldNames().stream().allMatch(
l -> l.equals("timestamp") || l.equals("consumption") || l.equals("location")));
assertTrue(meta.getFlags().stream().allMatch(
l -> l.equals(SensorFlags.T) || l.equals(SensorFlags.B)));

Encoder<Object> multiEncoder = htmSensor.getEncoder();
assertNotNull(multiEncoder);
assertTrue(multiEncoder instanceof MultiEncoder);


//////////////////////////////////////////////////////////////
// Test Encoder Composition //
//////////////////////////////////////////////////////////////

List<EncoderTuple> encoders = null;

// NEGATIVE TEST: first so that we can reuse the sensor below - APPLY WRONG PARAMS
try {
htmSensor.initEncoder(getTestEncoderParams()); // <--- WRONG PARAMS
// Should fail here
fail();
encoders = multiEncoder.getEncoders(multiEncoder);
assertEquals(2, encoders.size());
}catch(IllegalArgumentException e) {
assertEquals("Coordinate encoder never initialized: location", e.getMessage());
}

/////////////////////////////////////

// Recreate Sensor for POSITIVE TEST. Set the Local parameters on the Sensor
sensor = Sensor.create(
ObservableSensor::create, SensorParams.create(Keys::obs, "", manual));
htmSensor = (HTMSensor<ObservableSensor<String[]>>)sensor;
htmSensor.initEncoder(p);

multiEncoder = htmSensor.getEncoder();
assertNotNull(multiEncoder);
assertTrue(multiEncoder instanceof MultiEncoder);
encoders = multiEncoder.getEncoders(multiEncoder);
assertEquals(3, encoders.size());

Sensor<ObservableSensor<String[]>> finalSensor = sensor;

(new Thread() {
public void run() {
manual.onNext("7/12/10 13:10,35.3,40.6457;-73.7962;5"); //5 = meters per second
}
}).start();


int[] output = ((HTMSensor<ObservableSensor<String[]>>)finalSensor).getOutputStream().findFirst().get();

int[] expected = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };

assertTrue(Arrays.equals(expected, output));
}

}
2 changes: 2 additions & 0 deletions vocabulary.dictionary
Original file line number Diff line number Diff line change
Expand Up @@ -185,3 +185,5 @@ href
substeps
polyline
toolbar
geospatial
ok