From 0931c3dd2f05e4acd510abc7df3453d8bd594934 Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Thu, 16 Jan 2025 13:06:20 +0100 Subject: [PATCH] Extract VertexPainter implementation --- CMakeLists.txt | 2 + SeQuant/core/tensor_network_v2.cpp | 210 +---------------------------- SeQuant/core/vertex_painter.cpp | 159 ++++++++++++++++++++++ SeQuant/core/vertex_painter.hpp | 106 +++++++++++++++ 4 files changed, 268 insertions(+), 209 deletions(-) create mode 100644 SeQuant/core/vertex_painter.cpp create mode 100644 SeQuant/core/vertex_painter.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index ee759aca1..5ab939237 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -253,6 +253,8 @@ set(SeQuant_src SeQuant/core/utility/singleton.hpp SeQuant/core/utility/string.hpp SeQuant/core/utility/string.cpp + SeQuant/core/vertex_painter.cpp + SeQuant/core/vertex_painter.hpp SeQuant/core/wick.hpp SeQuant/core/wick.impl.hpp SeQuant/core/wolfram.hpp diff --git a/SeQuant/core/tensor_network_v2.cpp b/SeQuant/core/tensor_network_v2.cpp index bdd4a4808..047f78cf0 100644 --- a/SeQuant/core/tensor_network_v2.cpp +++ b/SeQuant/core/tensor_network_v2.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -639,215 +640,6 @@ ExprPtr TensorNetworkV2::canonicalize( return (byproduct->as().value() == 1) ? nullptr : byproduct; } -using ProtoBundle = - std::decay_t().proto_indices())>; - -struct BraGroup { - explicit BraGroup(std::size_t id) : id(id) {} - - std::size_t id; -}; -struct KetGroup { - explicit KetGroup(std::size_t id) : id(id) {} - - std::size_t id; -}; -struct AuxGroup { - explicit AuxGroup(std::size_t id) : id(id) {} - - std::size_t id; -}; -struct ParticleGroup { - explicit ParticleGroup(std::size_t id) : id(id) {} - - std::size_t id; -}; - -class VertexPainter { - public: - using Color = TensorNetworkV2::Graph::VertexColor; - using VertexData = - std::variant; - using ColorMap = container::map; - - VertexPainter(const TensorNetworkV2::NamedIndexSet &named_indices) - : used_colors_(), named_indices_(named_indices) {} - - const ColorMap &used_colors() const { return used_colors_; } - - Color operator()(const AbstractTensor &tensor) { - Color color = to_color(hash::value(label(tensor))); - - return ensure_uniqueness(color, tensor); - } - - Color operator()(const BraGroup &group) { - Color color = to_color(group.id + 0xff); - - return ensure_uniqueness(color, group); - } - - Color operator()(const KetGroup &group) { - Color color = to_color(group.id + 0xff00); - - return ensure_uniqueness(color, group); - } - - Color operator()(const AuxGroup &group) { - Color color = to_color(group.id + 3 * 0xff0000); - - return ensure_uniqueness(color, group); - } - - Color operator()(const ParticleGroup &group) { - Color color = to_color(group.id); - - return ensure_uniqueness(color, group); - } - - Color operator()(const Index &idx) { - auto it = named_indices_.find(idx); - - // TODO: shift - std::size_t pre_color; - if (it == named_indices_.end()) { - // anonymous index - pre_color = idx.color(); - } else { - pre_color = static_cast( - std::distance(named_indices_.begin(), it)); - } - - return ensure_uniqueness(to_color(pre_color), idx); - } - - Color operator()(const ProtoBundle &bundle) { - Color color = to_color(Index::proto_indices_color(bundle)); - - return ensure_uniqueness(color, bundle); - } - - private: - ColorMap used_colors_; - const TensorNetworkV2::NamedIndexSet &named_indices_; - - Color to_color(std::size_t color) const { - // Due to the way we compute the input color, different colors might only - // differ by a value of 1. This is fine for the algorithmic purpose (after - // all, colors need only be different - by how much is irrelevant), but - // sometimes we'll want to use those colors as actual colors to show to a - // human being. In those cases, having larger differences makes it easier to - // recognize different colors. Therefore, we hash-combined with an - // arbitrarily chosen salt with the goal that this will uniformly spread out - // all input values and therefore increase color differences. - constexpr std::size_t salt = 0x43d2c59cb15b73f0; - hash::combine(color, salt); - - if constexpr (sizeof(Color) >= sizeof(std::size_t)) { - return color; - } - - // Need to somehow fit the color into a lower precision integer. In the - // general case, this is necessarily a lossy conversion. We make the - // assumption that the input color is - // - a hash, or - // - computed from some object ID - // In the first case, we assume that the used hash function has a uniform - // distribution or if there is a bias, the bias is towards lower numbers. - // This allows us to simply reuse the lower x bits of the hash as a new hash - // (where x == CHAR_BIT * sizeof(VertexColor)). In the second case we assume - // that such values never exceed the possible value range of VertexColor so - // that again, we can simply take the lower x bits of color and in this case - // even retain the numeric value representing the color. Handily, this is - // exactly what happens when we perform a conversion into a narrower type. - // We only have to make sure that the underlying types are unsigned as - // otherwise the behavior is undefined. - static_assert(sizeof(Color) < sizeof(std::size_t)); - static_assert(std::is_unsigned_v, - "Narrowing conversion are undefined for signed integers"); - static_assert(std::is_unsigned_v, - "Narrowing conversion are undefined for signed integers"); - return static_cast(color); - } - - template - Color ensure_uniqueness(Color color, const T &val) { - auto it = used_colors_.find(color); - while (it != used_colors_.end() && !may_have_same_color(it->second, val)) { - // Color collision: val was computed to have the same color - // as another object, but these objects do not compare equal (for - // the purpose of color assigning). - // -> Need to modify color until conflict is resolved. - color++; - it = used_colors_.find(color); - } - - if (it == used_colors_.end()) { - // We have not yet seen this color before -> add it to cache - if constexpr (std::is_same_v || - std::is_same_v) { - used_colors_[color] = &val; - } else { - used_colors_[color] = val; - } - } - - return color; - } - - bool may_have_same_color(const VertexData &data, - const AbstractTensor &tensor) { - return std::holds_alternative(data) && - label(*std::get(data)) == label(tensor); - } - - bool may_have_same_color(const VertexData &data, const BraGroup &group) { - return std::holds_alternative(data) && - std::get(data).id == group.id; - } - - bool may_have_same_color(const VertexData &data, const KetGroup &group) { - return std::holds_alternative(data) && - std::get(data).id == group.id; - } - - bool may_have_same_color(const VertexData &data, const AuxGroup &group) { - return std::holds_alternative(data) && - std::get(data).id == group.id; - } - - bool may_have_same_color(const VertexData &data, const ParticleGroup &group) { - return std::holds_alternative(data) && - std::get(data).id == group.id; - } - - bool may_have_same_color(const VertexData &data, const Index &idx) { - if (!std::holds_alternative(data)) { - return false; - } - - const Index &lhs = std::get(data); - - auto it1 = named_indices_.find(lhs); - auto it2 = named_indices_.find(idx); - - if (it1 != it2) { - // Either one index is named and the other is not or both are named, but - // are different indices - return false; - } - - return lhs.color() == idx.color(); - } - - bool may_have_same_color(const VertexData &data, const ProtoBundle &bundle) { - return std::holds_alternative(data) && - Index::proto_indices_color(*std::get(data)) == - Index::proto_indices_color(bundle); - } -}; - TensorNetworkV2::Graph TensorNetworkV2::create_graph( const NamedIndexSet *named_indices_ptr) const { assert(have_edges_); diff --git a/SeQuant/core/vertex_painter.cpp b/SeQuant/core/vertex_painter.cpp new file mode 100644 index 000000000..06b23cce2 --- /dev/null +++ b/SeQuant/core/vertex_painter.cpp @@ -0,0 +1,159 @@ +#include +#include + +namespace sequant { + +VertexPainter::VertexPainter( + const TensorNetworkV2::NamedIndexSet &named_indices) + : used_colors_(), named_indices_(named_indices) {} + +VertexPainter::Color VertexPainter::operator()(const AbstractTensor &tensor) { + Color color = to_color(hash::value(label(tensor))); + + return ensure_uniqueness(color, tensor); +} + +VertexPainter::Color VertexPainter::operator()(const BraGroup &group) { + Color color = to_color(group.id + 0xff); + + return ensure_uniqueness(color, group); +} + +VertexPainter::Color VertexPainter::operator()(const KetGroup &group) { + Color color = to_color(group.id + 0xff00); + + return ensure_uniqueness(color, group); +} + +VertexPainter::Color VertexPainter::operator()(const AuxGroup &group) { + Color color = to_color(group.id + 3 * 0xff0000); + + return ensure_uniqueness(color, group); +} + +VertexPainter::Color VertexPainter::operator()(const ParticleGroup &group) { + Color color = to_color(group.id); + + return ensure_uniqueness(color, group); +} + +VertexPainter::Color VertexPainter::operator()(const Index &idx) { + auto it = named_indices_.find(idx); + + std::size_t pre_color; + if (it == named_indices_.end()) { + // anonymous index + pre_color = idx.color(); + } else { + pre_color = static_cast( + std::distance(named_indices_.begin(), it)); + } + // shift + pre_color += 0xaa; + + return ensure_uniqueness(to_color(pre_color), idx); +} + +VertexPainter::Color VertexPainter::operator()(const ProtoBundle &bundle) { + Color color = to_color(Index::proto_indices_color(bundle)); + + return ensure_uniqueness(color, bundle); +} + +VertexPainter::Color VertexPainter::to_color(std::size_t color) const { + // Due to the way we compute the input color, different colors might only + // differ by a value of 1. This is fine for the algorithmic purpose (after + // all, colors need only be different - by how much is irrelevant), but + // sometimes we'll want to use those colors as actual colors to show to a + // human being. In those cases, having larger differences makes it easier to + // recognize different colors. Therefore, we hash-combine with an + // arbitrarily chosen salt with the goal that this will uniformly spread out + // all input values and therefore increase color differences. + constexpr std::size_t salt = 0x43d2c59cb15b73f0; + hash::combine(color, salt); + + if constexpr (sizeof(Color) >= sizeof(std::size_t)) { + return color; + } + + // Need to somehow fit the color into a lower precision integer. In the + // general case, this is necessarily a lossy conversion. We make the + // assumption that the input color is + // - a hash, or + // - computed from some object ID + // In the first case, we assume that the used hash function has a uniform + // distribution or if there is a bias, the bias is towards lower numbers. + // This allows us to simply reuse the lower x bits of the hash as a new hash + // (where x == CHAR_BIT * sizeof(VertexColor)). In the second case we assume + // that such values never exceed the possible value range of VertexColor so + // that again, we can simply take the lower x bits of color and in this case + // even retain the numeric value representing the color. Handily, this is + // exactly what happens when we perform a conversion into a narrower type. + // We only have to make sure that the underlying types are unsigned as + // otherwise the behavior is undefined. + static_assert(sizeof(Color) < sizeof(std::size_t)); + static_assert(std::is_unsigned_v, + "Narrowing conversion are undefined for signed integers"); + static_assert(std::is_unsigned_v, + "Narrowing conversion are undefined for signed integers"); + return static_cast(color); +} + +bool VertexPainter::may_have_same_color(const VertexData &data, + const AbstractTensor &tensor) { + return std::holds_alternative(data) && + label(*std::get(data)) == label(tensor); +} + +bool VertexPainter::may_have_same_color(const VertexData &data, + const BraGroup &group) { + return std::holds_alternative(data) && + std::get(data).id == group.id; +} + +bool VertexPainter::may_have_same_color(const VertexData &data, + const KetGroup &group) { + return std::holds_alternative(data) && + std::get(data).id == group.id; +} + +bool VertexPainter::may_have_same_color(const VertexData &data, + const AuxGroup &group) { + return std::holds_alternative(data) && + std::get(data).id == group.id; +} + +bool VertexPainter::may_have_same_color(const VertexData &data, + const ParticleGroup &group) { + return std::holds_alternative(data) && + std::get(data).id == group.id; +} + +bool VertexPainter::may_have_same_color(const VertexData &data, + const Index &idx) { + if (!std::holds_alternative(data)) { + return false; + } + + const Index &lhs = std::get(data); + + auto it1 = named_indices_.find(lhs); + auto it2 = named_indices_.find(idx); + + if (it1 != it2) { + // Either one index is named and the other is not or both are named, but + // are different indices + return false; + } + + return lhs.color() == idx.color(); +} + +bool VertexPainter::may_have_same_color(const VertexData &data, + const ProtoBundle &bundle) { + return std::holds_alternative(data) && + Index::proto_indices_color(*std::get(data)) == + Index::proto_indices_color(bundle); +} + +} // namespace sequant diff --git a/SeQuant/core/vertex_painter.hpp b/SeQuant/core/vertex_painter.hpp new file mode 100644 index 000000000..896285707 --- /dev/null +++ b/SeQuant/core/vertex_painter.hpp @@ -0,0 +1,106 @@ +#ifndef SEQUANT_VERTEX_PAINTER_H +#define SEQUANT_VERTEX_PAINTER_H + +#include +#include +#include +#include + +#include +#include + +namespace sequant { + +using ProtoBundle = + std::decay_t().proto_indices())>; + +struct BraGroup { + explicit BraGroup(std::size_t id) : id(id) {} + + std::size_t id; +}; +struct KetGroup { + explicit KetGroup(std::size_t id) : id(id) {} + + std::size_t id; +}; +struct AuxGroup { + explicit AuxGroup(std::size_t id) : id(id) {} + + std::size_t id; +}; +struct ParticleGroup { + explicit ParticleGroup(std::size_t id) : id(id) {} + + std::size_t id; +}; + +/// Can be used to assign unique colors to a set of objects. The class +/// automatically ensures that there are no accidental color duplications for +/// objects that actually should have different colors (i.e. this is more than a +/// hash function). It is intended to be used to determine the vertex colors in +/// a colored graph representing a tensor network. +class VertexPainter { + public: + using Color = TensorNetworkV2::Graph::VertexColor; + using VertexData = + std::variant; + using ColorMap = container::map; + + VertexPainter(const TensorNetworkV2::NamedIndexSet &named_indices); + + const ColorMap &used_colors() const; + + Color operator()(const AbstractTensor &tensor); + Color operator()(const BraGroup &group); + Color operator()(const KetGroup &group); + Color operator()(const AuxGroup &group); + Color operator()(const ParticleGroup &group); + Color operator()(const Index &idx); + Color operator()(const ProtoBundle &bundle); + + private: + ColorMap used_colors_; + const TensorNetworkV2::NamedIndexSet &named_indices_; + + Color to_color(std::size_t color) const; + + template + Color ensure_uniqueness(Color color, const T &val) { + auto it = used_colors_.find(color); + while (it != used_colors_.end() && !may_have_same_color(it->second, val)) { + // Color collision: val was computed to have the same color + // as another object, but these objects do not compare equal (for + // the purpose of color assigning). + // -> Need to modify color until conflict is resolved. + color++; + it = used_colors_.find(color); + } + + if (it == used_colors_.end()) { + // We have not yet seen this color before -> add it to cache + if constexpr (std::is_same_v || + std::is_same_v) { + used_colors_[color] = &val; + } else { + used_colors_[color] = val; + } + } + + return color; + } + + bool may_have_same_color(const VertexData &data, + const AbstractTensor &tensor); + bool may_have_same_color(const VertexData &data, const BraGroup &group); + bool may_have_same_color(const VertexData &data, const KetGroup &group); + bool may_have_same_color(const VertexData &data, const AuxGroup &group); + bool may_have_same_color(const VertexData &data, const ParticleGroup &group); + bool may_have_same_color(const VertexData &data, const Index &idx); + bool may_have_same_color(const VertexData &data, const ProtoBundle &bundle); +}; + +} // namespace sequant + +#endif