Skip to content

Commit 46ca514

Browse files
authored
Merge pull request #956 from htm-community/fix_phases
New Feature: phases can be specified with addRegion() and run().
2 parents 7e10615 + 9ad20aa commit 46ca514

File tree

19 files changed

+1363
-410
lines changed

19 files changed

+1363
-410
lines changed

API_CHANGELOG.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,14 @@ This is obsolete. Use getRegion('name') instead.
121121
| VectorFileEffector | FileOutputRegion |
122122
| VectorFileSensor | FileInputRegion |
123123

124+
* Timing for data move PR# 956
125+
Originally data is moved from output to input through the links just before executing a region.
126+
Now, the data is moved just after execution of a region. The effect is that the internal values
127+
of the Input buffers between iterations was the value just used in the previous execute. It is
128+
now the input buffer contains the value to be used by the next execution.
129+
130+
There should be no effect on applications unless they are looking at the values of the internal
131+
input buffers.
124132

125133
## Python API Changes
126134

@@ -147,4 +155,5 @@ mostly just a thin wrapper around the C++ library.
147155
- Classifier::learn(SDR, label)
148156
The `label` argument can now be an unsigned integer (the label index) or it can be a vector containing a set of label indexes that relate to this pattern.
149157
This was done because syntax such as `{4}` passed as the label, intended to create a vector with one element, is now being rejected by at least one compiler.
150-
So, just pass the label index directly if there is only one.
158+
So, just pass the label index directly if there is only one.
159+

TestOutputDir/TestOutput.csv

Lines changed: 0 additions & 1 deletion
This file was deleted.
-1.22 KB
Binary file not shown.

bindings/py/cpp_src/bindings/engine/py_Engine.cpp

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -412,7 +412,18 @@ namespace htm_ext
412412

