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

[ENH] Augment coiteration algorithm to handle hashed level during conjunctive merge #19

Closed
wants to merge 32 commits into from
Closed
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
8714270
WIP example of compareVector
adam2392 Mar 23, 2023
652d8b0
Adding the unit test for coiteration that should work
adam2392 Apr 3, 2023
bcbcbe2
Clean diff
adam2392 Apr 3, 2023
462dbb0
Update test/source/coiteration_test.cpp
adam2392 Apr 8, 2023
7cc42f7
Update test/source/coiteration_test.cpp
adam2392 Apr 8, 2023
1969843
Fix unit test based on comments
adam2392 May 25, 2023
a9dc754
Merge branch 'comparevec' of github.com:adam2392/xsparse into comparevec
adam2392 May 25, 2023
0c91a0d
Fix unit test based on comments
adam2392 May 25, 2023
0a35239
Attempt at the right answer
adam2392 May 25, 2023
8d97006
Clean
adam2392 May 25, 2023
c0bc015
I think this enables us to store the ordered indices, but does not ye…
adam2392 May 26, 2023
fc08789
Fix docstring and clean up unit-test
adam2392 May 30, 2023
eb63b3c
Fix ci and doctest errors
adam2392 May 30, 2023
0420df3
Merge branch 'main' into comparevec
adam2392 May 30, 2023
828092a
Add levelproperty to all the levels
adam2392 May 31, 2023
b5ec7ef
Merge branch 'main' into levelprops
adam2392 May 31, 2023
aa56276
Try again
adam2392 May 31, 2023
29f4f7f
Adding unit tests for level property
adam2392 May 31, 2023
3e0afc4
Clean diff
adam2392 May 31, 2023
b63b67c
Only check strict property requirements
adam2392 May 31, 2023
2ac6d71
Fixed unit-tests for dense ordered
adam2392 May 31, 2023
5e72277
Level to lower-case
adam2392 Jun 1, 2023
25c5c2e
Merging level props
adam2392 Jun 1, 2023
ddaf8d2
Merge branch 'levelprops' into comparevec
adam2392 Jun 1, 2023
f77dd0d
Trying again
adam2392 Jun 1, 2023
6a73b73
Fix new design
adam2392 Jun 5, 2023
1a6d711
Merge branch 'levelprops' into comparevec
adam2392 Jun 5, 2023
8690b48
Fix co-iteration and just get initial design template for getting tup…
adam2392 Jun 5, 2023
a379e0b
Trying
adam2392 Jun 5, 2023
89ee47d
Merge branch 'main' into comparevec
adam2392 Jun 6, 2023
f7d0ed1
Merge main
adam2392 Jun 6, 2023
7730adb
Kind of wip
adam2392 Jun 7, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions include/xsparse/level_capabilities/co_iteration.hpp
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file contains my initial attempt at: adding a constexpr check at compile time to determine if the coiteration is valid (this assumes that the user converts non-ordered, non-locatable formats to a correct format).

What I'm not sure on now is how to best implement the locate logic. That is, we want to somehow given a tuple of levels, determine which of these have locate and instead of advancing the iterator on those levels, we locate into them given a index and pointer.

Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,40 @@
#include <algorithm>
#include <stdexcept>

#include <xsparse/level_capabilities/locate.hpp>

