Browse Source

SparseVector (#140)

* 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
kritz 5 years ago committed by GitHub
parent
commit
18699416b0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      .travis.yml
  2. 2
      CMakeLists.txt
  3. 8
      matrix/Slice.hpp
  4. 160
      matrix/SparseVector.hpp
  5. 1
      matrix/math.hpp
  6. 4
      test/CMakeLists.txt
  7. 18
      test/CMakeLists.txt.in
  8. 12
      test/gtest.cmake
  9. 98
      test/sparseVector.cpp

2
.travis.yml

@ -25,7 +25,7 @@ addons: @@ -25,7 +25,7 @@ addons:
packages:
- clang
- cmake
- g++
- g++-8
- gcc
- lcov

2
CMakeLists.txt

@ -6,7 +6,7 @@ set(VERSION_PATCH "2") @@ -6,7 +6,7 @@ set(VERSION_PATCH "2")
project(matrix CXX)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
if (NOT CMAKE_BUILD_TYPE)

8
matrix/Slice.hpp

@ -240,9 +240,9 @@ public: @@ -240,9 +240,9 @@ public:
return res;
}
Type norm_squared()
Type norm_squared() const
{
Slice<Type, P, Q, M, N>& self = *this;
const Slice<Type, P, Q, M, N>& self = *this;
Type accum(0);
for (size_t i = 0; i < P; i++) {
for (size_t j = 0; j < Q; j++) {
@ -252,12 +252,12 @@ public: @@ -252,12 +252,12 @@ public:
return accum;
}
Type norm()
Type norm() const
{
return matrix::sqrt(norm_squared());
}
bool longerThan(Type testVal)
bool longerThan(Type testVal) const
{
return norm_squared() > testVal*testVal;
}

160
matrix/SparseVector.hpp

@ -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...>;
}

1
matrix/math.hpp

@ -20,3 +20,4 @@ @@ -20,3 +20,4 @@
#include "LeastSquaresSolver.hpp"
#include "Dual.hpp"
#include "PseudoInverse.hpp"
#include "SparseVector.hpp"

4
test/CMakeLists.txt

@ -1,3 +1,5 @@ @@ -1,3 +1,5 @@
include(gtest.cmake)
set(tests
setIdentity
inverse
@ -13,6 +15,7 @@ set(tests @@ -13,6 +15,7 @@ set(tests
attitude
filter
integration
sparseVector
squareMatrix
helper
hatvee
@ -30,6 +33,7 @@ foreach(test_name ${tests}) @@ -30,6 +33,7 @@ foreach(test_name ${tests})
add_test(test_${test_name} ${test_name})
add_dependencies(test_build ${test_name})
endforeach()
target_link_libraries(sparseVector gtest_main)
if (${CMAKE_BUILD_TYPE} STREQUAL "Coverage")

18
test/CMakeLists.txt.in

@ -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
)

12
test/gtest.cmake

@ -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)

98
test/sparseVector.cpp

@ -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…
Cancel
Save