-
Notifications
You must be signed in to change notification settings - Fork 0
Initial CPP-SGL Draft #113
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
base: cpp-sgl-dev
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| #pragma once | ||
|
|
||
| #include "gl/constants.hpp" | ||
| #include "sgl/traits.hpp" | ||
|
|
||
| #include <cstddef> | ||
| #include <limits> | ||
|
|
||
| namespace sgl { | ||
|
|
||
| using gl::initial_id; | ||
| using gl::initial_id_t; | ||
| using gl::initial_id_v; | ||
| using gl::invalid_id; | ||
| using gl::invalid_id_t; | ||
| using gl::invalid_id_v; | ||
|
|
||
| inline constexpr std::size_t default_sketch_width = 64uz; | ||
|
|
||
| inline constexpr std::size_t cache_line_size = 64uz; | ||
|
|
||
| using traits::min_hash_word_size; | ||
|
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should not be defined in both sgl:: and sgl::traits at the same time - choose one |
||
|
|
||
| template <traits::c_weight_type WeightType> | ||
| inline constexpr WeightType sketch_infinity_v = std::numeric_limits<WeightType>::infinity(); | ||
|
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You could also introduce a scetch_infinity_t type and a scetch_infinity constant (same as for invalid_id_t, invalid_id)
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You should also define a scetch_inifity_t type and scetch_infinity constant as for ininitial/invalid_id constants |
||
|
|
||
| [[nodiscard]] constexpr std::size_t align_sketch_stride( | ||
|
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should not be defined in constants.hpp
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should not be defined in constants.hpp |
||
| const std::size_t m, const std::size_t slot_size | ||
| ) noexcept { | ||
| if (m == 0uz or slot_size == 0uz) | ||
| return m; | ||
|
|
||
| const auto slots_per_line = cache_line_size / slot_size; | ||
| if (slots_per_line == 0uz) | ||
| return m; | ||
|
|
||
| return ((m + slots_per_line - 1uz) / slots_per_line) * slots_per_line; | ||
| } | ||
|
|
||
| } // namespace sgl | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| #pragma once | ||
|
|
||
| #include "gl/directional_tags.hpp" | ||
| #include "sgl/traits.hpp" | ||
|
|
||
| namespace sgl { | ||
|
|
||
| using directed_t = gl::directed_t; | ||
|
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Again, we could be just doing |
||
|
|
||
| using undirected_t = gl::undirected_t; | ||
|
|
||
| namespace traits { | ||
|
|
||
| template <typename T> | ||
| concept c_sketch_directional_tag = c_one_of<T, directed_t, undirected_t>; | ||
|
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Such requirement is already defined as c_directional_tag in gl - maybe it could be reused?
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Isn't this the same as c_graph_directional_tag?
|
||
|
|
||
| using gl::traits::c_graph_directional_tag; | ||
|
|
||
| } // namespace traits | ||
|
|
||
| } // namespace sgl | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,135 @@ | ||
| #pragma once | ||
|
|
||
| #include "sgl/traits.hpp" | ||
| #include "sgl/types.hpp" | ||
|
|
||
| #include <algorithm> | ||
| #include <concepts> | ||
| #include <ranges> | ||
| #include <span> | ||
| #include <vector> | ||
|
|
||
| namespace sgl { | ||
|
|
||
| template <traits::c_id_type IdType = default_id_type> | ||
| struct binary_edge_key { | ||
| using id_type = IdType; | ||
| using key_type = homogeneous_pair<id_type>; | ||
|
|
||
| [[nodiscard]] static constexpr key_type make(const id_type u, const id_type v) noexcept { | ||
| return (u <= v) ? key_type{u, v} : key_type{v, u}; | ||
|
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. return std::minmax(u, v)
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. return std::minmax(u, v) |
||
| } | ||
|
|
||
| [[nodiscard]] static constexpr key_type make_directed(const id_type u, const id_type v) noexcept { | ||
|
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this actually work like this?
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should it be like this? |
||
| return make(u, v); | ||
| } | ||
| }; | ||
|
|
||
| template < | ||
| traits::c_id_type IdType = default_id_type, | ||
| std::unsigned_integral OccurrenceType = IdType> | ||
| struct multi_edge_key_value { | ||
|
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should be defined as a type within multi_edge_key or: or something similar
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should be defined as a inner type within the multi_edge_key type: or: |
||
| using id_type = IdType; | ||
| using occurrence_type = OccurrenceType; | ||
|
|
||
| id_type first{}; | ||
| id_type second{}; | ||
| occurrence_type occurrence{}; | ||
|
|
||
| [[nodiscard]] constexpr bool operator==(const multi_edge_key_value&) const noexcept = default; | ||
| }; | ||
|
|
||
| template < | ||
| traits::c_id_type IdType = default_id_type, | ||
| std::unsigned_integral OccurrenceType = IdType> | ||
| struct multi_edge_key { | ||
|
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. multi_edge or multiedge? |
||
| using id_type = IdType; | ||
| using occurrence_type = OccurrenceType; | ||
| using key_type = multi_edge_key_value<IdType, OccurrenceType>; | ||
|
|
||
| [[nodiscard]] static constexpr key_type make( | ||
| const id_type u, const id_type v, const occurrence_type occurrence | ||
| ) noexcept { | ||
| const auto [a, b] = binary_edge_key<id_type>::make(u, v); | ||
| return key_type{a, b, occurrence}; | ||
| } | ||
| }; | ||
|
|
||
| template <traits::c_id_type IdType = default_id_type, typename LabelType = IdType> | ||
| struct labeled_edge_key_value { | ||
|
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same as for multi_edge_key_value
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same as for multiedge |
||
| using id_type = IdType; | ||
| using label_type = LabelType; | ||
|
|
||
| id_type first{}; | ||
| id_type second{}; | ||
| label_type label{}; | ||
|
|
||
| [[nodiscard]] constexpr bool operator==(const labeled_edge_key_value&) const noexcept = default; | ||
| }; | ||
|
|
||
| template <traits::c_id_type IdType = default_id_type, typename LabelType = IdType> | ||
| struct labeled_edge_key { | ||
| using id_type = IdType; | ||
| using label_type = LabelType; | ||
| using key_type = labeled_edge_key_value<IdType, LabelType>; | ||
|
|
||
| [[nodiscard]] static constexpr key_type make( | ||
| const id_type u, const id_type v, const label_type& label | ||
| ) noexcept { | ||
| const auto [a, b] = binary_edge_key<id_type>::make(u, v); | ||
| return key_type{a, b, label}; | ||
| } | ||
| }; | ||
|
|
||
| template <traits::c_id_type IdType = default_id_type> | ||
| struct hyper_edge_key { | ||
|
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. hyperedge |
||
| using id_type = IdType; | ||
| using key_type = std::vector<id_type>; | ||
|
|
||
| [[nodiscard]] static key_type make(std::span<const id_type> endpoints) { | ||
|
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe std::span is a sized range, so this is reduntant |
||
| key_type key(endpoints.begin(), endpoints.end()); | ||
| std::ranges::sort(key); | ||
| key.erase(std::ranges::unique(key).begin(), key.end()); | ||
| return key; | ||
| } | ||
|
|
||
| template <traits::c_sized_range_of<id_type> R> | ||
| [[nodiscard]] static key_type make(const R& endpoints) { | ||
| key_type key; | ||
| key.reserve(std::ranges::size(endpoints)); | ||
| for (const auto id : endpoints) | ||
|
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. All ranges must have a begin() and end() methods so you could just do the same thing as in the span overload Moreover, then you don't need to reserve the space because std::vector will do that for you if the type of the range allows for this. Because of that this function could theoretically take as input any range (including input ranges) Additionally you should define the function paramter as One more thing - stating that a hyperedge has endpoints is a bit incorrect semantically (for undirected hyperedges) |
||
| key.push_back(id); | ||
| std::ranges::sort(key); | ||
| key.erase(std::ranges::unique(key).begin(), key.end()); | ||
| return key; | ||
| } | ||
| }; | ||
|
|
||
| namespace traits { | ||
|
|
||
| template <typename KeyPolicy> | ||
| concept c_edge_key_policy = requires { | ||
| typename KeyPolicy::id_type; | ||
| typename KeyPolicy::key_type; | ||
| requires c_id_type<typename KeyPolicy::id_type>; | ||
| }; | ||
|
|
||
| template <typename KeyPolicy> | ||
| concept c_binary_edge_key_policy = | ||
| c_edge_key_policy<KeyPolicy> and c_instantiation_of<KeyPolicy, binary_edge_key>; | ||
|
|
||
| template <typename KeyPolicy> | ||
| concept c_multi_edge_key_policy = | ||
| c_edge_key_policy<KeyPolicy> and c_instantiation_of<KeyPolicy, multi_edge_key>; | ||
|
|
||
| template <typename KeyPolicy> | ||
| concept c_labeled_edge_key_policy = | ||
| c_edge_key_policy<KeyPolicy> and c_instantiation_of<KeyPolicy, labeled_edge_key>; | ||
|
|
||
| template <typename KeyPolicy> | ||
| concept c_hyper_edge_key_policy = | ||
| c_edge_key_policy<KeyPolicy> and c_instantiation_of<KeyPolicy, hyper_edge_key>; | ||
|
|
||
| } // namespace traits | ||
|
|
||
| } // namespace sgl | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,166 @@ | ||
| #pragma once | ||
|
|
||
| #include "sgl/edge_key.hpp" | ||
| #include "sgl/traits.hpp" | ||
| #include "sgl/types.hpp" | ||
|
|
||
| #include <limits> | ||
| #include <type_traits> | ||
| #include <vector> | ||
|
|
||
| namespace sgl { | ||
|
|
||
| namespace detail { | ||
|
|
||
| template <traits::c_hash_word_type HashWord> | ||
| [[nodiscard]] constexpr HashWord splitmix(HashWord x) noexcept { | ||
| x += static_cast<HashWord>(0x9e3779b97f4a7c15ULL); | ||
| x = (x ^ (x >> 30)) * static_cast<HashWord>(0xbf58476d1ce4e5b9ULL); | ||
| x = (x ^ (x >> 27)) * static_cast<HashWord>(0x94d049bb133111ebULL); | ||
| return x ^ (x >> 31); | ||
| } | ||
|
|
||
| template <traits::c_hash_word_type HashWord> | ||
| [[nodiscard]] constexpr HashWord mix(HashWord a, HashWord b) noexcept { | ||
| return splitmix<HashWord>( | ||
| a + static_cast<HashWord>(0x9e3779b97f4a7c15ULL) + (b << 6) + (b >> 2) | ||
| ); | ||
| } | ||
|
|
||
| template <traits::c_hash_word_type HashWord, traits::c_uniform_type UniformType> | ||
| [[nodiscard]] constexpr UniformType hash_to_unit_interval(HashWord h) noexcept { | ||
| constexpr auto mantissa_bits = static_cast<HashWord>( | ||
| std::numeric_limits<UniformType>::digits > 0 ? std::numeric_limits<UniformType>::digits : 53 | ||
| ); | ||
| constexpr auto shift = static_cast<HashWord>(sizeof(HashWord) * 8) > mantissa_bits | ||
| ? static_cast<HashWord>(sizeof(HashWord) * 8) - mantissa_bits | ||
| : HashWord{0}; | ||
| constexpr auto scale = | ||
| UniformType{1} / static_cast<UniformType>(HashWord{1} << mantissa_bits); | ||
| return static_cast<UniformType>((h >> shift) + HashWord{1}) * scale; | ||
| } | ||
|
|
||
| template <traits::c_hash_word_type HashWord, traits::c_id_type IdType> | ||
| [[nodiscard]] constexpr HashWord hash_id(IdType id) noexcept { | ||
| return splitmix<HashWord>(static_cast<HashWord>(id)); | ||
| } | ||
|
|
||
| template <traits::c_hash_word_type HashWord, typename T> | ||
| [[nodiscard]] constexpr HashWord hash_value(const T& value) noexcept { | ||
| if constexpr (std::unsigned_integral<T>) { | ||
|
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. you can collapse the signed/unsigned cases to just one branch: |
||
| return splitmix<HashWord>(static_cast<HashWord>(value)); | ||
| } | ||
| else if constexpr (std::signed_integral<T>) { | ||
| return splitmix<HashWord>(static_cast<HashWord>(value)); | ||
| } | ||
| else { | ||
| HashWord h{}; | ||
| const auto* bytes = reinterpret_cast<const unsigned char*>(&value); | ||
|
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. reinterpret_cast is unsafe -> use std::bit_cast |
||
| for (size_type i = 0uz; i < sizeof(T); ++i) | ||
| h = mix<HashWord>(h, static_cast<HashWord>(bytes[i])); | ||
| return h; | ||
| } | ||
| } | ||
|
|
||
| } // namespace detail | ||
|
|
||
| template < | ||
| traits::c_hash_word_type HashWord = default_hash_word_type, | ||
| traits::c_uniform_type UniformType = default_uniform_type> | ||
| struct basic_edge_hash { | ||
| using hash_word_type = HashWord; | ||
| using seed_type = HashWord; | ||
| using uniform_type = UniformType; | ||
|
|
||
| template <traits::c_id_type IdType> | ||
| [[nodiscard]] static constexpr seed_type seed(const homogeneous_pair<IdType>& key) noexcept { | ||
| return detail::mix<hash_word_type>( | ||
| detail::hash_id<hash_word_type>(key.first), | ||
| detail::hash_id<hash_word_type>(key.second) | ||
| ); | ||
| } | ||
|
|
||
| template <traits::c_id_type IdType> | ||
| [[nodiscard]] static constexpr uniform_type uniform( | ||
| const homogeneous_pair<IdType>& key, const size_type k | ||
| ) noexcept { | ||
| return detail::hash_to_unit_interval<hash_word_type, uniform_type>( | ||
| detail::mix<hash_word_type>(seed(key), static_cast<hash_word_type>(k)) | ||
| ); | ||
| } | ||
|
|
||
| template <traits::c_id_type IdType, std::unsigned_integral Occ> | ||
| [[nodiscard]] static constexpr seed_type seed( | ||
| const multi_edge_key_value<IdType, Occ>& key | ||
| ) noexcept { | ||
| return detail::mix<hash_word_type>( | ||
| seed(homogeneous_pair<IdType>{key.first, key.second}), | ||
| detail::hash_value<hash_word_type>(key.occurrence) | ||
| ); | ||
| } | ||
|
|
||
| template <traits::c_id_type IdType, std::unsigned_integral Occ> | ||
| [[nodiscard]] static constexpr uniform_type uniform( | ||
| const multi_edge_key_value<IdType, Occ>& key, const size_type k | ||
| ) noexcept { | ||
| return detail::hash_to_unit_interval<hash_word_type, uniform_type>( | ||
| detail::mix<hash_word_type>(seed(key), static_cast<hash_word_type>(k)) | ||
| ); | ||
| } | ||
|
|
||
| template <traits::c_id_type IdType, typename LabelType> | ||
| [[nodiscard]] static seed_type seed( | ||
| const labeled_edge_key_value<IdType, LabelType>& key | ||
| ) noexcept { | ||
| return detail::mix<hash_word_type>( | ||
| seed(homogeneous_pair<IdType>{key.first, key.second}), | ||
| detail::hash_value<hash_word_type>(key.label) | ||
| ); | ||
| } | ||
|
|
||
| template <traits::c_id_type IdType, typename LabelType> | ||
| [[nodiscard]] static uniform_type uniform( | ||
| const labeled_edge_key_value<IdType, LabelType>& key, const size_type k | ||
| ) noexcept { | ||
| return detail::hash_to_unit_interval<hash_word_type, uniform_type>( | ||
| detail::mix<hash_word_type>(seed(key), static_cast<hash_word_type>(k)) | ||
| ); | ||
| } | ||
|
|
||
| template <traits::c_id_type IdType> | ||
| [[nodiscard]] static seed_type seed(const std::vector<IdType>& key) noexcept { | ||
| seed_type h = static_cast<seed_type>(0x9e3779b97f4a7c15ULL); | ||
| for (const auto id : key) | ||
| h = detail::mix<hash_word_type>(h, detail::hash_id<hash_word_type>(id)); | ||
| return h; | ||
| } | ||
|
|
||
| template <traits::c_id_type IdType> | ||
| [[nodiscard]] static uniform_type uniform( | ||
| const std::vector<IdType>& key, const size_type k | ||
| ) noexcept { | ||
| return detail::hash_to_unit_interval<hash_word_type, uniform_type>( | ||
| detail::mix<hash_word_type>(seed(key), static_cast<hash_word_type>(k)) | ||
| ); | ||
| } | ||
| }; | ||
|
|
||
| using default_edge_hash = basic_edge_hash<>; | ||
|
|
||
| namespace traits { | ||
|
|
||
| template <typename HashPolicy> | ||
| concept c_edge_hash_policy = requires( | ||
| const homogeneous_pair<default_id_type>& key, size_type k | ||
|
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This probably should not be using default_id_type but accept an IdType as a template parameter and validate against it: |
||
| ) { | ||
| typename HashPolicy::seed_type; | ||
| typename HashPolicy::uniform_type; | ||
| requires c_hash_word_type<typename HashPolicy::seed_type>; | ||
| requires c_uniform_type<typename HashPolicy::uniform_type>; | ||
| { HashPolicy::seed(key) } -> std::convertible_to<typename HashPolicy::seed_type>; | ||
| { HashPolicy::uniform(key, k) } -> std::convertible_to<typename HashPolicy::uniform_type>; | ||
| }; | ||
|
|
||
| } // namespace traits | ||
|
|
||
| } // namespace sgl | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be platfrom-dependent (see https://en.cppreference.com/cpp/thread/hardware_destructive_interference_size)