namespace xsparse::level_capabilities
{
/*
The class template for Coiteration of level formats.

Uses a generic function object F to compare elements
from different sequences at the same position and returns a tuple of the
minimum index and the corresponding elements from each sequence.

Parameters
----------
F : class
A function object that is used to compare two elements from different ranges.
IK : class
The type of the first element of each range.
PK : class
The type of the second element of each range.
Levels : Tuple of class
A tuple of level formats, where each level is itself a tuple of elements to be iterated.
Is : Tuple of class
A tuple of indices that is used to keep track of the current position in each level.

Notes
-----
Coiteration is only allowed through tuples of levels if the following criterion is met:
If:
1. the levels are all ordered (i.e. has the `is_ordered == True` property)
2. if any of the level are do not have the is_ordered property, it must have the locate
function, else return False. Then do a check that `m_comparisonHelper` defines
a conjunctive merge (i.e. AND operation).
Otherwise, coiteration is not allowed.
*/
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is great! However, I'd recommend Doxygen-style docstrings.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, I changed the docstring to this style: https://www.doxygen.nl/manual/docblocks.html. Not as familiar w/ Doxygen, so lmk if you think I did anything wrong here.

template <class F, class IK, class PK, class Levels, class Is>
class Coiterate;

Expand All @@ -28,6 +60,27 @@ namespace xsparse::level_capabilities
{
throw std::invalid_argument("level sizes should be same");
}

if (!meet_criteria(f, levels...))
{
throw std::invalid_argument("levels do not meet coiteration criteria");
}
}

// Check if the levels meet the criteria.
static constexpr bool meet_criteria(F f, Levels&... levels)
{
// check that all the levels are ordered
constexpr bool all_ordered = std::conjunction_v<levels.is_ordered...>;

// check if any has `locate` function
constexpr bool any_locate = std::disjunction_v<has_locate_v<levels...>...>;

// check function for coiteration comparison is conjunctive
constexpr bool is_conjunctive_merge
= std::is_same_v<bool, decltype(f(std::declval<bool>(), std::declval<bool>()))>;

return all_ordered || (any_locate && is_conjunctive_merge);
}

public:
Expand Down
1 change: 1 addition & 0 deletions include/xsparse/level_capabilities/coordinate_iterate.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ namespace xsparse::level_capabilities
};

iteration_helper iter_helper(typename BaseTraits::I i, typename BaseTraits::PKM1 pkm1)
/*Create an instance of iteration_helper that manages the iteration process.*/
{
return iteration_helper{ *static_cast<typename BaseTraits::Level*>(this), i, pkm1 };
}
Expand Down
95 changes: 85 additions & 10 deletions test/source/coiteration_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@
#include <tuple>
#include <vector>
#include <functional>
#include <unordered_set>
#include <unordered_map>

#include <xsparse/levels/compressed.hpp>
#include <xsparse/levels/dense.hpp>
#include <xsparse/levels/singleton.hpp>
#include <xsparse/levels/hashed.hpp>
#include <xsparse/version.h>

#include <xsparse/util/container_traits.hpp>
Expand All @@ -21,10 +24,7 @@ TEST_CASE("Coiteration-Dense-Dense")
xsparse::levels::dense<std::tuple<>, uintptr_t, uintptr_t> s1{ 5 };
xsparse::levels::dense<std::tuple<>, uintptr_t, uintptr_t> s2{ 5 };

auto fn = [](std::tuple<bool, bool> t) constexpr
{
return std::get<0>(t) && std::get<1>(t);
};
auto fn = [](std::tuple<bool, bool> t) constexpr { return std::get<0>(t) && std::get<1>(t); };

xsparse::level_capabilities::Coiterate<std::function<bool(std::tuple<bool, bool>)>,
uintptr_t,
Expand Down Expand Up @@ -70,9 +70,7 @@ TEST_CASE("Coiteration-Dense-Dense-Dense")
xsparse::levels::dense<std::tuple<>, uintptr_t, uintptr_t> s3{ 5 };

auto fn = [](std::tuple<bool, bool, bool> t) constexpr
{
return (std::get<0>(t) && std::get<1>(t)) || (std::get<2>(t));
};
{ return (std::get<0>(t) && std::get<1>(t)) || (std::get<2>(t)); };

xsparse::level_capabilities::Coiterate<std::function<bool(std::tuple<bool, bool, bool>)>,
uintptr_t,
Expand Down Expand Up @@ -132,9 +130,7 @@ TEST_CASE("Coiteration-Singleton-Singleton-Dense-Dense")
xsparse::levels::dense<std::tuple<>, uintptr_t, uintptr_t> s4{ 5 };

