Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor indptr / sparse structure #379

Merged
merged 73 commits into from
Oct 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
73 commits
Select commit Hold shift + click to select a range
b54a0d0
add rough structure
nitbharambe Sep 12, 2023
5c3e20d
Update test_sparse_idx_vector.cpp
nitbharambe Sep 12, 2023
af1e20a
address initial comments
nitbharambe Sep 12, 2023
950e607
Update sparse_idx_vector.hpp
nitbharambe Sep 12, 2023
cf3cf58
fix smell
nitbharambe Sep 12, 2023
c5e92c0
add data size function
nitbharambe Sep 12, 2023
e08ff3b
combine structures
nitbharambe Sep 12, 2023
fc1f0e5
minor fix
nitbharambe Sep 13, 2023
d9ebeca
make only map
nitbharambe Sep 18, 2023
18052a4
fix spell
nitbharambe Sep 18, 2023
22455c1
fix structure
nitbharambe Sep 18, 2023
2c2d455
modify example
nitbharambe Sep 18, 2023
09c2b46
redesign structure
nitbharambe Sep 19, 2023
658ebb9
add items
nitbharambe Sep 19, 2023
a056ba7
fix sonar
nitbharambe Sep 19, 2023
91141d5
add groups
nitbharambe Sep 21, 2023
e927645
add groups items
nitbharambe Sep 22, 2023
17020d7
clean up types
nitbharambe Sep 22, 2023
270b960
add comment
nitbharambe Sep 22, 2023
e8bd332
Merge branch 'main' into feature/refactor-indptr
nitbharambe Sep 22, 2023
1dab96d
with groups temp
nitbharambe Oct 2, 2023
0e7b238
remove unrequired code
nitbharambe Oct 2, 2023
aa924ee
Merge branch 'feature/refactor-indptr' of https://github.com/PowerGri…
nitbharambe Oct 2, 2023
86cb5a6
Merge branch 'main' into feature/refactor-indptr
nitbharambe Oct 2, 2023
903c306
add dense vector
nitbharambe Oct 2, 2023
654c245
add groups iterator
nitbharambe Oct 3, 2023
6500b2f
Merge branch 'feature/refactor-indptr' of https://github.com/PowerGri…
nitbharambe Oct 3, 2023
7ed3037
add zip iterator
nitbharambe Oct 9, 2023
add7a1c
Merge branch 'main' into feature/refactor-indptr
nitbharambe Oct 9, 2023
f4b4afd
add size check
nitbharambe Oct 9, 2023
23b580f
add dense iterator
nitbharambe Oct 10, 2023
d8db6e2
implement dense idx vector
mgovers Oct 10, 2023
b84cfd4
cleanup
mgovers Oct 10, 2023
ad44790
setup cleanup tests as template cases
mgovers Oct 10, 2023
6d4a930
Merge branch 'main' into feature/refactor-indptr
mgovers Oct 10, 2023
51671b9
rename
nitbharambe Oct 10, 2023
bddf389
Merge branch 'feature/refactor-indptr' of https://github.com/PowerGri…
nitbharambe Oct 10, 2023
af0e361
remove namespace detail
mgovers Oct 10, 2023
65525dd
static cast size
mgovers Oct 10, 2023
d360d7d
constexpr
mgovers Oct 10, 2023
ea1787e
const
mgovers Oct 10, 2023
a430e79
clean tests
nitbharambe Oct 10, 2023
4ca1880
resolve conflict
nitbharambe Oct 10, 2023
4450738
remove comment
nitbharambe Oct 10, 2023
ebbe72b
format
nitbharambe Oct 10, 2023
b68b1f3
address comments
nitbharambe Oct 10, 2023
d0aaf9d
add different contents
nitbharambe Oct 10, 2023
2cbadcb
try fold outside
nitbharambe Oct 10, 2023
a4bdb83
cast distance
nitbharambe Oct 10, 2023
2c15be7
fixes to usability
mgovers Oct 11, 2023
a268fbd
minor add static assert
mgovers Oct 11, 2023
1cab22a
minor fixes
nitbharambe Oct 11, 2023
31a9cf6
grouped idx vector concept
mgovers Oct 11, 2023
9534cca
Merge branch 'feature/refactor-indptr' of https://github.com/PowerGri…
mgovers Oct 11, 2023
a848f81
minor fix typo
mgovers Oct 11, 2023
c14e59c
Update power_grid_model_c/power_grid_model/include/power_grid_model/g…
nitbharambe Oct 11, 2023
bc29bec
change typename
nitbharambe Oct 11, 2023
edb8d65
Merge branch 'feature/refactor-indptr' of https://github.com/PowerGri…
nitbharambe Oct 11, 2023
0803fd7
improved naming + constructors
mgovers Oct 11, 2023
2b4dd78
Merge branch 'feature/refactor-indptr' of https://github.com/PowerGri…
mgovers Oct 11, 2023
e08103b
add assertions
mgovers Oct 11, 2023
5df2e6b
fix
mgovers Oct 11, 2023
0a99e31
sonar cloud
mgovers Oct 11, 2023
e76e740
no constexpr vector constructors
mgovers Oct 11, 2023
b669ae2
fix inline
mgovers Oct 11, 2023
020df69
fix move
mgovers Oct 11, 2023
e0c3242
gcc apparently has incomplete constexpr library support
mgovers Oct 11, 2023
cf2f8c6
make both sonar cloud and macos happy
mgovers Oct 11, 2023
ec11c95
make grouped idx vector iterator regular
mgovers Oct 12, 2023
53e206e
fix
mgovers Oct 12, 2023
b5fa5aa
enumerated zip sequence
mgovers Oct 12, 2023
38dcf69
cast ptrdiff to fix macos again
mgovers Oct 12, 2023
ac47b52
Merge branch 'main' into feature/refactor-indptr
mgovers Oct 12, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,273 @@
// SPDX-FileCopyrightText: 2022 Contributors to the Power Grid Model project <dynamic.grid.calculation@alliander.com>
//
// SPDX-License-Identifier: MPL-2.0

