Browse Source
* Add SparseVector temp * Add gtest * Some reworking of the sparse concept * Change type of M from int to size_t * Add const modifier * Add needed declaration for accessing elements of _indices * Add norm_squared, norm, longerThan * Add test for all sparse vector functions * Add missing const to slice's norm_squared, norm and longerThan * Construction from Vector<M> and carray[N] * try to fix ci Co-authored-by: Julian Kent <julian@auterion.com>master
9 changed files with 299 additions and 6 deletions
@ -0,0 +1,160 @@
@@ -0,0 +1,160 @@
|
||||
/**
|
||||
* @file SparseVector.hpp |
||||
* |
||||
* SparseVector class. |
||||
* |
||||
* @author Kamil Ritz <kritz@ethz.ch> |
||||
* @author Julian Kent <julian@auterion.com> |
||||
* |
||||
*/ |
||||
|
||||
#pragma once |
||||
|
||||
#include "math.hpp" |
||||
|
||||
namespace matrix { |
||||
template<int N> struct force_constexpr_eval { |
||||
static const int value = N; |
||||
}; |
||||
|
||||
// Vector that only store nonzero elements,
|
||||
// which indices are specified as parameter pack
|
||||
template<typename Type, size_t M, int... Idxs> |
||||
class SparseVector { |
||||
private: |
||||
static constexpr size_t N = sizeof...(Idxs); |
||||
static constexpr int _indices[N] {Idxs...}; |
||||
|
||||
static constexpr bool duplicateIndices() { |
||||
for (int i = 0; i < N; i++) |
||||
for (int j = 0; j < i; j++) |
||||
if (_indices[i] == _indices[j]) |
||||
return true; |
||||
return false; |
||||
} |
||||
static constexpr int findMaxIndex() { |
||||
int maxIndex = -1; |
||||
for (int i = 0; i < N; i++) { |
||||
if (maxIndex < _indices[i]) { |
||||
maxIndex = _indices[i]; |
||||
} |
||||
} |
||||
return maxIndex; |
||||
} |
||||
static_assert(duplicateIndices() == false, "Duplicate indices"); |
||||
static_assert(N < M, "More entries than elements, use a dense vector"); |
||||
static_assert(findMaxIndex() < M, "Largest entry doesn't fit in sparse vector"); |
||||
|
||||
Type _data[N] {}; |
||||
|
||||
static constexpr int findCompressedIndex(int index) { |
||||
int compressedIndex = -1; |
||||
for (int i = 0; i < N; i++) { |
||||
if (index == _indices[i]) { |
||||
compressedIndex = i; |
||||
} |
||||
} |
||||
return compressedIndex; |
||||
} |
||||
|
||||
public: |
||||
static constexpr int non_zeros() { |
||||
return N; |
||||
} |
||||
|
||||
constexpr int index(int i) const { |
||||
return SparseVector::_indices[i]; |
||||
} |
||||
|
||||
SparseVector() = default; |
||||
|
||||
SparseVector(const matrix::Vector<Type, M>& data) { |
||||
for (int i = 0; i < N; i++) { |
||||
_data[i] = data(_indices[i]); |
||||
} |
||||
} |
||||
|
||||
explicit SparseVector(const Type data[N]) { |
||||
memcpy(_data, data, sizeof(_data)); |
||||
} |
||||
|
||||
template <int i> |
||||
inline Type at() const { |
||||
static constexpr int compressed_index = force_constexpr_eval<findCompressedIndex(i)>::value; |
||||
static_assert(compressed_index >= 0, "cannot access unpopulated indices"); |
||||
return _data[compressed_index]; |
||||
} |
||||
|
||||
template <int i> |
||||
inline Type& at() { |
||||
static constexpr int compressed_index = force_constexpr_eval<findCompressedIndex(i)>::value; |
||||
static_assert(compressed_index >= 0, "cannot access unpopulated indices"); |
||||
return _data[compressed_index]; |
||||
} |
||||
|
||||
void setZero() { |
||||
for (size_t i = 0; i < N; i++) { |
||||
_data[i] = Type(0); |
||||
} |
||||
} |
||||
|
||||
Type dot(const matrix::Vector<Type, M>& other) const { |
||||
Type accum (0); |
||||
for (size_t i = 0; i < N; i++) { |
||||
accum += _data[i] * other(_indices[i]); |
||||
} |
||||
return accum; |
||||
} |
||||
|
||||
matrix::Vector<Type, M> operator+(const matrix::Vector<Type, M>& other) const { |
||||
matrix::Vector<Type, M> vec = other; |
||||
for (size_t i = 0; i < N; i++) { |
||||
vec(_indices[i]) += _data[i]; |
||||
} |
||||
return vec; |
||||
} |
||||
|
||||
SparseVector& operator+=(Type t) { |
||||
for (size_t i = 0; i < N; i++) { |
||||
_data[i] += t; |
||||
} |
||||
return *this; |
||||
} |
||||
|
||||
Type norm_squared() const |
||||
{ |
||||
Type accum(0); |
||||
for (size_t i = 0; i < N; i++) { |
||||
accum += _data[i] * _data[i]; |
||||
} |
||||
return accum; |
||||
} |
||||
|
||||
Type norm() const |
||||
{ |
||||
return matrix::sqrt(norm_squared()); |
||||
} |
||||
|
||||
bool longerThan(Type testVal) const |
||||
{ |
||||
return norm_squared() > testVal*testVal; |
||||
} |
||||
}; |
||||
|
||||
template<typename Type, size_t Q, size_t M, int ... Idxs> |
||||
matrix::Vector<Type, Q> operator*(const matrix::Matrix<Type, Q, M>& mat, const matrix::SparseVector<Type, M, Idxs...>& vec) { |
||||
matrix::Vector<Type, Q> res; |
||||
for (size_t i = 0; i < Q; i++) { |
||||
const Vector<Type, M> row = mat.row(i); |
||||
res(i) = vec.dot(row); |
||||
} |
||||
return res; |
||||
} |
||||
|
||||
template<typename Type,size_t M, int... Idxs> |
||||
constexpr int SparseVector<Type, M, Idxs...>::_indices[SparseVector<Type, M, Idxs...>::N]; |
||||
|
||||
template<size_t M, int ... Idxs> |
||||
using SparseVectorf = SparseVector<float, M, Idxs...>; |
||||
|
||||
} |
@ -0,0 +1,18 @@
@@ -0,0 +1,18 @@
|
||||
cmake_minimum_required(VERSION 2.8.4) |
||||
|
||||
project(googletest-download NONE) |
||||
|
||||
include(ExternalProject) |
||||
ExternalProject_Add(googletest |
||||
URL https://github.com/google/googletest/archive/8b6d3f9c4a774bef3081195d422993323b6bb2e0.zip |
||||
SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/googletest-src" |
||||
BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/googletest-build" |
||||
CONFIGURE_COMMAND "" |
||||
BUILD_COMMAND "" |
||||
INSTALL_COMMAND "" |
||||
TEST_COMMAND "" |
||||
# Wrap download, configure and build steps in a script to log output |
||||
LOG_DOWNLOAD ON |
||||
LOG_CONFIGURE ON |
||||
LOG_BUILD ON |
||||
) |
@ -0,0 +1,12 @@
@@ -0,0 +1,12 @@
|
||||
set_directory_properties(PROPERTIES COMPILE_OPTIONS "") |
||||
|
||||
# Download and unpack googletest at configure time |
||||
configure_file(${CMAKE_CURRENT_LIST_DIR}/CMakeLists.txt.in googletest-download/CMakeLists.txt) |
||||
execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" . RESULT_VARIABLE result1 WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/googletest-download) |
||||
execute_process(COMMAND ${CMAKE_COMMAND} --build . RESULT_VARIABLE result2 WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/googletest-download) |
||||
if(result1 OR result2) |
||||
message(FATAL_ERROR "Preparing googletest failed: ${result1} ${result2}") |
||||
endif() |
||||
|
||||
# Add googletest, defines gtest and gtest_main targets |
||||
add_subdirectory(${CMAKE_CURRENT_BINARY_DIR}/googletest-src ${CMAKE_CURRENT_BINARY_DIR}/googletest-build EXCLUDE_FROM_ALL) |
@ -0,0 +1,98 @@
@@ -0,0 +1,98 @@
|
||||
#include <matrix/math.hpp> |
||||
#include <gtest/gtest.h> |
||||
|
||||
using namespace matrix; |
||||
|
||||
TEST(sparseVectorTest, defaultConstruction) { |
||||
SparseVectorf<24, 4, 6> a; |
||||
EXPECT_EQ(a.non_zeros(), 2); |
||||
EXPECT_EQ(a.index(0), 4); |
||||
EXPECT_EQ(a.index(1), 6); |
||||
a.at<4>() = 1.f; |
||||
a.at<6>() = 2.f; |
||||
} |
||||
|
||||
TEST(sparseVectorTest, initializationWithData) { |
||||
const float data[3] = {1.f, 2.f, 3.f}; |
||||
SparseVectorf<24, 4, 6, 22> a(data); |
||||
EXPECT_EQ(a.non_zeros(), 3); |
||||
EXPECT_EQ(a.index(0), 4); |
||||
EXPECT_EQ(a.index(1), 6); |
||||
EXPECT_EQ(a.index(2), 22); |
||||
EXPECT_FLOAT_EQ(a.at<4>(), data[0]); |
||||
EXPECT_FLOAT_EQ(a.at<6>(), data[1]); |
||||
EXPECT_FLOAT_EQ(a.at<22>(), data[2]); |
||||
} |
||||
|
||||
TEST(sparseVectorTest, initialisationFromVector) { |
||||
const Vector3f vec(1.f, 2.f, 3.f); |
||||
const SparseVectorf<3, 0, 2> a(vec); |
||||
EXPECT_FLOAT_EQ(a.at<0>(), vec(0)); |
||||
EXPECT_FLOAT_EQ(a.at<2>(), vec(2)); |
||||
} |
||||
|
||||
TEST(sparseVectorTest, setZero) { |
||||
const float data[3] = {1.f, 2.f, 3.f}; |
||||
SparseVectorf<24, 4, 6, 22> a(data); |
||||
a.setZero(); |
||||
EXPECT_FLOAT_EQ(a.at<4>(), 0.f); |
||||
EXPECT_FLOAT_EQ(a.at<6>(), 0.f); |
||||
EXPECT_FLOAT_EQ(a.at<22>(), 0.f); |
||||
} |
||||
|
||||
TEST(sparseVectorTest, additionWithDenseVector) { |
||||
Vector<float, 4> dense_vec; |
||||
dense_vec.setAll(1.f); |
||||
const float data[3] = {1.f, 2.f, 3.f}; |
||||
const SparseVectorf<4, 1, 2, 3> sparse_vec(data); |
||||
const Vector<float, 4> res = sparse_vec + dense_vec; |
||||
EXPECT_FLOAT_EQ(res(0), 1.f); |
||||
EXPECT_FLOAT_EQ(res(1), 2.f); |
||||
EXPECT_FLOAT_EQ(res(2), 3.f); |
||||
EXPECT_FLOAT_EQ(res(3), 4.f); |
||||
} |
||||
|
||||
TEST(sparseVectorTest, addScalar) { |
||||
const float data[3] = {1.f, 2.f, 3.f}; |
||||
SparseVectorf<4, 1, 2, 3> sparse_vec(data); |
||||
sparse_vec += 2.f; |
||||
EXPECT_FLOAT_EQ(sparse_vec.at<1>(), 3.f); |
||||
EXPECT_FLOAT_EQ(sparse_vec.at<2>(), 4.f); |
||||
EXPECT_FLOAT_EQ(sparse_vec.at<3>(), 5.f); |
||||
} |
||||
|
||||
TEST(sparseVectorTest, dotProductWithDenseVector) { |
||||
Vector<float, 4> dense_vec; |
||||
dense_vec.setAll(3.f); |
||||
const float data[3] = {1.f, 2.f, 3.f}; |
||||
const SparseVectorf<4, 1, 2, 3> sparse_vec(data); |
||||
float res = sparse_vec.dot(dense_vec); |
||||
EXPECT_FLOAT_EQ(res, 18.f); |
||||
} |
||||
|
||||
TEST(sparseVectorTest, multiplicationWithDenseMatrix) { |
||||
Matrix<float, 2, 3> dense_matrix; |
||||
dense_matrix.setAll(2.f); |
||||
dense_matrix(1, 1) = 3.f; |
||||
const Vector3f dense_vec(0.f, 1.f, 5.f); |
||||
const SparseVectorf<3, 1, 2> sparse_vec(dense_vec); |
||||
const Vector<float, 2> res_sparse = dense_matrix * sparse_vec; |
||||
const Vector<float, 2> res_dense = dense_matrix * dense_vec; |
||||
EXPECT_TRUE(isEqual(res_dense, res_sparse)); |
||||
} |
||||
|
||||
TEST(sparseVectorTest, norms) { |
||||
const float data[2] = {3.f, 4.f}; |
||||
const SparseVectorf<4, 1, 3> sparse_vec(data); |
||||
EXPECT_FLOAT_EQ(sparse_vec.norm_squared(), 25.f); |
||||
EXPECT_FLOAT_EQ(sparse_vec.norm(), 5.f); |
||||
EXPECT_TRUE(sparse_vec.longerThan(4.5f)); |
||||
EXPECT_FALSE(sparse_vec.longerThan(5.5f)); |
||||
} |
||||
|
||||
|
||||
int main(int argc, char **argv) { |
||||
testing::InitGoogleTest(&argc, argv); |
||||
std::cout << "Run SparseVector tests" << std::endl; |
||||
return RUN_ALL_TESTS(); |
||||
} |
Loading…
Reference in new issue