|
1 | 1 | #ifndef SEQUANT_CORE_OPTIMIZE_SINGLE_TERM_HPP |
2 | 2 | #define SEQUANT_CORE_OPTIMIZE_SINGLE_TERM_HPP |
3 | 3 |
|
| 4 | +#include <SeQuant/core/algorithm.hpp> |
4 | 5 | #include <SeQuant/core/container.hpp> |
5 | 6 | #include <SeQuant/core/expr.hpp> |
| 7 | +#include <SeQuant/core/tensor_canonicalizer.hpp> |
6 | 8 | #include <SeQuant/core/tensor_network.hpp> |
7 | 9 | #include <SeQuant/core/utility/indices.hpp> |
8 | 10 | #include <SeQuant/core/utility/macros.hpp> |
9 | 11 | #include <SeQuant/external/bliss/graph.hh> |
10 | 12 |
|
11 | 13 | #include <range/v3/view.hpp> |
12 | 14 |
|
13 | | -#include <SeQuant/core/algorithm.hpp> |
14 | | -#include <SeQuant/core/tensor_canonicalizer.hpp> |
15 | 15 | #include <algorithm> |
16 | 16 | #include <bit> |
| 17 | +#include <limits> |
17 | 18 | #include <type_traits> |
18 | 19 |
|
19 | 20 | namespace sequant::opt { |
@@ -87,18 +88,18 @@ struct SubNetEqual { |
87 | 88 | /// \tparam CostFn A function object type that computes the cost of a single |
88 | 89 | /// binary contraction. |
89 | 90 | /// Expected signature: |
90 | | -// \code double(meta::range_of<Index> auto const& lhs, |
91 | | -// meta::range_of<Index> auto const& rhs, |
| 91 | +/// \code double(meta::range_of<Index> auto const& lhs, |
| 92 | +/// meta::range_of<Index> auto const& rhs, |
92 | 93 | /// meta::range_of<Index> auto const& res) |
93 | | -// \endcode |
| 94 | +/// \endcode |
94 | 95 | /// |
95 | 96 | /// \param network The \ref TensorNetwork containing the tensors to be |
96 | 97 | /// contracted. |
97 | | -// \param tidxs The set of indices that should remain open in the |
| 98 | +/// \param tidxs The set of indices that should remain open in the |
98 | 99 | /// final result. |
99 | | -// \param cost_fn The cost model used to evaluate contractions |
| 100 | +/// \param cost_fn The cost model used to evaluate contractions |
100 | 101 | /// (e.g., flop count). |
101 | | -// \param subnet_cse If true, enables Common Subexpression |
| 102 | +/// \param subnet_cse If true, enables Common Subexpression |
102 | 103 | /// Elimination (CSE) for |
103 | 104 | /// equivalent subnetworks. When enabled, the cost of |
104 | 105 | /// evaluating structurally identical subnetworks is counted |
@@ -154,10 +155,14 @@ EvalSequence single_term_opt_impl(TensorNetwork const& network, |
154 | 155 | } |
155 | 156 |
|
156 | 157 | // precompute all subnet_meta if subnet_cse is true |
| 158 | + // Note: the O(2^n) cost is bounded in practice — subset_target_indices above |
| 159 | + // asserts n <= 24, capping the number of subsets at ~16M. |
157 | 160 | container::vector<uint16_t> meta_ids; |
158 | 161 | container::vector<double> unique_meta_costs; |
159 | 162 | if (subnet_cse) { |
160 | | - meta_ids.resize(results.size(), 0); |
| 163 | + // Use max as sentinel for entries with popcount < 2 (singletons/empty), |
| 164 | + // which are skipped below and never assigned a real meta ID. |
| 165 | + meta_ids.resize(results.size(), std::numeric_limits<uint16_t>::max()); |
161 | 166 | container::unordered_map<TensorNetwork::SlotCanonicalizationMetadata, |
162 | 167 | uint16_t, SubNetHash, SubNetEqual> |
163 | 168 | meta_to_id; |
@@ -194,6 +199,8 @@ EvalSequence single_term_opt_impl(TensorNetwork const& network, |
194 | 199 | double new_cost = 0; |
195 | 200 | container::vector<uint16_t> combined_subnets; |
196 | 201 | if (subnet_cse) { |
| 202 | + // subnets is always kept sorted; set_union requires sorted inputs and |
| 203 | + // produces sorted output — this invariant is maintained throughout. |
197 | 204 | std::set_union(results[lp].subnets.begin(), results[lp].subnets.end(), |
198 | 205 | results[rp].subnets.begin(), results[rp].subnets.end(), |
199 | 206 | std::back_inserter(combined_subnets)); |
@@ -222,6 +229,9 @@ EvalSequence single_term_opt_impl(TensorNetwork const& network, |
222 | 229 |
|
223 | 230 | if (subnet_cse) { |
224 | 231 | auto mid = meta_ids[n]; |
| 232 | + // Canonically equivalent subnetworks share the same topology and index |
| 233 | + // sizes, so their cost is identical. Overwriting with a later bitmask's |
| 234 | + // cost is intentional and benign. |
225 | 235 | unique_meta_costs[mid] = |
226 | 236 | cost_fn(results[results[n].lp].indices, |
227 | 237 | results[results[n].rp].indices, results[n].indices); |
|
0 commit comments