From f33ea2474adc193008adbeddd7f50b53ffcf43c5 Mon Sep 17 00:00:00 2001 From: Konstantin Sidorov Date: Tue, 4 Jul 2017 11:41:06 +0300 Subject: [PATCH] Put TreeMemory to the working condition + tests for it --- .../methods/ann/augmented/CMakeLists.txt | 5 +- .../{tree_memory.cpp => tree_memory.hpp} | 8 +- .../ann/augmented/tree_memory_impl.cpp | 83 ------------- .../ann/augmented/tree_memory_impl.hpp | 113 ++++++++++++++++++ .../tests/augmented_rnns_tasks_test.cpp | 86 +++++++++++++ 5 files changed, 207 insertions(+), 88 deletions(-) rename src/mlpack/methods/ann/augmented/{tree_memory.cpp => tree_memory.hpp} (88%) delete mode 100644 src/mlpack/methods/ann/augmented/tree_memory_impl.cpp create mode 100644 src/mlpack/methods/ann/augmented/tree_memory_impl.hpp diff --git a/src/mlpack/methods/ann/augmented/CMakeLists.txt b/src/mlpack/methods/ann/augmented/CMakeLists.txt index 0356a522ac..3c58af40d9 100644 --- a/src/mlpack/methods/ann/augmented/CMakeLists.txt +++ b/src/mlpack/methods/ann/augmented/CMakeLists.txt @@ -1,6 +1,9 @@ # Define the files we need to compile # Anything not in this list will not be compiled into mlpack. -set(SOURCES) +set(SOURCES + tree_memory.hpp + tree_memory_impl.hpp +) # Add directory name to sources. set(DIR_SRCS) diff --git a/src/mlpack/methods/ann/augmented/tree_memory.cpp b/src/mlpack/methods/ann/augmented/tree_memory.hpp similarity index 88% rename from src/mlpack/methods/ann/augmented/tree_memory.cpp rename to src/mlpack/methods/ann/augmented/tree_memory.hpp index 965a648cd8..9615dad260 100644 --- a/src/mlpack/methods/ann/augmented/tree_memory.cpp +++ b/src/mlpack/methods/ann/augmented/tree_memory.hpp @@ -15,8 +15,6 @@ #include -using std::vector; - namespace mlpack { namespace ann /* Artificial Neural Network */ { namespace augmented /* Augmented neural network */ { @@ -25,18 +23,20 @@ class TreeMemory { public: TreeMemory(size_t size, J joiner, W writer); - void Initialize(vector& leafValues); + void Initialize(std::vector& leafValues); void Update(size_t pos, T el); T Get(size_t index); + T GetCell(size_t memIndex); inline size_t Root(); inline size_t Left(size_t origin); inline size_t Right(size_t origin); inline size_t Parent(size_t child); + inline size_t LeafIndex(size_t leafPos); private: - vector memory; + std::vector memory; J joinFunction; W writeFunction; size_t memorySize; diff --git a/src/mlpack/methods/ann/augmented/tree_memory_impl.cpp b/src/mlpack/methods/ann/augmented/tree_memory_impl.cpp deleted file mode 100644 index 7d54b82ca6..0000000000 --- a/src/mlpack/methods/ann/augmented/tree_memory_impl.cpp +++ /dev/null @@ -1,83 +0,0 @@ -/** - * @file tree_memory_impl.hpp - * @author Konstantin Sidorov - * - * Implementation of CopyTask class - * - * mlpack is free software; you may redistribute it and/or modify it under the - * terms of the 3-clause BSD license. You should have received a copy of the - * 3-clause BSD license along with mlpack. If not, see - * http://www.opensource.org/licenses/BSD-3-Clause for more information. - */ -#ifndef MLPACK_METHODS_AUGMENTED_TREE_MEMORY_IMPL_HPP -#define MLPACK_METHODS_AUGMENTED_TREE_MEMORY_IMPL_HPP - -#include - -#include "tree_memory.hpp" - -TreeMemory::TreeMemory(size_t size, J joiner, W writer) { - memorySize = size; - // Rounding size to the next highest power of 2. - assert(0 < size && size < static_cast(1 << 31)); - actualMemorySize = ceil(log2((double) size)) - assert(memorySize <= actualMemorySize); - // Allocating enough memory to store all leaf values AND inner node values. - memory.resize(2 * actualMemorySize - 1); - joinFunction = joiner; - writeFunction = writer; -} - -inline T TreeMemory::Get(size_t index) { - assert(0 <= index && index < memorySize); - return memory[actualMemorySize - 1 + index]; -} - -inline size_t TreeMemory::Root() { - return 0; -} - -inline size_t TreeMemory::Left(size_t origin) { - return res = (origin << 1) + 1; -} - -inline size_t TreeMemory::Right(size_t origin) { - return res = (origin << 1) + 2; -} - -inline size_t TreeMemory::Parent(size_t child) { - if (child == 0) return actualMemorySize; - return ((child + 1) >> 1) - 1; -} - -void TreeMemory::Initialize(vector& leafValues) { - assert(leafValues.size() <= memorySize); - // First, write in the leaf nodes. - for (size_t i = 0; i < leafValues.size(); ++i) { - memory[actualMemorySize - 1 + i] = leafValues[i]; - } - size_t lastWrittenIdx = actualMemorySize - 1 + leafValues.size() - 1; - // After that, write into inner nodes as prescribed by writeFunction. - for (size_t i = actualMemorySize - 2; ; --i) { - size_t l = Left(i), r = Right(i); - assert(l <= r); - if (r > lastWrittenIdx) continue; - memory[i] = joinFunction(memory[l], memory[r]); - - if (i == Root()) break; - } -} - -void TreeMemory::Update(size_t pos, T el) { - assert(pos >= 0 && pos < memorySize); - size_t start = actualMemorySize - 1 + pos; - memory[start] = writeFunction(memory[start], el); - while (true) { - start = Parent(start); - size_t l = Left(start), r = Right(start); - memory[start] = joinFunction(memory[l], memory[r]); - if (start == Root()) break; - } -} - -#endif \ No newline at end of file diff --git a/src/mlpack/methods/ann/augmented/tree_memory_impl.hpp b/src/mlpack/methods/ann/augmented/tree_memory_impl.hpp new file mode 100644 index 0000000000..20dadbef61 --- /dev/null +++ b/src/mlpack/methods/ann/augmented/tree_memory_impl.hpp @@ -0,0 +1,113 @@ +/** + * @file tree_memory_impl.hpp + * @author Konstantin Sidorov + * + * Implementation of CopyTask class + * + * mlpack is free software; you may redistribute it and/or modify it under the + * terms of the 3-clause BSD license. You should have received a copy of the + * 3-clause BSD license along with mlpack. If not, see + * http://www.opensource.org/licenses/BSD-3-Clause for more information. + */ +#ifndef MLPACK_METHODS_AUGMENTED_TREE_MEMORY_IMPL_HPP +#define MLPACK_METHODS_AUGMENTED_TREE_MEMORY_IMPL_HPP + +#include + +#include "tree_memory.hpp" + +namespace mlpack { +namespace ann /* Artificial Neural Network */ { +namespace augmented /* Augmented neural network */ { +template +TreeMemory::TreeMemory(size_t size, J joiner, W writer) { + memorySize = size; + // Rounding size to the next highest power of 2. + assert(0 < size && size < static_cast(1 << 31)); + actualMemorySize = 1 << static_cast(ceil(log2((double) size))); + assert(memorySize <= actualMemorySize); + // Allocating enough memory to store all leaf values AND inner node values. + memory.resize(2 * actualMemorySize - 1); + joinFunction = joiner; + writeFunction = writer; +} + +template +inline T TreeMemory::Get(size_t index) { + assert(0 <= index && index < memorySize); + return memory[actualMemorySize - 1 + index]; +} + +template +inline T TreeMemory::GetCell(size_t index) { + assert(0 <= index && index < memory.size()); + return memory[index]; +} + +template +inline size_t TreeMemory::Root() { + return 0; +} + +template +inline size_t TreeMemory::Left(size_t origin) { + return (origin << 1) + 1; +} + +template +inline size_t TreeMemory::Right(size_t origin) { + return (origin << 1) + 2; +} + +template +inline size_t TreeMemory::LeafIndex(size_t leafPos) { + assert(0 <= leafPos && leafPos < memorySize); + assert(Get(leafPos) == memory[actualMemorySize - 1 + leafPos]); + return actualMemorySize - 1 + leafPos; +} + +template +inline size_t TreeMemory::Parent(size_t child) { + if (child == 0) return actualMemorySize; + return ((child + 1) >> 1) - 1; +} + +template +void TreeMemory::Initialize(vector& leafValues) { + assert(leafValues.size() <= memorySize); + // First, write in the leaf nodes. + for (size_t i = 0; i < leafValues.size(); ++i) { + memory[actualMemorySize - 1 + i] = leafValues[i]; + } + if (actualMemorySize < 2) return; + size_t lastWrittenIdx = actualMemorySize - 1 + leafValues.size() - 1; + // After that, write into inner nodes as prescribed by writeFunction. + for (size_t i = actualMemorySize - 2; ; --i) { + size_t l = Left(i), r = Right(i); + assert(l <= r); + if (r > lastWrittenIdx) continue; + memory[i] = joinFunction(memory[l], memory[r]); + + if (i == Root()) break; + } +} + +template +void TreeMemory::Update(size_t pos, T el) { + assert(pos >= 0 && pos < memorySize); + size_t start = actualMemorySize - 1 + pos; + memory[start] = writeFunction(memory[start], el); + if (start > 0) { + while (true) { + start = Parent(start); + size_t l = Left(start), r = Right(start); + memory[start] = joinFunction(memory[l], memory[r]); + if (start == Root()) break; + } + } +} +} // namespace augmented +} // namespace ann +} // namespace mlpack + +#endif \ No newline at end of file diff --git a/src/mlpack/tests/augmented_rnns_tasks_test.cpp b/src/mlpack/tests/augmented_rnns_tasks_test.cpp index 94cd6e7701..506e5c7c42 100644 --- a/src/mlpack/tests/augmented_rnns_tasks_test.cpp +++ b/src/mlpack/tests/augmented_rnns_tasks_test.cpp @@ -22,6 +22,8 @@ #include #include +#include + #include #include #include @@ -34,6 +36,7 @@ using std::vector; using std::pair; using std::make_pair; +using namespace mlpack::ann::augmented; using namespace mlpack::ann::augmented::tasks; using namespace mlpack::ann::augmented::scorers; @@ -275,4 +278,87 @@ BOOST_AUTO_TEST_CASE(AddTaskTest) { BOOST_REQUIRE(ok); } +template +struct ReplaceWriter { + T operator() (T a, T b) { return b; } +}; + +template +struct AddJoiner { + T operator() (T a, T b) { return a + b; } +}; + +BOOST_AUTO_TEST_CASE(TreeMemoryTestMinimum) { + AddJoiner J; + ReplaceWriter W; + // With these definitions, mem is exactly one of the well-known data structres + // in competitive programming - segment tree. + TreeMemory, ReplaceWriter> mem(1, J, W); + std::vector initMem = {0}; + mem.Initialize(initMem); + BOOST_REQUIRE_EQUAL(mem.Get(0), 0.); + mem.Update(0, 12.); + BOOST_REQUIRE_EQUAL(mem.Get(0), 12.); +} + +BOOST_AUTO_TEST_CASE(TreeMemoryTestPowerOfTwo) { + AddJoiner J; + ReplaceWriter W; + TreeMemory, ReplaceWriter> mem(8, J, W); + std::vector initMem = {0, 0, 0, 0, 0, 0, 0, 0}; + mem.Initialize(initMem); + for (size_t idx = 0; idx < 15; ++idx) + BOOST_REQUIRE_EQUAL(mem.GetCell(idx), 0); + mem.Update(0, 1); + mem.Update(1, 2); + BOOST_REQUIRE_EQUAL(mem.Get(0), 1); + BOOST_REQUIRE_EQUAL(mem.Get(1), 2); + BOOST_REQUIRE_EQUAL(mem.GetCell( + mem.Parent(mem.LeafIndex(0))), 3); + BOOST_REQUIRE_EQUAL(mem.GetCell( + mem.Parent(mem.Parent(mem.LeafIndex(0)))), 3); + BOOST_REQUIRE_EQUAL(mem.GetCell( + mem.Parent(mem.Parent(mem.Parent(mem.LeafIndex(0))))), 3); +} + +BOOST_AUTO_TEST_CASE(TreeMemoryTestArbitrary) { + AddJoiner J; + ReplaceWriter W; + TreeMemory, ReplaceWriter> mem(9, J, W); + std::vector initMem = {0, 0, 0, 0, 0, 0, 0, 0, 0}; + mem.Initialize(initMem); + for (size_t idx = 0; idx < 31; ++idx) + BOOST_REQUIRE_EQUAL(mem.GetCell(idx), 0); + mem.Update(0, 1); + mem.Update(1, 2); + mem.Update(8, -3); + BOOST_REQUIRE_EQUAL(mem.Get(0), 1); + BOOST_REQUIRE_EQUAL(mem.Get(1), 2); + BOOST_REQUIRE_EQUAL(mem.GetCell( + mem.Parent(mem.LeafIndex(0))), 3); + BOOST_REQUIRE_EQUAL(mem.GetCell( + mem.Parent(mem.Parent(mem.LeafIndex(0)))), 3); + BOOST_REQUIRE_EQUAL(mem.GetCell( + mem.Parent(mem.Parent(mem.Parent(mem.LeafIndex(0))))), 3); + BOOST_REQUIRE_EQUAL(mem.GetCell( + mem.Parent(mem.Parent(mem.Parent(mem.Parent(mem.LeafIndex(0)))))), 0); + BOOST_REQUIRE_EQUAL(mem.Get(8), -3); + BOOST_REQUIRE_EQUAL(mem.GetCell( + mem.Parent(mem.LeafIndex(8))), -3); + BOOST_REQUIRE_EQUAL(mem.GetCell( + mem.Parent(mem.Parent(mem.LeafIndex(8)))), -3); + BOOST_REQUIRE_EQUAL(mem.GetCell( + mem.Parent(mem.Parent(mem.Parent(mem.LeafIndex(8))))), -3); + BOOST_REQUIRE_EQUAL(mem.Parent(mem.LeafIndex(0)), + mem.Parent(mem.LeafIndex(1))); + for (size_t i = 0; i < 9; ++i) { + // Not quite compliant with the style guide, + // but at least it fits in 80 charactes. + BOOST_REQUIRE_EQUAL( + mem.Parent(mem.Parent(mem.Parent(mem.Parent(mem.LeafIndex(i))))), + mem.Root() + ); + } +} + BOOST_AUTO_TEST_SUITE_END();