Put TreeMemory to the working condition + tests for it

This commit is contained in:
Konstantin Sidorov
2017-07-31 12:36:22 +03:00
parent 416098cd18
commit f33ea2474a
5 changed files with 207 additions and 88 deletions
@@ -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)
@@ -15,8 +15,6 @@
#include <vector>
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<T>& leafValues);
void Initialize(std::vector<T>& 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<T> memory;
std::vector<T> memory;
J joinFunction;
W writeFunction;
size_t memorySize;
@@ -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 <cassert>
#include "tree_memory.hpp"
TreeMemory<T>::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<size_t>(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<T>::Get(size_t index) {
assert(0 <= index && index < memorySize);
return memory[actualMemorySize - 1 + index];
}
inline size_t TreeMemory<T>::Root() {
return 0;
}
inline size_t TreeMemory<T>::Left(size_t origin) {
return res = (origin << 1) + 1;
}
inline size_t TreeMemory<T>::Right(size_t origin) {
return res = (origin << 1) + 2;
}
inline size_t TreeMemory<T>::Parent(size_t child) {
if (child == 0) return actualMemorySize;
return ((child + 1) >> 1) - 1;
}
void TreeMemory<T>::Initialize(vector<T>& 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<T>::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
@@ -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 <cassert>
#include "tree_memory.hpp"
namespace mlpack {
namespace ann /* Artificial Neural Network */ {
namespace augmented /* Augmented neural network */ {
template<typename T, typename J, typename W>
TreeMemory<T, J, W>::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<size_t>(1 << 31));
actualMemorySize = 1 << static_cast<int>(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<typename T, typename J, typename W>
inline T TreeMemory<T, J, W>::Get(size_t index) {
assert(0 <= index && index < memorySize);
return memory[actualMemorySize - 1 + index];
}
template<typename T, typename J, typename W>
inline T TreeMemory<T, J, W>::GetCell(size_t index) {
assert(0 <= index && index < memory.size());
return memory[index];
}
template<typename T, typename J, typename W>
inline size_t TreeMemory<T, J, W>::Root() {
return 0;
}
template<typename T, typename J, typename W>
inline size_t TreeMemory<T, J, W>::Left(size_t origin) {
return (origin << 1) + 1;
}
template<typename T, typename J, typename W>
inline size_t TreeMemory<T, J, W>::Right(size_t origin) {
return (origin << 1) + 2;
}
template<typename T, typename J, typename W>
inline size_t TreeMemory<T, J, W>::LeafIndex(size_t leafPos) {
assert(0 <= leafPos && leafPos < memorySize);
assert(Get(leafPos) == memory[actualMemorySize - 1 + leafPos]);
return actualMemorySize - 1 + leafPos;
}
template<typename T, typename J, typename W>
inline size_t TreeMemory<T, J, W>::Parent(size_t child) {
if (child == 0) return actualMemorySize;
return ((child + 1) >> 1) - 1;
}
template<typename T, typename J, typename W>
void TreeMemory<T, J, W>::Initialize(vector<T>& 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<typename T, typename J, typename W>
void TreeMemory<T, J, W>::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
@@ -22,6 +22,8 @@
#include <mlpack/methods/ann/augmented/tasks/add.hpp>
#include <mlpack/methods/ann/augmented/tasks/score.hpp>
#include <mlpack/methods/ann/augmented/tree_memory.hpp>
#include <mlpack/core/optimizers/adam/adam.hpp>
#include <mlpack/methods/ann/layer/layer.hpp>
#include <mlpack/methods/ann/layer/leaky_relu.hpp>
@@ -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<typename T>
struct ReplaceWriter {
T operator() (T a, T b) { return b; }
};
template<typename T>
struct AddJoiner {
T operator() (T a, T b) { return a + b; }
};
BOOST_AUTO_TEST_CASE(TreeMemoryTestMinimum) {
AddJoiner<double> J;
ReplaceWriter<double> W;
// With these definitions, mem is exactly one of the well-known data structres
// in competitive programming - segment tree.
TreeMemory<double, AddJoiner<double>, ReplaceWriter<double>> mem(1, J, W);
std::vector<double> 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<double> J;
ReplaceWriter<double> W;
TreeMemory<double, AddJoiner<double>, ReplaceWriter<double>> mem(8, J, W);
std::vector<double> 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<double> J;
ReplaceWriter<double> W;
TreeMemory<double, AddJoiner<double>, ReplaceWriter<double>> mem(9, J, W);
std::vector<double> 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();