413413
py_Network.def("addRegion", (Region_Ptr_t (htm::Network::*)(
414414
const std::string&,
415-
const std::string&,
415+
const std::string&,
416+
const std::string&,
417+
const std::set<UInt32> &phases))
418+
&htm::Network::addRegion,
419+
"Normal add region."
420+
, py::arg("name")
421+
, py::arg("nodeType" )
422+
, py::arg("nodeParams")
423+
, py::arg("phases"));
424+
py_Network.def("addRegion", (Region_Ptr_t (htm::Network::*)(
425+
const std::string&,
426+
const std::string&,
416427
const std::string&))
417428
&htm::Network::addRegion,
418429
"Normal add region."
@@ -438,7 +449,15 @@ namespace htm_ext
438449
.def("getMinEnabledPhase", &htm::Network::getMinPhase)
439450
.def("getMaxEnabledPhase", &htm::Network::getMaxPhase)
440451
.def("setPhases", &htm::Network::setPhases)
441-
.def("run", &htm::Network::run);
452+
.def("getExecutionMap", &htm::Network::getExecutionMap);
453+
454+
py_Network.def("run", (void (htm::Network::*)(int n)) &htm::Network::run
455+
, "Executes all phases n times."
456+
, py::arg("n"));
457+
py_Network.def("run", (void (htm::Network::*)(int n, std::vector<UInt32> phases)) &htm::Network::run
458+
, "Execute a set of phases in the given order, repeating n times."
459+
, py::arg("n"), py::arg("phases"));
460+
442461

443462
py_Network.def("initialize", &htm::Network::initialize);
444463

docs/NetworkAPI_Engine.md

Lines changed: 376 additions & 12 deletions
Large diffs are not rendered by default.

docs/NetworkAPI_Links.md

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
Network API is all about experimenting and building apps from the HTM building blocks (or regions) which are wrappers around the htm algorithms. See [Network API Regions](NetworkAPI_Regions.md). But to be useful, we also need to be able to connect up those building blocks so that data can flow between them. We do that with links.
44

55
## Data Flow
6-
A link is a data path with buffers on both ends. The buffer on the source end of the link are assiciated with an output of a region. The buffer on the destination end of the link is associated with an input to a region. On each iteration of a run, each region is executed. As a region is executed, it will first move the data in the buffer on the source side to the buffer on the destination side of any links connected to its inputs. Then it will call the underlining algorithm to generated some output. That output is moved to the output buffer.
6+
A link is a data path with buffers on both ends. The buffer on the source end of the link are assiciated with an output of a region. The buffer on the destination end of the link is associated with an input to a region.
7+
8+
When a region is executed, it will call the underlining algorithm using data from the input buffer to generated some output. That output is moved to the output buffer. After each region is executed its outputs are propogated through all links assigned to that output.
79

810
Here is an example of a NetworkAPI configuration using JSON syntax:
911
```
@@ -14,18 +16,18 @@ Here is an example of a NetworkAPI configuration using JSON syntax:
1416
{addRegion: {name: "fileOutput", type "FileOutputRegion", params: {outputFile: "result.csv"}
1517
{addLink: {src: "encoder.encoded", dest: "sp.bottomUpIn"}},
1618
{addLink: {src: "sp.bottomUpOut", dest: "tm.bottomUpIn"}}
17-
{addLink: {src: "tm.bottomUpOut", dest: "fileOutput.DataOut"}}
19+
{addLink: {src: "tm.bottomUpOut", dest: "fileOutput.DataIn"}}
1820
]})";
1921
```
2022
The app, in a loop, would set a value in the parameter "sensedValue" on the encoder then call `run(1)`.
2123

2224
As you can see in this diagram, on a single iteration of a run, data will flow through the blocks from one to another.
2325
![Example Layout](./images/DocsImage1.JPG)
24-
The regions are executed in the order that they are declared in the configuration. Here is basicly what happens:
25-
- The encoder has no inputs so no data is moved. The encoder is executed and using the current value of the parameter SensedValue, produces data in the "encoded" output of the encoder.
26-
- The sp region has a link to an input so the encoder's 'encoded' output buffer is moved to the buffer of the 'bottomUpIn' input of the SP as specified in the link. The SP is executed with that input and produces an output in sp.bottomUpOut.
27-
- The tm region has a link so the buffer in sp.bottomUpOut is moved to tm.bottomUpIn. TM is executed and produces data on several outputs.
28-
- The FileOutput region has a link so the buffer in tm.bottomUpOut is moved to fileOutput.DataOut. Then the FileOutput region is executed and saves the data into the file.
26+
The regions are executed in the order that they are declared in the configuration (modified by the phase into which they may be placed). Here is basicly what happens:
27+
- The encoder has no links connected to its inputs. The encoder is executed and using the current value of the parameter SensedValue, produces data in the "encoded" output of the encoder. That data is then distributed along the output link to connected input buffer named "bottomUpIn" of the sp region.
28+
- The SP is executed with that input and produces an output in sp.bottomUpOut. The sp region has a link connected to its output so the buffer in sp.bottomUpOut is moved to tm.bottomUpIn.
29+
- TM is executed and produces data on several outputs but only one has a link. The FileOutput region is connected from TM with a link so the buffer in tm.bottomUpOut is moved to fileOutput.DataOut.
30+
- Then the FileOutput region is executed and saves the data in its input buffer fileOutput.DataIn, into the file.
2931

3032
So the data cascades through the links.
3133

@@ -39,13 +41,14 @@ The syntax for a Link declaration using JSON format:
3941
addLink: {src: "<srcName>.<srcOutput>",
4042
dest: "<destName>.<destInput>",
4143
dim: [<dimensions>],
44+
mode: [<"overwrite" or "<fanin>"]
4245
delay: <propogationDelay>}
4346
}
4447
```
4548

4649
The syntax for a Link declaration using C++ calls:
4750
```
48-
link(<srcName>, <destName>, "", "{dim: [<dimensions>]}", <srcOutput>, <destInput>, <propogationDelay>);
51+
link(<srcName>, <destName>, "", "{dim: [<dimensions>], mode: <"overwrite" or "fanin">}", <srcOutput>, <destInput>, <propogationDelay>);
4952
```
5053

5154
The syntax for a Link declaration using Python calls:
@@ -56,6 +59,8 @@ link(srcName, destName, LinkType, LinkParams, srcOutput, destInput, propogationD
5659

5760
- The optional `dim` parameter is only required for the special case of the "INPUT" source discussed later.
5861

62+
- The optional `mode` parameter is only needed if there are more than one link connected to the destination buffer. A value of "overwrite" means the outputs from each link will overwrite the entire input buffer. The last output to execute wins. A value of "fanin" means put the outputs from each link into separate portions of the input buffer. See more about the Fan-In condition below. If not given, the mode is "fanin".
63+
5964
- The `srcOutput` and `destInput` are the targets of the link on their respecitive regions. For backward compatability, if either is not given or blank (not recomended), they are the ones identified as the default for the region in the Spec.
6065

6166
- The optional `delay` argument is propogation delay parameter to set the number of iterations to delay the buffer movements.
@@ -89,15 +94,15 @@ For example, if the source buffer consist of a C-type array of Real32 values and
8994
## Fan-In
9095
There may be times when more than one region output should be connected to a single input of a region. To implement, configure a link for each output and indicate the same target input.
9196

92-
When this occurs, the data from each source is converted to the type of the destination and then their buffers are concatinated. So, the input buffer size will be the sum of the widths of all of the source buffers.
97+
If the `overwrite` mode was not specified, the data from each source is converted to the type of the destination and then their buffers are concatinated. So, the input buffer size will be the sum of the widths of all of the source buffers.
9398

9499
This is convenent for the construction of an app that employs multiple encoders to encode independent variables. It is the concatination of the encoder outputs that should be presented to the SP to turn this into a true SDR.
95100

96101
## Fan-Out
97-
There may be times that a single source output will be connected to multiple destination inputs. To implement, use one link per input and reference the same source output. The data is moved to a destination input when that destination region is executed.
102+
There may be times that a single source output will be connected to multiple destination inputs. To implement, use one link per input and reference the same source output. The data is moved to a destination input when that source region is executed.
98103

99104
## Propogation Delay
100-
The link also has the feature of being able to delay the propogation of an output buffer by a number of run iterations. This is configured by setting the link property `propogationDelay` to the number of run iterations to delay. Normally this is 0 and there is no delay. The propogationDelay queue is rotated at the beginning of the run so the source being propogated to the input is the buffer at the top of the queue.
105+
The link also has the feature of being able to delay the propogation of an output buffer by a number of run iterations. This is configured by setting the link property `propogationDelay` to the number of run iterations to delay. Normally this is 0 and there is no delay. The propogationDelay queue is rotated after an output is generated. The value in the output buffer is placed into the bottom of the queue and the value being propogated to the input is the buffer at the top of the queue.
101106

102107
For example. If the propogationDelay is set to 2 then the propogation will be something like this:
103108
```
@@ -157,12 +162,17 @@ Sometimes we want the app to provide the data that will be in the input buffer o
157162

