All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.numenta.nupic.network.Network Maven / Gradle / Ivy

/* ---------------------------------------------------------------------
 * 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.network;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.numenta.nupic.Connections;
import org.numenta.nupic.Parameters;
import org.numenta.nupic.Parameters.KEY;
import org.numenta.nupic.algorithms.CLAClassifier;
import org.numenta.nupic.encoders.MultiEncoder;
import org.numenta.nupic.examples.network.NetworkAPIDemo;
import org.numenta.nupic.network.sensor.HTMSensor;
import org.numenta.nupic.network.sensor.Sensor;
import org.numenta.nupic.network.sensor.SensorFactory;
import org.numenta.nupic.research.SpatialPooler;
import org.numenta.nupic.research.TemporalMemory;

import rx.Observable;

/**
 * 

* A {@code Network} is the fundamental component of the HTM.java Network API. * It is comprised of {@link Region}s which are in turn comprised of {@link Layer}s; * each Layer directly containing one or more algorithm or computational components * such (i.e. {@link Sensor}, {@link MultiEncoder}, {@link SpatialPooler}, * {@link TemporalMemory}, {@link CLAClassifier} etc.) *

*

* Networks in HTM.java are extremely easy to compose. For instance, here is an example * of a network which contains everything: *

 * Parameters p = NetworkTestHarness.getParameters();
 * p = p.union(NetworkTestHarness.getNetworkDemoTestEncoderParams());
 * 
 * Network network = Network.create("test network", p)
 *     .add(Network.createRegion("r1")
 *         .add(Network.createLayer("1", 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"))))));
 * 
* *

* As you can see, {@code Networks} can be composed in "fluent" style making their * declaration much more concise. * * While the above Network contains only 1 Region and only 1 Layer, Networks with many Regions * and Layers within them; may be composed. For example: *

 * Connections cons = new Connections();
 * 
 * Network network = Network.create("test network", p)
 *     .add(Network.createRegion("r1")
 *         .add(Network.createLayer("1", p)
 *             .add(Anomaly.create())
 *             .add(new TemporalMemory())
 *             .add(new SpatialPooler())))
 *     .add(Network.createRegion("r2")
 *         .add(Network.createLayer("1", p)
 *             .using(cons)
 *             .alterParameter(KEY.AUTO_CLASSIFY, Boolean.TRUE)
 *             .add(new TemporalMemory()))                    
 *         .add(Network.createLayer("2", p)
 *             .using(cons)
 *             .add(new SpatialPooler())
 *             .add(Sensor.create(FileSensor::create, SensorParams.create(
 *                 Keys::path, "", ResourceLocator.path("rec-center-hourly.csv")))))
 *         .connect("1", "2")) // Tell the Region to connect the two layers {@link Region#connect(String, String)}.
 *     .connect("r1", "r2");   // Tell the Network to connect the two regions {@link Network#connect(String, String)}.
 * 
* As you can see above, a {@link Connections} object is being shared among Region "r2"'s inner layers * via the use of the {@link Layer#using(Connections)} method. Additionally, {@link Parameters} may be * altered in place without changing the values in the original Parameters object, by calling {@link Layer#alterParameter(KEY, Object)} * as seen in the above examples. * *

* Networks can be "observed", meaning they can return an {@link Observable} object * which can be operated on in the map-reduce pattern of usage; and they can be * subscribed to, to receive {@link Inference} objects as output from every process * cycle. For instance: *

 * network.observe().subscribe(new Subscriber<Inference>() {
 *      public void onCompleted() { System.out.println("Input Completed!"); }
 *      public void onError(Throwable e) { e.printStackTrace(); }
 *      public void onNext(Inference i) {
 *          System.out.println(String.format("%d: %s", i.getRecordNum(), Arrays.toString(i.getSDR())));
 *      }
 * });
 * 
*

*

* Likewise, each Region within a Network may be "observed" and/or subscribed to; and each * Layer may be observed and subscribed to as well - for those instances where you would like * to obtain the processing from an individual component within the network for use outside * the network in some other area of your application. To find and subscribe to individual * components try: *

 * Network network = ...
 * 
 * Region region = network.lookup("<region name>");
 * region.observe().subscribe(new Subscriber<Inference>() {
 *     public void onCompleted() { System.out.println("Input Completed!"); }
 *     public void onError(Throwable e) { e.printStackTrace(); }
 *     public void onNext(Inference i) {
 *         int[] sdr = i.getSDR();
 *         do something...
 *     }    
 * }
 * 
 * Layer l2_3 = region.lookup("<layer name>");
 * l2_3.observe().subscribe(new Subscriber<Inference>() {
 *     public void onCompleted() { System.out.println("Input Completed!"); }
 *     public void onError(Throwable e) { e.printStackTrace(); }
 *     public void onNext(Inference i) {
 *         int[] sdr = i.getSDR();
 *         do something...
 *     }    
 * }
 * 
* * In addition there are many usage examples to be found in the {@link org.numenta.nupic.examples.network} package * where there are tests which may be examined for details, and the {@link NetworkAPIDemo} * * @author David Ray * @see Region * @see Layer * @see Inference * @see ManualInput * @see NetworkAPIDemo */ public class Network { public enum Mode { MANUAL, AUTO, REACTIVE }; private String name; private Parameters parameters; private HTMSensor sensor; private MultiEncoder encoder; private Region head; private Region tail; private Region sensorRegion; private List regions = new ArrayList<>(); /** * Creates a new {@link Network} * @param name * @param parameters */ public Network(String name, Parameters parameters) { this.name = name; this.parameters = parameters; if(parameters == null) { throw new IllegalArgumentException("Network Parameters were null."); } } /** * Creates and returns an implementation of {@link Network} * * @param name * @param parameters * @return */ public static Network create(String name, Parameters parameters) { return new Network(name, parameters); } /** * Creates and returns a child {@link Region} of this {@code Network} * * @param name The String identifier for the specified {@link Region} * @return */ public static Region createRegion(String name) { Network.checkName(name); Region r = new Region(name, null); return r; } /** * Creates a {@link Layer} to hold algorithmic components and returns * it. * * @param name the String identifier for the specified {@link Layer} * @param p the {@link Parameters} to use for the specified {@link Layer} * @return */ @SuppressWarnings("rawtypes") public static Layer createLayer(String name, Parameters p) { Network.checkName(name); return new Layer(name, null, p); } /** * Returns the String identifier for this {@code Network} * @return */ public String getName() { return name; } /** * If {@link Network.Mode} == {@link Mode#AUTO}, calling this * method will start the main engine thread which pulls in data * from the connected {@link Sensor}(s). * * Warning: Calling this method with any other Mode than * {@link Mode#AUTO} will result in an {@link UnsupportedOperationException} * being thrown. */ public void start() { if(regions.size() < 1) { throw new IllegalStateException("Nothing to start - 0 regions"); } Region tail = regions.get(0); Region upstream = tail; while((upstream = upstream.getUpstreamRegion()) != null) { tail = upstream; } tail.start(); } /** * Halts this {@code Network}, stopping all threads and closing * all {@link SensorFactory} connections to incoming data, freeing up * any resources associated with the input connections. */ public void halt() { if(regions.size() == 1) { this.tail = regions.get(0); } tail.halt(); } /** * Pauses all underlying {@code Network} nodes, maintaining any * connections (leaving them open until they possibly time out). * Does nothing to prevent any sensor connections from timing out * on their own. */ public void pause() { throw new UnsupportedOperationException("Pausing is not (yet) supported."); } /** * Returns the current {@link Mode} with which this {@link Network} is * currently configured. * * @return */ public Mode getMode() { // TODO Auto-generated method stub return null; } /** * Finds any {@link Region} containing a {@link Layer} which contains a {@link TemporalMemory} * and resets them. */ public void reset() { for(Region r : regions) { r.reset(); } } /** * Resets the recordNum in all {@link Region}s. */ public void resetRecordNum() { for(Region r : regions) { r.resetRecordNum(); } } /** * Returns an {@link Observable} capable of emitting {@link Inference}s * which contain the results of this {@code Network}'s processing chain. * @return */ public Observable observe() { if(regions.size() == 1) { this.head = regions.get(0); } return head.observe(); } /** * Returns the top-most (last in execution order from * bottom to top) {@link Region} in this {@code Network} * * @return */ public Region getHead() { if(regions.size() == 1) { this.head = regions.get(0); } return this.head; } /** * Returns the bottom-most (first in execution order from * bottom to top) {@link Region} in this {@code Network} * * @return */ public Region getTail() { if(regions.size() == 1) { this.tail = regions.get(0); } return this.tail; } /** * Returns a {@link Iterator} capable of walking the tree of regions * from the root {@link Region} down through all the child Regions. In turn, * a {@link Region} may be queried for a {@link Iterator} which will return * an iterator capable of traversing the Region's contained {@link Layer}s. * * @return */ public Iterator iterator() { return getRegions().iterator(); } /** * Used to manually input data into a {@link Network}, the other way * being the call to {@link Network#start()} for a Network that contains a * Region that contains a {@link Layer} which in turn contains a {@link Sensor} -OR- * subscribing a receiving Region to this Region's output Observable. * * @param input One of (int[], String[], {@link ManualInput}, or Map<String, Object>) */ public void compute(T input) { if(regions.size() == 1) { this.tail = regions.get(0); } tail.compute(input); } /** * Connects the specified source to the specified sink (the order of * processing flows from source to sink, or lower level region to higher * level region). * @param regionSink the receiving end of the connection * @param regionSource the source end of the connection * * @return this {@code Network} */ public Network connect(String regionSink, String regionSource) { Region source = lookup(regionSource); if(source == null) { throw new IllegalArgumentException("Region with name: " + regionSource + " not added to Network."); } Region sink = lookup(regionSink); if(sink == null) { throw new IllegalArgumentException("Region with name: " + regionSink + " not added to Network."); } sink.connect(source); tail = tail == null ? source : tail; head = head == null ? sink : head; Region bottom = source; while((bottom = bottom.getUpstreamRegion()) != null) { tail = bottom; } Region top = sink; while((top = top.getDownstreamRegion()) != null) { head = top; } return this; } /** * Adds a {@link Region} to this {@code Network} * @param region * @return */ public Network add(Region region) { regions.add(region); region.setNetwork(this); return this; } /** * Returns a {@link List} view of the contained {@link Region}s. * @return */ public List getRegions() { return new ArrayList(regions); } /** * Sets a reference to the {@link Region} which contains the {@link Sensor} * (if any). * * @param r */ public void setSensorRegion(Region r) { this.sensorRegion = r; } /** * Returns a reference to the {@link Region} which contains the {@link Sensor} * * @return the Region which contains the Sensor */ public Region getSensorRegion() { return sensorRegion; } /** * Returns the {@link Region} with the specified name * or null if it doesn't exist within this {@code Network} * @param regionName * @return */ public Region lookup(String regionName) { for(Region r : regions) { if(r.getName().equals(regionName)) { return r; } } return null; } /** * Returns the network-level {@link Parameters}. * @return */ public Parameters getParameters() { return parameters; } /** * Sets the reference to this {@code Network}'s Sensor * @param sensor */ public void setSensor(HTMSensor sensor) { this.sensor = sensor; this.sensor.initEncoder(this.parameters); } /** * Returns the encoder present in one of this {@code Network}'s * {@link Sensor}s * * @return */ public HTMSensor getSensor() { return sensor; } /** * Sets the {@link MultiEncoder} on this Network * @param e */ public void setEncoder(MultiEncoder e) { this.encoder = e; } /** * Returns the {@link MultiEncoder} with which this Network is configured. * @return */ public MultiEncoder getEncoder() { return this.encoder; } /** * Checks the name for suitability within a given network, * checking for reserved characters and such. * * @param name */ private static void checkName(String name) { if(name.indexOf(":") != -1) { throw new IllegalArgumentException("\":\" is a reserved character."); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy