Skip to content
Merged
15 changes: 8 additions & 7 deletions bindings/py/cpp_src/bindings/engine/py_Engine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,16 @@ PyBind11 bindings for Engine classes
#include <pybind11/stl.h>

#include <htm/os/Timer.hpp>

#include <htm/ntypes/Array.hpp>
#include <htm/utils/Log.hpp>

#include <htm/engine/Link.hpp>
#include <htm/engine/Network.hpp>
#include <htm/engine/Region.hpp>
#include <htm/engine/Input.hpp>
#include <htm/engine/Spec.hpp>
#include <htm/types/Sdr.hpp>

#include <plugin/PyBindRegion.hpp>
#include <plugin/RegisteredRegionImplPy.hpp>

Expand Down Expand Up @@ -435,13 +436,13 @@ namespace htm_ext
, py::arg("srcOutput") = "", py::arg("destInput") = ""
, py::arg("propagationDelay") = 0);

py::enum_<LogLevel>(m, "LogLevel", py::arithmetic(), "An enumeration of logging levels.")
.value("None", LogLevel::LogLevel_None) // default
.value("Minimal", LogLevel::LogLevel_Minimal)
.value("Normal", LogLevel::LogLevel_Normal)
.value("Verbose", LogLevel::LogLevel_Verbose)
py::enum_<htm::LogLevel>(m, "LogLevel", "An enumeration of logging levels.")
.value("None", htm::LogLevel::LogLevel_None) // default
.value("Minimal", htm::LogLevel::LogLevel_Minimal)
.value("Normal", htm::LogLevel::LogLevel_Normal)
.value("Verbose", htm::LogLevel::LogLevel_Verbose)
.export_values();
py_Network.def("setLogLevel", &htm::Network::setLogLevel);
py_Network.def_static("setLogLevel", &htm::Network::setLogLevel, py::arg("level") = htm::LogLevel::LogLevel_None);


// plugin registration
Expand Down
8 changes: 5 additions & 3 deletions bindings/py/tests/regions/network_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -278,10 +278,10 @@ def testBuiltInRegions(self):
"""
This sets up a network with built-in regions.
"""

import htm
net = engine.Network()
net.setLogLevel(engine.Verbose) # Verbose shows data inputs and outputs while executing.
#net.setLogLevel(htm.bindings.engine_internal.LogLevel.Verbose) # Verbose shows data inputs and outputs while executing.

encoder = net.addRegion("encoder", "ScalarSensor", "{n: 6, w: 2}");
sp = net.addRegion("sp", "SPRegion", "{columnCount: 200}");
tm = net.addRegion("tm", "TMRegion", "");
Expand All @@ -302,6 +302,8 @@ def testBuiltInRegions(self):

tm_output = tm.getOutputArray("predictedActiveCells")
sdr = tm_output.getSDR()
print(sdr.sparse)
print(EXPECTED_RESULT3)
self.assertTrue(np.array_equal(sdr.sparse, EXPECTED_RESULT3))