158163
The source_name argument is an identifier of your choice that matches the link which will be used. This allows the app to provide multiple streams of data, each with their own source_name and their own corresponding link.
159164

160-
Since the link will not be able to infer the dimensions on this source, this link declaration must include the dimsions of the data that the app will be providing. This dimension will be available for the link to infer the dimensions of the input which is the target of the link.
165+
Since the link will not be able to infer the dimensions on this source, this link declaration must include the dimensions of the data that the app will be providing. This dimension will be available for the link to infer the dimensions of the input which is the target of the link. For example, use `"{dim: [<dimensions>]}"` as the link parameter.
161166

162-
Here is an example that might show up in a JSON configuration for NetworkAPI. Our `<source_name>` field is `source1` in this case and a call to setInputData( ... ) to feed data to this link must use `source1` as the first argument.
167+
Here is an example that might show up in a JSON configuration for NetworkAPI. Our `<source_name>` field is `source1` in this case and a call to setInputData( ... ) to feed data to this link must use `source1` as the first argument.
168+
If you are defining the links directly use:
169+
```
170+
network.link("INPUT", "sp", "", "{dim: [100]}", "source1", "bottomUpIn");
171+
```
172+
If you are defining the links in a configuration string to be used with network.config() then the link portions would include:
163173
```
164174
{addLink: {src: "INPUT.source1", dest: "sp.bottomUpIn", dim: [100]}},
165175
```
166176

