Skip to content

Commit abe0a39

Browse files
authored
Merge pull request #760 from htm-community/hotgym_napi
Example implemented using NetworkAPI
2 parents 7059371 + b162f83 commit abe0a39

File tree

14 files changed

+356
-53
lines changed

14 files changed

+356
-53
lines changed

bindings/py/tests/regions/network_test.py

+6-4
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,9 @@
3232

3333
TEST_DATA = [0,1,2,3,4]
3434
EXPECTED_RESULT1 = [ 4, 5 ]
35-
EXPECTED_RESULT2 = [ 4, 11, 28, 42, 43, 87, 89, 93, 110, 127, 132, 137, 149, 187, 193]
36-
EXPECTED_RESULT3 = [ 134, 371, 924, 1358, 1386, 2791, 2876, 2996, 3526, 4089, 4242, 4406, 4778, 5994, 6199]
35+
#EXPECTED_RESULT2 = [ 4, 11, 28, 42, 43, 87, 89, 93, 110, 127, 132, 137, 149, 187, 193]
36+
EXPECTED_RESULT2 = [ 16, 32, 44, 81, 104, 109, 114, 166, 197, 198 ]
37+
EXPECTED_RESULT3 = [ 518, 1043, 1436, 2606, 3338, 3495, 3676, 5332, 6310, 6361 ]
3738

3839

3940
class LinkRegion(PyRegion):
@@ -312,12 +313,13 @@ def testBuiltInRegions(self):
312313

313314
sp_output = sp.getOutputArray("bottomUpOut")
314315
sdr = sp_output.getSDR()
316+
#print(sdr)
315317
self.assertTrue(np.array_equal(sdr.sparse, EXPECTED_RESULT2))
316318

317319
tm_output = tm.getOutputArray("predictedActiveCells")
318320
sdr = tm_output.getSDR()
319-
print(sdr.sparse)
320-
print(EXPECTED_RESULT3)
321+
#print(sdr)
322+
#print(EXPECTED_RESULT3)
321323
self.assertTrue(np.array_equal(sdr.sparse, EXPECTED_RESULT3))
322324

323325
def testExecuteCommand1(self):

src/CMakeLists.txt

+26
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,7 @@ set(examples_files
185185
examples/hotgym/Hotgym.cpp # contains conflicting main()
186186
examples/hotgym/HelloSPTP.cpp
187187
examples/hotgym/HelloSPTP.hpp
188+
examples/napi_sine/napi_sine.cpp
188189
examples/mnist/MNIST_SP.cpp
189190
)
190191