#pragma once
#ifndef POWER_GRID_MODEL_SPARSE_IDX_VECTOR_HPP
#define POWER_GRID_MODEL_SPARSE_IDX_VECTOR_HPP

#include "power_grid_model.hpp"

#include <boost/iterator/iterator_facade.hpp>
#include <boost/iterator/zip_iterator.hpp>
#include <boost/range.hpp>
#include <boost/range/adaptor/indexed.hpp>
#include <boost/range/counting_range.hpp>

/*
A data-structure for iterating through the indptr, ie. sparse representation of data.
Indptr can be eg: [0, 3, 6, 7]
This means that:
objects 0, 1, 2 are coupled to index 0
objects 3, 4, 5 are coupled to index 1
objects 6 is coupled to index 2

Another intuitive way to look at this for python developers is like list of lists: [[0, 1, 2], [3, 4, 5], [6]].

DenseGroupedIdxVector is a vector of element to group. ie. [0, 1, 1, 4] would denote that [[0], [1, 2], [], [], [3]]
The input, ie. [0, 1, 3] should be strictly increasing

*/

namespace power_grid_model {

namespace detail {

inline auto sparse_encode(IdxVector const& element_groups, Idx num_groups) {
IdxVector result(num_groups + 1);
auto next_group = std::begin(element_groups);
for (Idx group = 0; group < num_groups; group++) {
next_group = std::upper_bound(next_group, std::end(element_groups), group);
result[group + 1] = std::distance(std::begin(element_groups), next_group);
}
return result;
}

inline auto sparse_decode(IdxVector const& indptr) {
auto result = IdxVector(indptr.back());
for (Idx group{}; group < static_cast<Idx>(indptr.size()) - 1; ++group) {
std::fill(std::begin(result) + indptr[group], std::begin(result) + indptr[group + 1], group);
}
return result;
}

template <typename T, typename U> constexpr auto narrow_cast(U value) {
if constexpr (std::same_as<T, U>) {
return value;
}
assert(std::in_range<T>(value));
return static_cast<T>(value);
}

// TODO(mgovers): replace the below relevant subset here ourselves with the STD equivalent when we have std::ranges.
// boost::counting_iterator does not satisfy all requirements std::*_iterator concepts:
static_assert(!std::random_access_iterator<IdxCount>);
// we have to declare the relevant subset here ourselves.
template <typename T, typename ElementType>
concept iterator_like = requires(T const t) {
{ *t } -> std::convertible_to<std::remove_cvref_t<ElementType> const&>;
};

template <typename T, typename ElementType>
concept random_access_iterator_like =
std::regular<T> && iterator_like<T, ElementType> && std::totally_ordered<T> && requires(T t, Idx n) {
{ t++ } -> std::same_as<T>;
{ t-- } -> std::same_as<T>;
{ ++t } -> std::same_as<T&>;
{ --t } -> std::same_as<T&>;

{ t + n } -> std::same_as<T>;
{ t - n } -> std::same_as<T>;
{ t += n } -> std::same_as<T&>;
{ t -= n } -> std::same_as<T&>;
};

template <typename T, typename ElementType>
concept random_access_iterable_like = requires(T const t) {
{ t.begin() } -> random_access_iterator_like<ElementType>;
{ t.end() } -> random_access_iterator_like<ElementType>;
};

template <typename T>
concept index_range_iterator =
random_access_iterator_like<T, typename T::iterator> && requires(T const t) {
typename T::iterator;
{ *t } -> random_access_iterable_like<Idx>;
};

template <typename T>
concept grouped_index_vector_type = std::default_initializable<T> && requires(T const t, Idx const idx) {
{ t.size() } -> std::same_as<Idx>;

{ t.begin() } -> index_range_iterator;
{ t.end() } -> index_range_iterator;
{
t.get_element_range(idx)
} -> random_access_iterable_like<Idx>;

{ t.element_size() } -> std::same_as<Idx>;
{ t.get_group(idx) } -> std::same_as<Idx>;
};
} // namespace detail

template <typename T>
concept grouped_idx_vector_type = detail::grouped_index_vector_type<T>;

struct from_sparse_t {};
struct from_dense_t {};

constexpr auto from_sparse = from_sparse_t{};
constexpr auto from_dense = from_dense_t{};

class DenseGroupedIdxVector;

class SparseGroupedIdxVector {
private:
class GroupIterator : public boost::iterator_facade<GroupIterator, Idx, boost::random_access_traversal_tag,
TonyXiang8787 marked this conversation as resolved.
Show resolved Hide resolved
boost::iterator_range<IdxCount>, Idx> {
public:
using iterator = boost::iterator_range<IdxCount>;

GroupIterator() = default;
explicit constexpr GroupIterator(IdxVector const& indptr, Idx group) : indptr_{&indptr}, group_{group} {}

private:
IdxVector const* indptr_{};
Idx group_{};

friend class boost::iterator_core_access;

auto dereference() const -> iterator {
assert(indptr_ != nullptr);
return boost::counting_range((*indptr_)[group_], (*indptr_)[group_ + 1]);
}
constexpr auto equal(GroupIterator const& other) const { return group_ == other.group_; }
constexpr auto distance_to(GroupIterator const& other) const { return other.group_ - group_; }

constexpr void increment() { ++group_; }
constexpr void decrement() { --group_; }
constexpr void advance(Idx n) { group_ += n; }
};

auto group_iterator(Idx group) const { return GroupIterator{indptr_, group}; }

public:
auto size() const { return static_cast<Idx>(indptr_.size()) - 1; }
auto begin() const { return group_iterator(0); }
auto end() const { return group_iterator(size()); }
auto get_element_range(Idx group) const { return *group_iterator(group); }

auto element_size() const { return indptr_.back(); }
auto get_group(Idx element) const -> Idx {
assert(element < element_size());
return std::distance(std::begin(indptr_), std::upper_bound(std::begin(indptr_), std::end(indptr_), element)) -
TonyXiang8787 marked this conversation as resolved.
Show resolved Hide resolved
1;
}

SparseGroupedIdxVector() = default;
explicit SparseGroupedIdxVector(IdxVector sparse_group_elements)
: indptr_{sparse_group_elements.empty() ? IdxVector{0} : std::move(sparse_group_elements)} {
assert(size() >= 0);
assert(element_size() >= 0);
assert(std::is_sorted(std::begin(indptr_), std::end(indptr_)));
}
SparseGroupedIdxVector(from_sparse_t /* tag */, IdxVector sparse_group_elements)
: SparseGroupedIdxVector{std::move(sparse_group_elements)} {}
SparseGroupedIdxVector(from_dense_t /* tag */, IdxVector const& dense_group_elements, Idx num_groups)
: SparseGroupedIdxVector{detail::sparse_encode(dense_group_elements, num_groups)} {}

private:
IdxVector indptr_;
};

class DenseGroupedIdxVector {
private:
class GroupIterator : public boost::iterator_facade<GroupIterator, Idx, boost::random_access_traversal_tag,
TonyXiang8787 marked this conversation as resolved.
Show resolved Hide resolved
boost::iterator_range<IdxCount>, Idx> {
public:
using iterator = boost::iterator_range<IdxCount>;

GroupIterator() = default;
explicit constexpr GroupIterator(IdxVector const& dense_vector, Idx const& group)
: dense_vector_{&dense_vector},
group_{group},
group_range_{std::equal_range(std::cbegin(*dense_vector_), std::cend(*dense_vector_), group)} {}

private:
using group_iterator = IdxVector::const_iterator;

IdxVector const* dense_vector_{};
Idx group_{};
std::pair<group_iterator, group_iterator> group_range_{};

friend class boost::iterator_core_access;

auto dereference() const -> iterator {
assert(dense_vector_ != nullptr);
return boost::counting_range(
detail::narrow_cast<Idx>(std::distance(std::cbegin(*dense_vector_), group_range_.first)),
detail::narrow_cast<Idx>(std::distance(std::cbegin(*dense_vector_), group_range_.second)));
}
constexpr auto equal(GroupIterator const& other) const { return group_ == other.group_; }
constexpr auto distance_to(GroupIterator const& other) const { return other.group_ - group_; }

constexpr void increment() { advance(1); }
constexpr void decrement() { advance(-1); }
constexpr void advance(Idx n) {
auto const start = n > 0 ? group_range_.second : std::cbegin(*dense_vector_);
auto const stop = n < 0 ? group_range_.first : std::cend(*dense_vector_);

group_ += n;
group_range_ = std::equal_range(start, stop, group_);
TonyXiang8787 marked this conversation as resolved.
Show resolved Hide resolved
}
};

auto group_iterator(Idx group) const { return GroupIterator{dense_vector_, group}; }

public:
auto size() const { return num_groups_; }
auto begin() const { return group_iterator(Idx{}); }
auto end() const { return group_iterator(size()); }

auto element_size() const { return static_cast<Idx>(dense_vector_.size()); }
auto get_group(Idx element) const { return dense_vector_[element]; }
auto get_element_range(Idx group) const { return *group_iterator(group); }

DenseGroupedIdxVector() = default;
explicit DenseGroupedIdxVector(IdxVector dense_vector, Idx num_groups)
: num_groups_{num_groups}, dense_vector_{std::move(dense_vector)} {
assert(size() >= 0);
assert(element_size() >= 0);
assert(std::is_sorted(std::begin(dense_vector_), std::end(dense_vector_)));
assert(num_groups_ >= (dense_vector_.empty() ? 0 : dense_vector_.back()));
}
DenseGroupedIdxVector(from_sparse_t /* tag */, IdxVector const& sparse_group_elements)
: DenseGroupedIdxVector{detail::sparse_decode(sparse_group_elements),
static_cast<Idx>(sparse_group_elements.size()) - 1} {}
DenseGroupedIdxVector(from_dense_t /* tag */, IdxVector dense_group_elements, Idx num_groups)
: DenseGroupedIdxVector{std::move(dense_group_elements), num_groups} {}

private:
Idx num_groups_;
IdxVector dense_vector_;
};

static_assert(grouped_idx_vector_type<SparseGroupedIdxVector>);
static_assert(grouped_idx_vector_type<DenseGroupedIdxVector>);

inline auto enumerated_zip_sequence(grouped_idx_vector_type auto const& first,
grouped_idx_vector_type auto const&... rest) {
assert(((first.size() == rest.size()) && ...));

auto const indices = boost::counting_range(Idx{}, first.size());

auto const zip_begin =
boost::make_zip_iterator(boost::make_tuple(std::begin(indices), std::begin(first), std::begin(rest)...));
auto const zip_end =
boost::make_zip_iterator(boost::make_tuple(std::end(indices), std::end(first), std::end(rest)...));
return boost::make_iterator_range(zip_begin, zip_end);
}

} // namespace power_grid_model

#endif
1 change: 1 addition & 0 deletions tests/cpp_unit_tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ set(PROJECT_SOURCES
"test_y_bus.cpp"
"test_math_solver.cpp"
"test_topology.cpp"
"test_grouped_index_vector.cpp"
"test_container.cpp"
"test_sparse_mapping.cpp"
"test_meta_data_generation.cpp"
Expand Down