diff --git a/SeQuant/core/algorithm.hpp b/SeQuant/core/algorithm.hpp index 6c2bd5ba9e..ddc2867eab 100644 --- a/SeQuant/core/algorithm.hpp +++ b/SeQuant/core/algorithm.hpp @@ -138,34 +138,6 @@ bool next_permutation_parity(int& parity, BidirIt first, BidirIt last, } } -/// -/// Given a size of a container @c n, returns a range of bitsets. The bitsets -/// represent the sieve that can be used to construct the subsequences of the -/// size-n container. -/// -inline auto subset_indices(size_t n) { - using ranges::views::iota; - using ranges::views::transform; - return iota(size_t{1}, size_t(1 << n)) | - transform([n](auto i) { return boost::dynamic_bitset<>(n, i); }); -} - -/// -/// Given an iterable @c it and a bitset @c bs, select the elements in the -/// iterable that correspond to an 'on' bit. -/// -template -auto subsequence(Iterable const& it, boost::dynamic_bitset<> const& bs) { - using ranges::views::filter; - using ranges::views::iota; - using ranges::views::transform; - using ranges::views::zip; - - auto bits = iota(size_t{0}) | transform([&bs](auto i) { return bs.at(i); }); - return zip(it, bits) | filter([](auto&& kv) { return std::get<1>(kv); }) | - transform([](auto&& kv) { return std::get<0>(kv); }); -} - /// /// All elements in the vector belong to the integral range [-1,N) /// where N is the length of the [Expr] (ie. the iterable of expressions) diff --git a/SeQuant/core/meta.hpp b/SeQuant/core/meta.hpp index 23b20f76bb..e1053992f0 100644 --- a/SeQuant/core/meta.hpp +++ b/SeQuant/core/meta.hpp @@ -9,6 +9,7 @@ #include #include #include +#include #include namespace sequant { @@ -506,6 +507,23 @@ struct std_array_size> template constexpr inline std::size_t std_array_size_v = std_array_size::value; +/// +/// True if @tparam Rng is a range whose value type is convertible +/// to @tparam V . +/// +template +concept range_of = std::ranges::range && + std::is_convertible_v, V>; + +/// +/// True if @tparam Rng is a range whose value type is convertible +/// to @tparam V and the size (std::ranges::distance) is known in constant time. +/// +/// @see https://en.cppreference.com/w/cpp/ranges/sized_range.html +/// +template +concept sized_range_of = std::ranges::sized_range && range_of; + } // namespace meta } // namespace sequant diff --git a/SeQuant/core/utility/indices.hpp b/SeQuant/core/utility/indices.hpp index f564885e9a..62d8017784 100644 --- a/SeQuant/core/utility/indices.hpp +++ b/SeQuant/core/utility/indices.hpp @@ -566,6 +566,46 @@ auto get_ket_idx(T&& container) } } +auto subexpr_index_counts(meta::sized_range_of auto const& tnsrs) { + size_t const N = ranges::distance(tnsrs); + container::vector> + result((1 << N)); + for (auto i = 1; i < result.size(); ++i) { + auto bs = boost::dynamic_bitset<>(N, i); + for (auto b = 0; b < N; ++b) + if (bs[b]) + for (auto&& ix : ranges::at(tnsrs, b).indices()) + if (auto [it, yn] = result[i].try_emplace(ix, 1); !yn) // + ++(it->second); + } + return result; +} + +template Rng, meta::range_of Ixs> +auto subexpr_target_indices(Rng const& tnsrs, Ixs const& tixs) { + using IndexSet = container::set; + size_t const N = ranges::distance(tnsrs); + container::vector result((1 << N)); + + if (result.empty()) return result; + + for (auto i = 0; i < N; ++i) { + auto&& ixs = ranges::at(tnsrs, i).indices(); + result[(1 << i)] = IndexSet(ranges::begin(ixs), ranges::end(ixs)); + } + + *result.rbegin() = IndexSet(ranges::begin(tixs), ranges::end(tixs)); + + auto counts = subexpr_index_counts(tnsrs); + + for (auto i = 0; i < result.size(); ++i) + for (auto&& [k, v] : counts[i]) + if (v == 1 || (v > 0 && counts.at(counts.size() - i - 1).contains(k))) + result[i].emplace(k); + + return result; +} + } // namespace sequant #endif // SEQUANT_CORE_UTILITY_INDICES_HPP