def testExecuteCommand1(self):
Expand Down
4 changes: 0 additions & 4 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -167,10 +167,6 @@ set(types_files
set(utils_files
htm/utils/GroupBy.hpp
htm/utils/Log.hpp
htm/utils/LoggingException.cpp
htm/utils/LoggingException.hpp
htm/utils/LogItem.cpp
htm/utils/LogItem.hpp
htm/utils/MovingAverage.cpp
htm/utils/MovingAverage.hpp
htm/utils/Random.cpp
Expand Down
2 changes: 1 addition & 1 deletion src/htm/engine/Link.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
#include <htm/ntypes/BasicType.hpp>
#include <htm/utils/Log.hpp>

// By calling LogItem::setLogLevel(LogLevel_Verbose)
// By calling NTA_LOG_LEVEL = LogLevel::LogLevel_Verbose
// you can enable the NTA_DEBUG macros below.

namespace htm {
Expand Down
10 changes: 5 additions & 5 deletions src/htm/engine/Network.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -381,12 +381,12 @@ class Link;
*/
void resetProfiling();

/**
* Set one of the debug levels: LogLevel_None = 0, LogLevel_Minimal, LogLevel_Normal, LogLevel_Verbose
/**
* Set one of the debug levels: LogLevel_None = 0, LogLevel_Minimal, LogLevel_Normal, LogLevel_Verbose
*/
void setLogLevel(LogLevel level) {
LogItem::setLogLevel(level);
}
static void setLogLevel(LogLevel level) {
NTA_LOG_LEVEL = level;
}


/**
Expand Down
26 changes: 20 additions & 6 deletions src/htm/regions/TMRegion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,16 @@ void TMRegion::initialize() {
args_.sequencePos = 0;
}


bool TMRegion::isConnected_(string name) const {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We talked about always generating all outputs, even if no connections are made. So this new function would not be needed.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

about always generating all outputs, even if no connections are made

I'm having second thoughts about this. I've implemented that in TMRegion and it works fine! (same should then be done for SPRegion).
What we should consider is the tradeoff between ease of use for the user/speed. We're computing outputs that may not be needed (and I'm not sure of it's costly enough to care).

The proble is only with the leaf node (here TM, and not SP) which has no outgoing links. Alternative approach would be a dummy OutputRegion that the user would add after the TM to specify which outputs are linked (=used).

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What we should consider is the tradeoff between ease of use for the user/speed.

I agree with you. We should not be computing the outputs if they are not used if we can help it.

The problem is when an app gets the input or output buffers directly with region.getOutputData( ) or region.setInputData( ). These could be used on any buffer at any time without the region impl being involved.

Originally, these two functions were not exposed to apps. The apps had to call region.getArrayParameter(name) to access the 'optional' data and in those handlers the region impl had the chance to generate the buffer before returning it.

Perhaps we could add a hook in getOutputData( ) that allowed a region impl to do something before it returned the buffer. That adds yet another complication to the region impl's, but only for those that have optional buffers.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That adds yet another complication to the region impl's, but only for those that have optional buffers.

the implementation seems already quite complex to me.

  • is there a way in the Spec to say "I will be using this optional output"?
  • or we just KISS it and compute it all for now, unless someone complains on Regions' performance.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there a way in the Spec to say "I will be using this optional output"?

No, the Spec is static and describes what the region will do, not how it is used.

or we just KISS it and compute it all for now, unless someone complains on Regions' performance.

I guess so. We should keep thinking about how we might manage this however.

const auto in = getInput(name);
const auto out = getOutput(name);
const bool hasOutput = (out != nullptr) and out->hasOutgoingLinks();
const bool hasInput = (in != nullptr) and in->hasIncomingLinks();
return hasInput or hasOutput;
}


void TMRegion::compute() {

NTA_ASSERT(tm_) << "TM not initialized";
Expand Down Expand Up @@ -230,7 +240,11 @@ void TMRegion::compute() {
//
std::shared_ptr<Output> out;
out = getOutput("bottomUpOut");
if (out && (out->hasOutgoingLinks() || LogItem::isDebug())) {
//set
NTA_LOG_LEVEL = htm::LogLevel::LogLevel_Verbose;
NTA_CHECK(NTA_LOG_LEVEL == LogLevel::LogLevel_Verbose) << "setting Verbose failed, man";
//to output the NTA_DEBUG statements below
if (isConnected_("bottomUpOut") ) {
SDR& sdr = out->getData().getSDR();
tm_->getActiveCells(sdr); //active cells
if (args_.orColumnOutputs) { //output as columns
Expand All @@ -239,24 +253,24 @@ void TMRegion::compute() {
NTA_DEBUG << "bottomUpOut " << *out << std::endl;
}
out = getOutput("activeCells");
if (out && (out->hasOutgoingLinks() || LogItem::isDebug())) {
if (isConnected_("activeCells")) {
tm_->getActiveCells(out->getData().getSDR());
NTA_DEBUG << "active " << *out << std::endl;
}
out = getOutput("predictedActiveCells");
if (out && (out->hasOutgoingLinks() || LogItem::isDebug())) {
if (isConnected_("predictedActiveCells") ) {
tm_->activateDendrites();
tm_->getWinnerCells(out->getData().getSDR());
NTA_DEBUG << "winners " << *out << std::endl;
}
out = getOutput("anomaly");
if (out && (out->hasOutgoingLinks() || LogItem::isDebug())) {
if (isConnected_("anomaly") ) {
Real32* buffer = reinterpret_cast<Real32*>(out->getData().getBuffer());
buffer[0] = tm_->anomaly;
buffer[0] = tm_->anomaly; //only the first field is valid
NTA_DEBUG << "anomaly " << *out << std::endl;
}
out = getOutput("predictiveCells");
if (out && (out->hasOutgoingLinks() || LogItem::isDebug())) {
if (isConnected_("predictiveCells") ) {
out->getData().getSDR() = tm_->getPredictiveCells();
NTA_DEBUG << "predictive " << *out << std::endl;
}
Expand Down
2 changes: 1 addition & 1 deletion src/htm/regions/TMRegion.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ class TMRegion : public RegionImpl, Serializable {
Size iter;
} args_;


bool isConnected_(std::string name) const;

computeCallbackFunc computeCallback_;
std::unique_ptr<TemporalMemory> tm_;
Expand Down
58 changes: 32 additions & 26 deletions src/htm/types/Exception.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@
#define NTA_EXCEPTION_HPP

#include <htm/types/Types.hpp>

#include <stdexcept>
#include <string>
#include <sstream>
#include <utility>

//----------------------------------------------------------------------
Expand All @@ -47,22 +49,10 @@ namespace htm {
* This class may be used directly by instatiating an instance
* and throwing it, but usually you will use the NTA_THROW macro
* that makes it much simpler by automatically retreiving the __FILE__
* and __LINE__ for you and also using a wrapping LogItem that allows
* you to construct the exception message conveniently using the <<
* streame operator (see htm/utils/Log.hpp for further details).
*
* @b Notes:
* 1. Exception is a subclass of the standard std::runtime_error.
* This is useful if your code needs to interoperate with other
* code that is not aware of the Exception class, but understands
* std::runtime_error. The what() method will return the exception message
* and the location information will not be avaialable to such code.
* and __LINE__ for you.
*
* 2. Source file and line number information is useful of course
* only if you have access to the source code. It is not recommended
* to display this information to users most of the time.
*/
class Exception : public std::runtime_error {
class Exception : public std::runtime_error { //TODO rename to NTAException to be less confusing?
public:
/**
* Constructor
Expand All @@ -77,10 +67,18 @@ class Exception : public std::runtime_error {
*
* @param message [const std::string &] the description of exception
*/
Exception(std::string filename, UInt32 lineno, std::string message,
Exception(std::string filename,
UInt32 lineno,
std::string message = "",
std::string stacktrace = "")
: std::runtime_error(""), filename_(std::move(filename)), lineno_(lineno),
message_(std::move(message)), stackTrace_(std::move(stacktrace)) {}
message_(std::move(message)), stackTrace_(std::move(stacktrace)),ss_("") {}

Exception(const Exception& copy):
std::runtime_error("") {
Exception(filename_, lineno_, copy.getMessage(), stackTrace_);
}


/**
* Destructor
Expand All @@ -104,12 +102,8 @@ class Exception : public std::runtime_error {
*
* @retval [const Byte *] the exception message
*/
virtual const char *what() const throw() {
try {
virtual const char *what() const noexcept override {
return getMessage();
} catch (...) {
return "Exception caught in non-throwing Exception::what()";
}
}

/**
Expand All @@ -120,7 +114,7 @@ class Exception : public std::runtime_error {
*
* @retval [const Byte *] the source filename
*/
const char *getFilename() const { return filename_.c_str(); }
const char *getFilename() const noexcept { return filename_.c_str(); }

/**
* Get the line number in the source file
Expand All @@ -130,14 +124,17 @@ class Exception : public std::runtime_error {
*
* @retval [UInt32] the line number in the source file
*/
UInt32 getLineNumber() const { return lineno_; }
UInt32 getLineNumber() const noexcept { return lineno_; }

/**
* Get the error message
*
* @retval [const char *] the error message
*/
virtual const char *getMessage() const { return message_.c_str(); }
virtual const char *getMessage() const noexcept {
message_ += ss_.str();
ss_.clear();
return message_.c_str(); }

/**
* Get the stack trace
Expand All @@ -147,13 +144,22 @@ class Exception : public std::runtime_error {
*
* @retval [const Byte *] the stack trace
*/
virtual const char *getStackTrace() const { return stackTrace_.c_str(); }
virtual const char *getStackTrace() const noexcept { return stackTrace_.c_str(); }


template <typename T>
Exception &operator<<(const T &obj) {
ss_ << obj;
return *this;
}

protected:
std::string filename_;
UInt32 lineno_;
std::string message_;
mutable std::string message_; //mutable bcs modified in getMessage which is used in what() but that needs be const
std::string stackTrace_;
private:
mutable std::stringstream ss_;

}; // end class Exception
} // end namespace htm
Expand Down
59 changes: 28 additions & 31 deletions src/htm/utils/Log.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,65 +23,62 @@
#ifndef NTA_LOG2_HPP
#define NTA_LOG2_HPP

#include <htm/utils/LogItem.hpp>
#include <htm/utils/LoggingException.hpp>
#include <iostream>
#include <htm/types/Exception.hpp>

#define NTA_DEBUG \
if (htm::LogItem::getLogLevel() < htm::LogLevel_Verbose) { \
} else \
htm::LogItem(__FILE__, __LINE__, htm::LogType_debug).stream()
namespace htm {
enum class LogLevel { LogLevel_None = 0, LogLevel_Minimal=1, LogLevel_Normal=2, LogLevel_Verbose=3 };
static LogLevel NTA_LOG_LEVEL = LogLevel::LogLevel_Minimal; // change this in your class to set log level

// Can be used in Loggable classes
// level is one of (LogLevel_None, LogLevel_Minimal, LogLevel_Normal, LogLevel_Verbose)
#define NTA_LDEBUG(level) \
if (htm::LogItem::getLogLevel() < (level)) { \
} else \
htm::LogItem(__FILE__, __LINE__, htm::LogType_debug).stream()
//this code intentionally uses "if() dosomething" instead of "if() { dosomething }"
// as the macro expects another "<< "my clever message";
// so it eventually becomes: `if() std::cout << "DEBUG:\t" << "users message";`
//
//Expected usage:
//<your class>:
//NTA_LOG_LEVEL = LogLevel::LogLevel_Normal;
//NTA_WARN << "Hello World!" << std::endl; //shows
//NTA_DEBUG << "more details how cool this is"; //not showing under "Normal" log level
//NTA_ERR << "You'll always see this, HAHA!";
//NTA_THROW << "crashing for a good cause";

#define NTA_DEBUG \
if (NTA_LOG_LEVEL >= LogLevel::LogLevel_Verbose ) std::cout << "DEBUG:\t" << __FILE__ << ":" << __LINE__ << ":"

// For informational messages that report status but do not indicate that
// anything is wrong
#define NTA_INFO \
if (htm::LogItem::getLogLevel() < htm::LogLevel_Normal) { \
} else \
htm::LogItem(__FILE__, __LINE__, htm::LogType_info).stream()
if (NTA_LOG_LEVEL >= LogLevel::LogLevel_Normal ) std::cout << "INFO:\t" << __FILE__ << ":" << __LINE__ << ":"

// For messages that indicate a recoverable error or something else that it may
// be important for the end user to know about.
#define NTA_WARN \
if (htm::LogItem::getLogLevel() < htm::LogLevel_Normal) { \
} else \
htm::LogItem(__FILE__, __LINE__, htm::LogType_warn).stream()
if (NTA_LOG_LEVEL >= LogLevel::LogLevel_Normal ) std::cout << "WARN:\t" << __FILE__ << ":" << __LINE__ << ":"

// To throw an exception and make sure the exception message is logged
// appropriately
#define NTA_THROW throw htm::LoggingException(__FILE__, __LINE__)
#define NTA_THROW throw htm::Exception(__FILE__, __LINE__)

// The difference between CHECK and ASSERT is that ASSERT is for
// performance critical code and can be disabled in a release
// build. Both throw an exception on error (if NTA_ASSERTIONS_ON is set).

#define NTA_CHECK(condition) \
if (condition) { \
} else \
if (not (condition) ) \
NTA_THROW << "CHECK FAILED: \"" << #condition << "\" "

#ifdef NTA_ASSERTIONS_ON
// With NTA_ASSERTIONS_ON, NTA_ASSERT macro throws exception if condition is false.
// NTA_ASSERTIONS_ON should be set ON only in debug mode.
#define NTA_ASSERT(condition) \
if (condition) { \
} else \
NTA_THROW << "ASSERTION FAILED: \"" << #condition << "\" "
#define NTA_ASSERT(condition) NTA_CHECK(condition)

#else
// Without NTA_ASSERTIONS_ON, NTA_ASSERT macro does nothing.
// The second line (with LogItem) should never be executed, or even compiled, but we
// need something that is syntactically compatible with NTA_ASSERT
// The second line (with `if(false)`) should never be executed, or even compiled, but we
// need something that is syntactically compatible with NTA_ASSERT << "msg";
#define NTA_ASSERT(condition) \
if (1) { \
} else \
htm::LogItem(__FILE__, __LINE__, htm::LogType_debug).stream()
if (false) std::cerr << "This line should never happen"

#endif // NTA_ASSERTIONS_ON

}
#endif // NTA_LOG2_HPP
Loading