added SparseMask class

This commit is contained in:
2026-06-25 17:00:50 +02:00
parent 8c9f4ca763
commit 3ba9ef05d8
4 changed files with 175 additions and 3 deletions
+6 -3
View File
@@ -419,7 +419,8 @@ set(PUBLICHEADERS
include/aare/RawMasterFile.hpp
include/aare/RawSubFile.hpp
include/aare/VarClusterFinder.hpp
include/aare/utils/task.hpp)
include/aare/utils/task.hpp
include/aare/utils/SparseMask.hpp)
set(SourceFiles
${CMAKE_CURRENT_SOURCE_DIR}/src/calibration.cpp
@@ -444,7 +445,8 @@ set(SourceFiles
${CMAKE_CURRENT_SOURCE_DIR}/src/RawSubFile.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/to_string.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/utils/task.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/utils/ifstream_helpers.cpp)
${CMAKE_CURRENT_SOURCE_DIR}/src/utils/ifstream_helpers.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/utils/SparseMask.cpp)
add_library(aare_core STATIC ${SourceFiles} ${PUBLICHEADERS})
target_include_directories(
@@ -512,7 +514,8 @@ if(AARE_TESTS)
${CMAKE_CURRENT_SOURCE_DIR}/src/RawFile.test.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/RawSubFile.test.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/utils/task.test.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/to_string.test.cpp)
${CMAKE_CURRENT_SOURCE_DIR}/src/to_string.test.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/utils/SparseMask.test.cpp)
target_sources(tests PRIVATE ${TestSources})
endif()
+56
View File
@@ -0,0 +1,56 @@
#include "aare/NDView.hpp"
#include <cstdint>
#include <filesystem>
#include <vector>
namespace aare {
enum STORAGEFORMAT : uint8_t { ROWMAJOR = 0, COLUMNMAJOR = 1 };
/**
* @brief A class representing a sparse mask for a 2D array.
*
* The SparseMask class allows for efficient storage and retrieval of non-zero
* elements in a 2D array. It supports both row-major and column-major storage
* formats.
*/
class SparseMask {
public:
SparseMask(const STORAGEFORMAT storage_format, const size_t rows,
const size_t cols);
SparseMask(NDView<bool, 2> mask, const STORAGEFORMAT storage_format);
// TODO: think of storage hdf5 probably the best innerindices, outerindices
// as datasets and storage_format as attribute
SparseMask(const std::filesystem::path &filename);
void insert(const size_t row, const size_t col);
/**
* @brief Check if the element at (row, col) is masked (non-zero).
* @param row Row index of the element.
* @param col Column index of the element.
* @return true if the element is masked (non-zero), false otherwise.
*/
bool is_masked(const size_t row, const size_t col) const;
void write_to_file(const std::filesystem::path &filename) const;
private:
/// @brief stoarge format of the sparse mask, either row major or column
/// major
STORAGEFORMAT storage_format_;
/// @brief for column major stores row indices of non-zero elements, for row
/// major stores column indices of non-zero elements
std::vector<uint32_t> innerindices_;
/// @brief for column major outerindices[j] gives the index in innerindices_
/// of the first non-zero element in column j, for row major outerindices[i]
/// gives the index in innerindices_ of the first non-zero element in row i
std::vector<uint32_t> outerindices_;
};
} // namespace aare
+64
View File
@@ -0,0 +1,64 @@
#include "aare/utils/SparseMask.hpp"
namespace aare {
SparseMask::SparseMask(const STORAGEFORMAT storage_format, const size_t rows,
const size_t cols)
: storage_format_(storage_format) {
if (storage_format_ == STORAGEFORMAT::ROWMAJOR) {
outerindices_.resize(rows + 1, 0);
} else if (storage_format_ == STORAGEFORMAT::COLUMNMAJOR) {
outerindices_.resize(cols + 1, 0);
} else {
throw std::invalid_argument(
"Invalid storage format: must be either ROWMAJOR or COLUMNMAJOR");
}
innerindices_.reserve(rows * cols); // Reserve maximum possible size
}
void SparseMask::insert(const size_t row, const size_t col) {
// TODO: can be very inefficient -> most generic so does not depend on row,
// col order of insertion
// -> maybe better std::vector<std::vector>> for each row, col flatten after
// inserting all -> create innerindices from size
if (storage_format_ == STORAGEFORMAT::ROWMAJOR) {
std::for_each(outerindices_.begin() + row + 1, outerindices_.end(),
[](uint32_t &x) { ++x; });
innerindices_.insert(innerindices_.begin() + outerindices_[row],
static_cast<uint32_t>(col));
} else {
std::for_each(outerindices_.begin() + col + 1, outerindices_.end(),
[](uint32_t &x) { ++x; });
innerindices_.insert(innerindices_.begin() + outerindices_[col],
static_cast<uint32_t>(row));
}
}
bool SparseMask::is_masked(const size_t row, const size_t col) const {
const size_t index_outer_indices =
storage_format_ == STORAGEFORMAT::ROWMAJOR ? row : col;
const size_t nonzero_index =
storage_format_ == STORAGEFORMAT::ROWMAJOR ? col : row;
if (outerindices_[index_outer_indices + 1] -
outerindices_[index_outer_indices] ==
0) {
return false; // No non-zero elements in this row
} else {
auto start = outerindices_[index_outer_indices];
auto end = outerindices_[index_outer_indices + 1];
for (size_t i = start; i < end; ++i) {
if (innerindices_[i] == nonzero_index) {
return true; // Found a non-zero element at (row, col)
}
}
return false; // No non-zero element found at (row, col)
}
}
} // namespace aare
+49
View File
@@ -0,0 +1,49 @@
#include "aare/utils/SparseMask.hpp"
#include <catch2/catch_all.hpp>
#include <catch2/catch_test_macros.hpp>
TEST_CASE("Create Sparse Mask") {
std::vector<std::pair<size_t, size_t>> masked_pairs = {
{1, 1}, {1, 2}, {1, 4}, {2, 0}, {2, 2}, {2, 4}, {3, 4}, {4, 1}};
SECTION("Row Major") {
aare::SparseMask mask(aare::STORAGEFORMAT::ROWMAJOR, 5, 5);
std::for_each(masked_pairs.begin(), masked_pairs.end(),
[&mask](const std::pair<size_t, size_t> &p) {
mask.insert(p.first, p.second);
});
for (size_t row = 0; row < 5; ++row) {
for (size_t col = 0; col < 5; ++col) {
if (std::find(masked_pairs.begin(), masked_pairs.end(),
std::make_pair(row, col)) != masked_pairs.end()) {
CHECK(mask.is_masked(row, col) == true);
} else {
CHECK(mask.is_masked(row, col) == false);
}
}
}
}
SECTION("Column Major") {
aare::SparseMask mask(aare::STORAGEFORMAT::COLUMNMAJOR, 5, 5);
std::for_each(masked_pairs.begin(), masked_pairs.end(),
[&mask](const std::pair<size_t, size_t> &p) {
mask.insert(p.first, p.second);
});
for (size_t row = 0; row < 5; ++row) {
for (size_t col = 0; col < 5; ++col) {
if (std::find(masked_pairs.begin(), masked_pairs.end(),
std::make_pair(row, col)) != masked_pairs.end()) {
CHECK(mask.is_masked(row, col) == true);
} else {
CHECK(mask.is_masked(row, col) == false);
}
}
}
}
}