auto fn = [](std::tuple<bool, bool, bool, bool> t) constexpr
{
return (std::get<0>(t) || std::get<2>(t)) || (std::get<1>(t) || std::get<3>(t));
};
{ return (std::get<0>(t) || std::get<2>(t)) || (std::get<1>(t) || std::get<3>(t)); };

xsparse::level_capabilities::Coiterate<
std::function<bool(std::tuple<bool, bool, bool, bool>)>,
Expand Down Expand Up @@ -193,3 +189,82 @@ TEST_CASE("Coiteration-Singleton-Singleton-Dense-Dense")
}
CHECK(fn(std::tuple(it1 == end1, it2 == end2, it3 == end3, it4 == end4)) == true);
}

TEST_CASE("Coiteration-Dense-Hashed-ConjunctiveMerge")
{
/* Test coiteration for dense and hashed formats.

A conjunctive merge requires coiterating over a dense and hashed format. This
test checks that the coiteration is done correctly. The test proceeds as follows:

- If one of the levels is unordered (e.g. hashed, or singleton), then the
coiteration is done by iterating over the ordered level, and then looking up
the corresponding value in the unordered level.
- The coiteration stops when the end of the ordered (i.e. dense) level is reached.

This test checks that the lookup is done correctly.
*/
constexpr uint8_t ZERO = 0;

std::unordered_map<uintptr_t, uintptr_t> const umap1{ { 0, 1 }, { 2, 0 }, { 1, 2 } };
std::vector<std::unordered_map<uintptr_t, uintptr_t>> const crd0{ umap1 };

// initialize the two levels to be coiterated
xsparse::levels::dense<std::tuple<>, uintptr_t, uintptr_t> dense_level{ 5 };
xsparse::levels::hashed<
std::tuple<>,
uintptr_t,
uintptr_t,
xsparse::util::container_traits<std::vector, std::unordered_set, std::unordered_map>,
xsparse::level_properties<false, false, false, false, false>>
hash_level{ 5, crd0 };

// define a conjunctive function
auto fn = [](std::tuple<bool, bool> t) constexpr { return (std::get<0>(t) && std::get<1>(t)); };

xsparse::level_capabilities::Coiterate<std::function<bool(std::tuple<bool, bool>)>,
uintptr_t,
uintptr_t,
std::tuple<decltype(dense_level), decltype(hash_level)>,
std::tuple<>>
coiter(fn, dense_level, hash_level);

// define iteration helper through dense and hashed level
auto it_helper1 = dense_level.iter_helper(std::make_tuple(), ZERO);
auto it_helper2 = hash_level.iter_helper(ZERO);
adam2392 marked this conversation as resolved.
Show resolved Hide resolved

// initialize iterators for dense and hashed level
auto it1 = it_helper1.begin();
auto it2 = it_helper2.begin();
auto end1 = it_helper1.end();
auto end2 = it_helper2.end();

// when co-iterating over levels that are unordered (i.e. hashed), then we use locate to check
// if the index exists in the hashed level. If not, then we skip it.
for (auto const [ik, pk_tuple] : coiter.coiter_helper(std::make_tuple(), ZERO))
{
// get the index and pointer from the levels involved in co-iteration
auto [i1, p1] = *it1;

// should only iterate over the ordered dense level
uintptr_t l = i1;
CHECK(ik == l);

// check that neither level has reached the end
if (it1 != end1)
{
// if both levels have a non-zero value at index "i1", then those values
// should be present in the co-iterated tuple
if (i1 == l && hash_level.locate(p1, i1) != std::nullopt)
{
CHECK(p1 == std::get<0>(pk_tuple).value());
}

// increment through the dense level always
++it1;
}
}

// check that the dense level should've reached its end
CHECK((it1 == end1) == true);
}