167-
Note that all of the Link features such as Fan-In, Fan-Out, propogation delay, and type conversion apply to this special link.
177+
Note that all of the Link features such as Fan-In, Fan-Out, mode, propogation delay, and type conversion apply to this special link.
168178

py/tests/networkapi/run_test.py

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
# ----------------------------------------------------------------------
2+
# HTM Community Edition of NuPIC
3+
# Copyright (C) 2020, Numenta, Inc.
4+
#
5+
# author: David Keeney, [email protected], Aug 2021
6+
#
7+
# This program is free software: you can redistribute it and/or modify
8+
# it under the terms of the GNU Affero Public License version 3 as
9+
# published by the Free Software Foundation.
10+
#
11+
# This program is distributed in the hope that it will be useful,
12+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14+
# See the GNU Affero Public License for more details.
15+
#
16+
# You should have received a copy of the GNU Affero Public License
17+
# along with this program. If not, see http://www.gnu.org/licenses.
18+
# ----------------------------------------------------------------------
19+
20+
"""
21+
Unit tests for using NetworkAPI with python.
22+
"""
23+
24+
import datetime
25+
import numpy as np
26+
import math
27+
import unittest
28+
import json
29+
30+
from htm.bindings.sdr import SDR
31+
from htm.bindings.engine_internal import Network,Region
32+
33+
34+
class NetworkAPI_Run_Test(unittest.TestCase):
35+
""" Unit tests for Network class. """
36+
37+
def testRun(self):
38+
"""
39+
A simple NetworkAPI example with three regions.
40+
///////////////////////////////////////////////////////////////
41+
//
42+
// .------------------.
43+
// | encoder |
44+
// |(RDSEEncoderRegion)|<------ inital input
45+
// | | (INPUT.begin)
46+
// `-------------------'
47+
// | --------------. sp.bottomUpIn
48+
// |/ |
49+
// .-----------------. |
50+
// | sp | |
51+
// | (SPRegion) | |
52+
// | | |
53+
// `-----------------' |
54+
// | ^
55+
// .-----------------. |
56+
// | tm | |
57+
// | (TMRegion) |-----' (tm.bottomUpOut)
58+
// | |
59+
// `-----------------'
60+
//
61+
//////////////////////////////////////////////////////////////////
62+
"""
63+
64+
""" Creating network instance. """
65+
config = """
66+
{network: [
67+
{addRegion: {name: "encoder", type: "RDSEEncoderRegion", params: {size: 1000, sparsity: 0.2, radius: 0.03, seed: 2019, noise: 0.01}, phase: [1]}},
68+
{addRegion: {name: "sp", type: "SPRegion", params: {columnCount: 1000, globalInhibition: true}, phase: [2]}},
69+
{addRegion: {name: "tm", type: "TMRegion", params: {cellsPerColumn: 8, orColumnOutputs: true}, phase: [2]}},
70+
{addLink: {src: "INPUT.begin", dest: "encoder.values", dim: [1]}},
71+
{addLink: {src: "encoder.encoded", dest: "sp.bottomUpIn", mode: "overwrite"}},
72+
{addLink: {src: "sp.bottomUpOut", dest: "tm.bottomUpIn"}},
73+
{addLink: {src: "tm.bottomUpOut", dest: "sp.bottomUpIn"}}
74+
]}"""
75+
net = Network()
76+
net.configure(config)
77+
78+
net.initialize()
79+
# for debugging: print(net.getExecutionMap())
80+
81+
""" Force initial data. """
82+
net.setInputData("begin", np.array([10]))
83+
84+
""" Execute encoder once, the loop (sp and tm) 4 times """
85+
net.run(1, [1]) # execute initial entry (phase 1)
86+
net.run(4, [2]) # execute loop 4 times (phase 2)
87+
88+
# Here is how you access the buffers
89+
sp_input_buffer = np.array(net.getRegion('sp').getInputArray('bottomUpIn'))
90+
tn_output_buffer = np.array(net.getRegion('tm').getOutputArray('bottomUpOut'))
91+
self.assertEqual(sp_input_buffer.size, tn_output_buffer.size)
92+
93+
94+
95+
96+
97+
98+
if __name__ == "__main__":
99+
unittest.main()

0 commit comments

Comments
 (0)