Put TreeMemory to the working condition + tests for it
This commit is contained in:
@@ -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)
|
||||
|
||||
+4
-4
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user