-
Notifications
You must be signed in to change notification settings - Fork 13.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* 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 <[email protected]>
- Loading branch information
Showing
9 changed files
with
299 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -25,7 +25,7 @@ addons: | |
packages: | ||
- clang | ||
- cmake | ||
- g++ | ||
- g++-8 | ||
- gcc | ||
- lcov | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,160 @@ | ||
/** | ||
* @file SparseVector.hpp | ||
* | ||
* SparseVector class. | ||
* | ||
* @author Kamil Ritz <[email protected]> | ||
* @author Julian Kent <[email protected]> | ||
* | ||
*/ | ||
|
||
#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...>; | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,3 +20,4 @@ | |
#include "LeastSquaresSolver.hpp" | ||
#include "Dual.hpp" | ||
#include "PseudoInverse.hpp" | ||
#include "SparseVector.hpp" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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(); | ||
} |