@@ -356,6 +357,30 @@ else()
356357
${EXTERNAL_INCLUDES}
357358
)
358359
endif()
360+
361+
#########################################################
362+
## NetworkAPI version of benchmark_sine
363+
364+
set(src_executable_napi_sine napi_sine)
365+
add_executable(${src_executable_napi_sine} examples/napi_sine/napi_sine.cpp)
366+
# link with the static library
367+
target_link_libraries(${src_executable_napi_sine}
368+
${INTERNAL_LINKER_FLAGS}
369+
${core_library}
370+
${COMMON_OS_LIBS}
371+
)
372+
target_compile_options( ${src_executable_napi_sine} PUBLIC ${INTERNAL_CXX_FLAGS})
373+
target_compile_definitions(${src_executable_napi_sine} PRIVATE ${COMMON_COMPILER_DEFINITIONS})
374+
target_include_directories(${src_executable_napi_sine} PRIVATE
375+
${CORE_LIB_INCLUDES}
376+
${EXTERNAL_INCLUDES}
377+
)
378+
add_custom_target(sine_napi_run
379+
COMMAND ${src_executable_napi_sine}
380+
DEPENDS ${src_executable_napi_sine}
381+
COMMENT "Executing ${src_executable_napi_sine}"
382+
VERBATIM)
383+
359384
#########################################################
360385
## MNIST Spatial Pooler Example
361386
#
@@ -392,6 +417,7 @@ install(TARGETS
392417

393418
install(TARGETS
394419
${src_executable_hotgym}
420+
${src_executable_napi_sine}
395421
${src_executable_mnistsp}
396422
RUNTIME DESTINATION bin
397423
LIBRARY DESTINATION lib

src/examples/hotgym/HelloSPTP.cpp

+9-7
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ EPOCHS = 2; // make test faster in Debug
121121
input.addNoise(0.01f, rnd); //change 1% of the SDR for each iteration, this makes a random sequence, but seemingly stable
122122
tRng.stop();
123123

124-
//SP (global x local)
124+
//SP (global and local)
125125
if(useSPlocal) {
126126
tSPloc.start();
127127
spLocal.compute(input, true, outSPlocal);
@@ -154,7 +154,7 @@ EPOCHS = 2; // make test faster in Debug
154154
avgAnomOld_ = avgAnom10.getCurrentAvg(); //update
155155
}
156156
tAnLikelihood.start();
157-
anLikelihood.anomalyProbability(an); //FIXME AnLikelihood is 0.0, probably not working correctly
157+
anLikely = anLikelihood.anomalyProbability(an);
158158
tAnLikelihood.stop();
159159

160160

@@ -173,13 +173,14 @@ EPOCHS = 2; // make test faster in Debug
173173
<< "\n";
174174

175175
// output values
176-
cout << "Epoch = " << e << endl;
176+
cout << "Epoch = " << e+1 << endl;
177177
cout << "Anomaly = " << an << endl;
178178
cout << "Anomaly (avg) = " << avgAnom10.getCurrentAvg() << endl;
179179
cout << "Anomaly (Likelihood) = " << anLikely << endl;
180-
cout << "SP (g)= " << outSP << endl;
181-
cout << "SP (l)= " << outSPlocal <<endl;
182-
cout << "TM= " << outTM << endl;
180+
cout << "input = " << input << endl;
181+
if(useSPlocal) cout << "SP (g)= " << outSP << endl;
182+
if(useSPlocal) cout << "SP (l)= " << outSPlocal <<endl;
183+
if(useTM) cout << "TM= " << outTM << endl;
183184

184185
//timers
185186
cout << "==============TIMERS============" << endl;
@@ -220,7 +221,7 @@ EPOCHS = 2; // make test faster in Debug
220221
const float goldAnAvg = 0.411894f; // ...the averaged value, on the other hand, should improve/decrease.
221222

222223
#ifdef _ARCH_DETERMINISTIC
223-
if(EPOCHS == 5000) {
224+
if(e+1 == 5000) {
224225
//these hand-written values are only valid for EPOCHS = 5000 (default), but not for debug and custom runs.
225226
NTA_CHECK(input == goldEnc) << "Deterministic output of Encoder failed!\n" << input << "should be:\n" << goldEnc;
226227
if(useSPglobal) { NTA_CHECK(outSPglobal == goldSP) << "Deterministic output of SP (g) failed!\n" << outSP << "should be:\n" << goldSP; }
@@ -230,6 +231,7 @@ EPOCHS = 2; // make test faster in Debug
230231
<< "Deterministic output of Anomaly failed! " << an << "should be: " << goldAn;
231232
NTA_CHECK(static_cast<UInt>(avgAnom10.getCurrentAvg() * 10000.0f) == static_cast<UInt>(goldAnAvg * 10000.0f))
232233
<< "Deterministic average anom score failed:" << avgAnom10.getCurrentAvg() << " should be: " << goldAnAvg;
234+
std::cout << "outputs match\n";
233235
}
234236
#endif
235237

src/examples/napi_sine/README.md

+83
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
# C++ example using Network API
2+
The program `sine_napi` is an example of an app using the Network API tools
3+
available in the htm.core library. In this example we generate a sine wave
4+
with some noise as the input. This is passed to an encoder to turn that
5+
into a quantized format. This is passed to an instance of SpatialPooler (SP).
6+
7+
The output of the SP passed on to the temporalMemory (TM) algorithm. The
8+
output of the TM can be written to a file so that it can be plotted.
9+
10+
```
11+
///////////////////////////////////////////////////////////////
12+
//
13+
// .------------------.
14+
// | encoder |
15+
// data--->| (RDSERegion) |
16+
// | |
17+
// `------------------'
18+
// |
19+
// .------------------.
20+
// | SP (global) |
21+
// | (SPRegion) |
22+
// | |
23+
// `------------------'
24+
// |
25+
// .------------------.
26+
// | TM |
27+
// | (TMRegion) |
28+
// | |
29+
// `------------------'
30+
//
31+
//////////////////////////////////////////////////////////////////
32+
```
33+
34+
Each "region" is a wrapper around an algorithm. This wrapper provides a uniform interface that can be plugged into the Network API engine for execution. The htm.core library contains regions for each of the primary algorithms in the library. The user can create their own algorithms and corresponding regions and plug them into the Network API engine by registering them with the Network class. The following chart shows the 'built-in' C++ regions.
35+
<table>
36+
<thead>
37+
<tr>
38+
<th>Built-in Region</th>
39+
<th>Algorithm</th>
40+
</tr>
41+
</thead>
42+
<tbody>
43+
<tr>
44+
<td>ScalarSensor</td>
45+
<td>ScalarEncoder; Original encoder for numeric values</td>
46+
</tr>
47+
<tr>
48+
<td>RDSERegion</td>
49+
<td>RandomDistributedScalarEncoder (RDSE); advanced encoder for numeric values.</td>
50+
</tr>
51+
<tr>
52+
<td>SPRegion</td>
53+
<td>SpatialPooler (SP)</td>
54+
</tr>
55+
<tr>
56+
<td>TMRegion</td>
57+
<td>TemporalMemory (TM)</td>
58+
</tr>
59+
<tr>
60+
<td>VectorFileSensor</td>
61+
<td>for reading from a file</td>
62+
</tr>
63+
<tr>
64+
<td>VectorFileEffector</td>
65+
<td>for writing to a file</td>
66+
</tr>
67+
</tbody>
68+
</table>
69+
70+
## Usage
71+
72+
```
73+
sine_napi [iterations [filename]]
74+
```
75+
- *iterations* is the number of times to execute the regions configured into the network. The default is 5000.
76+
- *filename* is the path for a file to be written which contains the following for each iteration. The default is no file written.
77+
```
78+
<iteration>, <sin data>, <anomaly>\n
79+
```
80+
81+
82+
## Experimentation
83+
It is intended that this program be used as a launching point for experimenting with combinations of the regions and using different parameters. Try it and see what happens...

src/examples/napi_sine/napi_sine.cpp

+163
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
/* ---------------------------------------------------------------------
2+
* HTM Community Edition of NuPIC
3+
* Copyright (C) 2013-2015, Numenta, Inc.
4+
*
5+
* This program is free software: you can redistribute it and/or modify
6+
* it under the terms of the GNU Affero Public License version 3 as
7+
* published by the Free Software Foundation.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12+
* See the GNU Affero Public License for more details.
13+
*
14+
* You should have received a copy of the GNU Affero Public License
15+
* along with this program. If not, see http://www.gnu.org/licenses.
16+
* --------------------------------------------------------------------- */
17+
18+
#include <htm/engine/Network.hpp>
19+
#include <htm/utils/Random.hpp>
20+
#include <htm/utils/Log.hpp>
21+
#include <htm/ntypes/Value.hpp>
22+
#include "htm/utils/MovingAverage.hpp"
23+
#include "htm/algorithms/AnomalyLikelihood.hpp"
24+
#include <fstream>
25+
26+
using namespace htm;
27+
28+
static bool verbose = true;
29+
#define VERBOSE if (verbose) std::cout << " "
30+
31+
32+
//this runs as an executable
33+
34+
int main(int argc, char* argv[]) {
35+
htm::UInt EPOCHS = 5000; // number of iterations (calls to encoder/SP/TP compute() )
36+
#ifndef NDEBUG
37+
EPOCHS = 2; // make test faster in Debug
38+
#endif
39+
40+
const UInt DIM_INPUT = 1000; // Width of encoder output
41+
const UInt COLS = 2048; // number of columns in SP, TP
42+
const UInt CELLS = 8; // cells per column in TP
43+
Random rnd(42); // uses fixed seed for deterministic output
44+
std::ofstream ofs;
45+
46+
std::string encoder_params = "{size: " + std::to_string(DIM_INPUT) + ", sparsity: 0.2, radius: 0.03, seed: 2019, noise: 0.01}";
47+
std::string sp_global_params = "{columnCount: " + std::to_string(COLS) + ", globalInhibition: true}";
48+
std::string tm_params = "{cellsPerColumn: " + std::to_string(CELLS) + ", orColumnOutputs: true}";
49+
50+
// Runtime arguments: napi_sine [epochs [filename]]
51+
if(argc >= 2) {
52+
EPOCHS = std::stoi(argv[1]); // number of iterations (default 5000)
53+
}
54+
if (argc >= 3) {
55+
ofs.open(argv[2], std::ios::out); // output filename (for plotting)
56+
}
57+
58+
try {
59+
60+
std::cout << "initializing. DIM_INPUT=" << DIM_INPUT << ", COLS=" << COLS << ", CELLS=" << CELLS << "\n";
61+
62+
Network net;
63+
64+
// Declare the regions to use
65+
std::shared_ptr<Region> encoder = net.addRegion("encoder", "RDSERegion", encoder_params);
66+
std::shared_ptr<Region> sp_global = net.addRegion("sp_global", "SPRegion", sp_global_params);
67+
std::shared_ptr<Region> tm = net.addRegion("tm", "TMRegion", tm_params);
68+
69+
// Setup data flows between regions
70+
net.link("encoder", "sp_global", "", "", "encoded", "bottomUpIn");
71+
net.link("sp_global", "tm", "", "", "bottomUpOut", "bottomUpIn");
72+
73+
net.initialize();
74+
75+
76+
///////////////////////////////////////////////////////////////
77+
//
78+
// .----------------.
79+
// | encoder |
80+
// data--->| (RDSERegion) |
81+
// | |
82+
// `-----------------'
83+
// |
84+
// .-----------------.
85+
// | sp_global |
86+
// | (SPRegion) |
87+
// | |
88+
// `-----------------'
89+
// |
90+
// .-----------------.
91+
// | tm |
92+
// | (TMRegion) |
93+
// | |
94+
// `-----------------'
95+
//
96+
//////////////////////////////////////////////////////////////////
97+
98+
99+
// enable this to see a trace as it executes
100+
//net.setLogLevel(LogLevel::LogLevel_Verbose);
101+
102+
std::cout << "Running: " << EPOCHS << " Iterations.\n ";
103+
104+
float anLikely = 0.0f;
105+
MovingAverage avgAnomaly(1000);
106+
AnomalyLikelihood anLikelihood;
107+
108+
// RUN
109+
float x = 0.00f;
110+
for (size_t e = 0; e < EPOCHS; e++) {
111+
// genarate some data to send to the encoder
112+
113+
// -- A sine wave, one degree rotation per iteration (an alternate function)
114+
//double data = std::sin(i * (3.1415 / 180));
115+
116+
// -- sine wave, 0.01 radians per iteration (Note: first iteration is for x=0.01, not 0)
117+
x += 0.01f; // step size for fn(x)
118+
double data = std::sin(x);
119+
encoder->setParameterReal64("sensedValue", data); // feed data into RDSE encoder for this iteration.
120+
121+
// Execute an iteration.
122+
net.run(1);
123+
124+
float an = ((float *)tm->getOutputData("anomaly").getBuffer())[0];
125+
avgAnomaly.compute(an);
126+
anLikely = anLikelihood.anomalyProbability(an);
127+
128+
129+
// Save the data for plotting. <iteration>, <sin data>, <anomaly>, <likelyhood>\n
130+
if (ofs.is_open()) {
131+
ofs << e << "," << data << "," << an << "," << anLikely << std::endl;
132+
}
133+
134+
if (e == EPOCHS - 1)
135+
{
136+
137+
// output values
138+
VERBOSE << "Result after " << e + 1 << " iterations.\n";
139+
VERBOSE << " Anomaly = " << an << std::endl;
140+
VERBOSE << " Anomaly(avg) = " << avgAnomaly.getCurrentAvg() << std::endl;
141+
VERBOSE << " Anomaly(Likelihood) = " << anLikely << endl;
142+
VERBOSE << " Encoder out = " << encoder->getOutputData("encoded").getSDR();
143+
VERBOSE << " SP (global) = " << sp_global->getOutputData("bottomUpOut").getSDR();
144+
VERBOSE << " TM predictive = " << tm->getOutputData("predictiveCells").getSDR();
145+
}
146+
}
147+
if (ofs.is_open())
148+
ofs.close();
149+
150+
151+
std::cout << "finished\n";
152+
153+
154+
} catch (Exception &ex) {
155+
std::cerr << ex.what();
156+
if (ofs.is_open())
157+
ofs.close();
158+
return 1;
159+
}
160+
161+
return 0;
162+
}
163+

0 commit comments

Comments
 (0)