/* ---------------------------------------------------------------------
* 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 General 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses.
*
* http://numenta.org/licenses/
* ---------------------------------------------------------------------
*/
package org.numenta.nupic.examples.napi.hotgym;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import org.numenta.nupic.Parameters;
import org.numenta.nupic.Parameters.KEY;
import org.numenta.nupic.algorithms.Anomaly;
import org.numenta.nupic.algorithms.CLAClassifier;
import org.numenta.nupic.algorithms.SpatialPooler;
import org.numenta.nupic.algorithms.TemporalMemory;
import org.numenta.nupic.datagen.ResourceLocator;
import org.numenta.nupic.encoders.Encoder;
import org.numenta.nupic.encoders.MultiEncoder;
import org.numenta.nupic.network.Inference;
import org.numenta.nupic.network.Layer;
import org.numenta.nupic.network.Network;
import org.numenta.nupic.network.Region;
import org.numenta.nupic.network.sensor.FileSensor;
import org.numenta.nupic.network.sensor.Sensor;
import org.numenta.nupic.network.sensor.SensorParams;
import org.numenta.nupic.network.sensor.SensorParams.Keys;
import rx.Observer;
import rx.Subscriber;
/**
* Demonstrates the Java version of the NuPIC Network API (NAPI) Demo.
*
* This demo demonstrates many powerful features of the HTM.java
* NAPI. Looking at the {@link NetworkAPIDemo#createBasicNetwork()} method demonstrates
* the conciseness of setting up a basic network. As you can see, the network is
* constructed in fluent style proceeding from the top-level {@link Network} container,
* to a single {@link Region}; then to a single {@link Layer}.
*
* Layers contain most of the operation logic and are the only constructs that contain
* algorithm components (i.e. {@link CLAClassifier}, {@link Anomaly} (the anomaly computer),
* {@link TemporalMemory}, {@link SpatialPooler}, and {@link Encoder} (actually, a {@link MultiEncoder}
* which can be the parent of many child encoders).
*
*
* @author cogmission
*
*/
public class NetworkAPIDemo {
/** 3 modes to choose from to demonstrate network usage */
static enum Mode { BASIC, MULTILAYER, MULTIREGION };
private Network network;
private File outputFile;
private PrintWriter pw;
private double predictedValue = 0.0;
public NetworkAPIDemo(Mode mode) {
switch(mode) {
case BASIC: network = createBasicNetwork(); break;
case MULTILAYER: network = createMultiLayerNetwork(); break;
case MULTIREGION: network = createMultiRegionNetwork(); break;
}
network.observe().subscribe(getSubscriber());
try {
outputFile = new File(System.getProperty("user.home").concat(File.separator).concat("napi_hotgym_demo_output.txt"));
pw = new PrintWriter(new FileWriter(outputFile));
}catch(IOException e) {
e.printStackTrace();
}
}
/**
* Creates a basic {@link Network} with 1 {@link Region} and 1 {@link Layer}. However
* this basic network contains all algorithmic components.
*
* @return a basic Network
*/
Network createBasicNetwork() {
Parameters p = NetworkDemoHarness.getParameters();
p = p.union(NetworkDemoHarness.getNetworkDemoTestEncoderParams());
// This is how easy it is to create a full running Network!
return Network.create("Network API Demo", p)
.add(Network.createRegion("Region 1")
.add(Network.createLayer("Layer 2/3", p)
.alterParameter(KEY.AUTO_CLASSIFY, Boolean.TRUE)
.add(Anomaly.create())
.add(new TemporalMemory())
.add(new SpatialPooler())
.add(Sensor.create(FileSensor::create, SensorParams.create(
Keys::path, "", ResourceLocator.path("rec-center-hourly.csv"))))));
}
/**
* Creates a {@link Network} containing one {@link Region} with multiple
* {@link Layer}s. This demonstrates the method by which multiple layers
* are added and connected; and the flexibility of the fluent style api.
*
* @return a multi-layer Network
*/
Network createMultiLayerNetwork() {
Parameters p = NetworkDemoHarness.getParameters();
p = p.union(NetworkDemoHarness.getNetworkDemoTestEncoderParams());
return Network.create("Network API Demo", p)
.add(Network.createRegion("Region 1")
.add(Network.createLayer("Layer 2/3", p)
.alterParameter(KEY.AUTO_CLASSIFY, Boolean.TRUE)
.add(Anomaly.create())
.add(new TemporalMemory()))
.add(Network.createLayer("Layer 4", p)
.add(new SpatialPooler()))
.add(Network.createLayer("Layer 5", p)
.add(Sensor.create(FileSensor::create, SensorParams.create(
Keys::path, "", ResourceLocator.path("rec-center-hourly.csv")))))
.connect("Layer 2/3", "Layer 4")
.connect("Layer 4", "Layer 5"));
}
/**
* Creates a {@link Network} containing 2 {@link Region}s with multiple
* {@link Layer}s in each.
*
* @return a multi-region Network
*/
Network createMultiRegionNetwork() {
Parameters p = NetworkDemoHarness.getParameters();
p = p.union(NetworkDemoHarness.getNetworkDemoTestEncoderParams());
return Network.create("Network API Demo", p)
.add(Network.createRegion("Region 1")
.add(Network.createLayer("Layer 2/3", p)
.alterParameter(KEY.AUTO_CLASSIFY, Boolean.TRUE)
.add(Anomaly.create())
.add(new TemporalMemory()))
.add(Network.createLayer("Layer 4", p)
.add(new SpatialPooler()))
.connect("Layer 2/3", "Layer 4"))
.add(Network.createRegion("Region 2")
.add(Network.createLayer("Layer 2/3", p)
.alterParameter(KEY.AUTO_CLASSIFY, Boolean.TRUE)
.add(Anomaly.create())
.add(new TemporalMemory())
.add(new SpatialPooler()))
.add(Network.createLayer("Layer 4", p)
.add(Sensor.create(FileSensor::create, SensorParams.create(
Keys::path, "", ResourceLocator.path("rec-center-hourly.csv")))))
.connect("Layer 2/3", "Layer 4"))
.connect("Region 1", "Region 2");
}
/**
* Demonstrates the composition of a {@link Subscriber} (may also use
* {@link Observer}). There are 3 methods one must be concerned with:
* </p>
* <p>
* <pre>
* 1. onCompleted(). Called when the stream is exhausted and will be closed.
* 2. onError(). Called when there is an underlying exception or error in the processing.
* 3. onNext(). Called for each processing cycle of the network. This is the method
* that is overridden to do downstream work in your application.
*
* @return
*/
Subscriber<Inference> getSubscriber() {
return new Subscriber<Inference>() {
@Override public void onCompleted() {
System.out.println("\nstream completed. see output: " + outputFile.getAbsolutePath());
try {
pw.flush();
pw.close();
}catch(Exception e) {
e.printStackTrace();
}
}
@Override public void onError(Throwable e) { e.printStackTrace(); }
@Override public void onNext(Inference i) { writeToFile(i, "consumption"); }
};
}
/**
* Primitive file appender for collecting output. This just demonstrates how to use
* {@link Subscriber#onNext(Object)} to accomplish some work.
*
* @param infer The {@link Inference} object produced by the Network
* @param classifierField The field we use in this demo for anomaly computing.
*/
private void writeToFile(Inference infer, String classifierField) {
try {
double newPrediction;
if(null != infer.getClassification(classifierField).getMostProbableValue(1)) {
newPrediction = (Double)infer.getClassification(classifierField).getMostProbableValue(1);
} else {
newPrediction = predictedValue;
}
if(infer.getRecordNum() > 0) {
double actual = (Double)infer.getClassifierInput()
.get(classifierField).get("inputValue");
double error = Math.abs(predictedValue - actual);
StringBuilder sb = new StringBuilder()
.append(infer.getRecordNum()).append(", ")
//.append("classifier input=")
.append(String.format("%3.2f", actual)).append(", ")
//.append("prediction= ")
.append(String.format("%3.2f", predictedValue)).append(", ")
.append(String.format("%3.2f", error)).append(", ")
//.append("anomaly score=")
.append(infer.getAnomalyScore());
pw.println(sb.toString());
pw.flush();
System.out.println(sb.toString());
} else {
}
predictedValue = newPrediction;
}catch(Exception e) {
e.printStackTrace();
pw.flush();
}
}
/**
* Simple run hook
*/
private void runNetwork() {
network.start();
}
/**
* Main entry point of the demo
* @param args
*/
public static void main(String[] args) {
// Substitute the other modes here to see alternate examples of Network construction
// in operation.
NetworkAPIDemo demo = new NetworkAPIDemo(Mode.MULTILAYER);
demo.runNetwork();
}
}