35template <
class LeftTensor,
class RightTensor>
36concept PairwiseContractionSources =
37 BlockTensorView<LeftTensor> && BlockTensorView<RightTensor> &&
38 (std::remove_cvref_t<LeftTensor>::codomain_type::size() > 0) &&
39 (std::remove_cvref_t<RightTensor>::domain_type::size() > 0) &&
40 requires(block_tensor_value_t<LeftTensor> lhs, block_tensor_value_t<RightTensor> rhs) { lhs* rhs; } &&
41 Scalar<std::remove_cvref_t<
decltype(std::declval<block_tensor_value_t<LeftTensor>>() *
42 std::declval<block_tensor_value_t<RightTensor>>())>>;
44template <
class LeftTensor,
class RightTensor>
45 requires PairwiseContractionSources<LeftTensor, RightTensor>
46using pairwise_contraction_value_t = std::remove_cvref_t<
decltype(std::declval<block_tensor_value_t<LeftTensor>>() *
47 std::declval<block_tensor_value_t<RightTensor>>())>;
49struct DefaultPairwiseContractionStorage
52template <
class RequestedStorage,
class DefaultStorage>
53using selected_pairwise_storage_t =
54 std::conditional_t<std::same_as<RequestedStorage, DefaultPairwiseContractionStorage>, DefaultStorage,
57template <
class Tensor>
58using default_pairwise_storage_t =
59 PackedSparseBlockStorage<typename block_tensor_type_t<Tensor>::storage_policy::leaf_storage_policy>;
61template <
class Tensor>
using pairwise_const_block_t = block_tensor_const_block_t<Tensor>;
63template <
class Tensor>
using pairwise_mutable_block_t = block_tensor_mutable_block_t<Tensor>;
65template <
class Tensor>
66concept LocalContractionSource =
67 BlockTensorView<Tensor> && !AsyncBlockTensorView<Tensor> &&
69 typename std::remove_cvref_t<Tensor>::storage_policy,
typename std::remove_cvref_t<Tensor>::value_type,
70 std::remove_cvref_t<Tensor>::key_coordinate_count(), std::remove_cvref_t<Tensor>::dense_block_order()>;
72template <std::
size_t ContractedCount = 1,
class LeftTensor,
class RightTensor>
73 requires PairwiseContractionSources<LeftTensor, RightTensor>
74auto make_pairwise_contraction_domain(LeftTensor
const& left, RightTensor
const& right)
76 using right_type = std::remove_cvref_t<RightTensor>;
77 constexpr std::size_t right_domain_size = right_type::domain_type::size();
78 return domain_from_tuple(
79 std::tuple_cat(
left.domain().spaces(),
80 tuple_slice<ContractedCount, right_domain_size - ContractedCount>(
right.domain().spaces())));
83template <std::
size_t ContractedCount = 1,
class LeftTensor,
class RightTensor>
84 requires PairwiseContractionSources<LeftTensor, RightTensor>
85auto make_pairwise_contraction_codomain(LeftTensor
const& left, RightTensor
const& right)
87 using left_type = std::remove_cvref_t<LeftTensor>;
88 constexpr std::size_t left_codomain_size = left_type::codomain_type::size();
89 return codomain_from_tuple(std::tuple_cat(
90 tuple_slice<0, left_codomain_size - ContractedCount>(
left.codomain().spaces()),
right.codomain().spaces()));
93template <
class Boundary, std::size_t First, std::size_t... Axis>
94consteval auto boundary_key_coordinate_count(std::index_sequence<Axis...>) -> std::size_t
97 std::size_t{0} + ... +
98 (BlockTensorSpaceTraits<typename Boundary::template space_type<First + Axis>>::has_block_coordinate ? 1U : 0U));
101template <
class Boundary, std::size_t First, std::size_t... Axis>
102consteval auto boundary_dense_axis_count(std::index_sequence<Axis...>) -> std::size_t
104 return (std::size_t{0} + ... +
105 (BlockTensorSpaceTraits<typename Boundary::template space_type<First + Axis>>::has_dense_axis ? 1U : 0U));
108template <
class LeftBoundary,
class RightBoundary, std::size_t LeftFirst, std::size_t... Axis>
109consteval auto pairwise_contracted_space_types_match(std::index_sequence<Axis...>) ->
bool
111 return (std::same_as<
typename LeftBoundary::template space_type<LeftFirst + Axis>,
112 typename RightBoundary::template space_type<Axis>> &&
116template <
class LeftTensor,
class RightTensor, std::
size_t ContractedCount = 1>
117 requires PairwiseContractionSources<LeftTensor, RightTensor>
118struct PairwiseContractionTraits
120 using left_type = std::remove_cvref_t<LeftTensor>;
121 using right_type = std::remove_cvref_t<RightTensor>;
122 static constexpr std::size_t contracted_count = ContractedCount;
123 static constexpr std::size_t left_codomain_size = left_type::codomain_type::size();
124 static constexpr std::size_t right_domain_size = right_type::domain_type::size();
125 static_assert(contracted_count > 0);
126 static_assert(contracted_count <= left_codomain_size);
127 static_assert(contracted_count <= right_domain_size);
129 pairwise_contracted_space_types_match<
typename left_type::codomain_type,
typename right_type::domain_type,
130 left_codomain_size - contracted_count>(
131 std::make_index_sequence<contracted_count>{}));
132 static constexpr std::size_t contracted_key_count =
133 boundary_key_coordinate_count<
typename left_type::codomain_type, left_codomain_size - contracted_count>(
134 std::make_index_sequence<contracted_count>{});
135 static constexpr std::size_t contracted_dense_count =
136 boundary_dense_axis_count<
typename left_type::codomain_type, left_codomain_size - contracted_count>(
137 std::make_index_sequence<contracted_count>{});
138 static constexpr bool contracted_has_key = contracted_key_count != 0;
139 static constexpr bool contracted_has_dense_axis = contracted_dense_count != 0;
140 static constexpr std::size_t left_domain_key_count =
141 BoundaryBlockShape<typename left_type::domain_type>::key_coordinate_count;
142 static constexpr std::size_t left_codomain_key_count =
143 BoundaryBlockShape<typename left_type::codomain_type>::key_coordinate_count;
144 static constexpr std::size_t right_domain_key_count =
145 BoundaryBlockShape<typename right_type::domain_type>::key_coordinate_count;
146 static constexpr std::size_t right_codomain_key_count =
147 BoundaryBlockShape<typename right_type::codomain_type>::key_coordinate_count;
148 static constexpr std::size_t result_key_count =
149 left_type::key_coordinate_count() + right_type::key_coordinate_count() - 2 * contracted_key_count;
150 using result_key_type = BlockKey<result_key_count>;
151 using domain_type =
decltype(make_pairwise_contraction_domain<contracted_count>(std::declval<left_type const&>(),
152 std::declval<right_type const&>()));
153 using codomain_type =
decltype(make_pairwise_contraction_codomain<contracted_count>(
154 std::declval<left_type const&>(), std::declval<right_type const&>()));
156 static_assert(BoundaryBlockShape<domain_type>::key_coordinate_count +
157 BoundaryBlockShape<codomain_type>::key_coordinate_count ==
161template <
class OutputStorage,
class LeftTensor,
class RightTensor, std::
size_t ContractedCount = 1>
162 requires PairwiseContractionSources<LeftTensor, RightTensor>
163struct PairwiseContractionOutputTraits
165 using contraction_traits = PairwiseContractionTraits<LeftTensor, RightTensor, ContractedCount>;
166 using value_type = pairwise_contraction_value_t<LeftTensor, RightTensor>;
167 static constexpr std::size_t key_count = contraction_traits::result_key_count;
168 static constexpr std::size_t dense_order =
169 BoundaryBlockShape<typename contraction_traits::domain_type>::dense_block_order +
170 BoundaryBlockShape<typename contraction_traits::codomain_type>::dense_block_order;
171 using storage_type =
typename OutputStorage::template storage_t<value_type, key_count, dense_order>;
172 using block_type =
typename storage_type::mutable_block_type;
175template <
class OutputStorage,
class LeftTensor,
class RightTensor, std::
size_t ContractedCount = 1>
176concept LocalContractionOutput =
177 PairwiseContractionSources<LeftTensor, RightTensor> && BlockTensorStorage<OutputStorage> &&
178 LocalBlockStorageFor<
180 typename PairwiseContractionOutputTraits<OutputStorage, LeftTensor, RightTensor, ContractedCount>::value_type,
181 PairwiseContractionOutputTraits<OutputStorage, LeftTensor, RightTensor, ContractedCount>::key_count,
182 PairwiseContractionOutputTraits<OutputStorage, LeftTensor, RightTensor, ContractedCount>::dense_order> &&
183 !AsyncLocalBlockStorageFor<
185 typename PairwiseContractionOutputTraits<OutputStorage, LeftTensor, RightTensor, ContractedCount>::value_type,
186 PairwiseContractionOutputTraits<OutputStorage, LeftTensor, RightTensor, ContractedCount>::key_count,
187 PairwiseContractionOutputTraits<OutputStorage, LeftTensor, RightTensor, ContractedCount>::dense_order> &&
189 typename PairwiseContractionOutputTraits<OutputStorage, LeftTensor, RightTensor, ContractedCount>::block_type>;
191template <
class Traits,
class LeftKey,
class RightKey>
192auto pairwise_contracted_coordinates_match(LeftKey
const& left_key, RightKey
const& right_key) ->
bool
194 for (std::size_t coordinate = 0; coordinate < Traits::contracted_key_count; ++coordinate)
196 auto const left_axis = Traits::left_type::key_coordinate_count() - Traits::contracted_key_count + coordinate;
197 if (left_key.coordinate(left_axis) != right_key.coordinate(coordinate))
return false;
202template <
class Traits,
class LeftKey,
class RightKey>
203auto make_pairwise_result_key(LeftKey
const& left_key, RightKey
const& right_key) ->
typename Traits::result_key_type
205 std::array<std::size_t, Traits::result_key_count> coordinates{};
206 std::size_t output = 0;
207 auto append = [&](
auto const& key, std::size_t first, std::size_t last) {
208 for (std::size_t axis = first; axis < last; ++axis)
209 coordinates[output++] = key.coordinate(axis);
211 append(left_key, 0, Traits::left_domain_key_count);
212 append(right_key, Traits::contracted_key_count, Traits::right_domain_key_count);
213 append(left_key, Traits::left_domain_key_count,
214 Traits::left_type::key_coordinate_count() - Traits::contracted_key_count);
215 append(right_key, Traits::right_domain_key_count, Traits::right_type::key_coordinate_count());
216 return typename Traits::result_key_type{coordinates};
219template <
class Traits>
struct PairwiseContractionWorkItem
221 typename Traits::left_type::key_type left_key;
222 typename Traits::right_type::key_type right_key;
223 typename Traits::result_key_type result_key;
226template <
class Traits,
class LeftTensor,
class RightTensor>
227auto make_pairwise_contraction_worklist(LeftTensor
const& left,
228 RightTensor
const& right) -> std::vector<PairwiseContractionWorkItem<Traits>>
230 std::vector<PairwiseContractionWorkItem<Traits>> worklist;
231 for (
auto const& left_key :
left.stored_keys())
233 for (
auto const& right_key :
right.stored_keys())
235 if (pairwise_contracted_coordinates_match<Traits>(left_key, right_key))
237 worklist.push_back({left_key, right_key, make_pairwise_result_key<Traits>(left_key, right_key)});
244template <
class Traits>
245auto pairwise_contraction_result_keys(std::span<PairwiseContractionWorkItem<Traits>
const> worklist)
246 -> std::vector<typename Traits::result_key_type>
248 std::vector<typename Traits::result_key_type> result_keys;
249 result_keys.reserve(worklist.size());
250 for (
auto const& item : worklist)
251 result_keys.push_back(item.result_key);
252 std::ranges::sort(result_keys);
253 result_keys.erase(std::unique(result_keys.begin(), result_keys.end()), result_keys.end());
257template <
class Result,
class Prototype,
class DomainType,
class CodomainType,
class Key>
258auto make_pairwise_contraction_result(Prototype
const& prototype, Symmetry symmetry, DomainType domain,
259 CodomainType codomain, std::vector<Key> result_keys) -> Result
261 if constexpr (CompleteBlockStorage<typename Result::storage_policy>)
263 if constexpr (
requires { Result(
uninitialized, symmetry, domain, codomain, prototype.allocation_context()); })
264 return Result(
uninitialized, symmetry, std::move(domain), std::move(codomain), prototype.allocation_context());
266 return Result(
uninitialized, symmetry, std::move(domain), std::move(codomain));
270 if constexpr (
requires {
271 Result(
uninitialized, symmetry, domain, codomain, result_keys, prototype.allocation_context());
273 return Result(
uninitialized, symmetry, std::move(domain), std::move(codomain), std::move(result_keys),
274 prototype.allocation_context());
276 return Result(
uninitialized, symmetry, std::move(domain), std::move(codomain), std::move(result_keys));
280template <
class Traits>
struct LocalPairwiseContractionBinding
282 PairwiseContractionWorkItem<Traits> logical;
283 std::size_t left_ordinal;
284 std::size_t right_ordinal;
285 std::size_t result_ordinal;
286 bool initializes_result;
289template <
class Function>
void execute_block_batch(SerialBlockExecution, std::size_t size, Function&& function)
292 for (std::size_t index = 0; index <
size; ++index)
296template <
class Function>
void execute_block_batch(SchedulerBatchBlockExecution, std::size_t size, Function&& function)
301template <
class Key>
auto stored_key_ordinal(std::span<Key const> keys, Key
const& key) -> std::size_t
303 auto const found = std::ranges::lower_bound(keys, key);
304 if (found == keys.end() || *found != key)
306 throw std::logic_error(
"pairwise BlockTensor work item has no stored block binding");
308 return static_cast<std::size_t
>(found - keys.begin());
311template <
class Traits,
class LeftTensor,
class RightTensor,
class ResultTensor>
312auto bind_pairwise_contraction_worklist(LeftTensor
const& left, RightTensor
const& right, ResultTensor
const& result,
313 std::span<PairwiseContractionWorkItem<Traits>
const> worklist)
314 -> std::vector<LocalPairwiseContractionBinding<Traits>>
316 std::vector<LocalPairwiseContractionBinding<Traits>> bindings;
317 bindings.reserve(worklist.size());
318 std::vector<bool> result_initialized(result.stored_block_count(),
false);
319 for (
auto const& item : worklist)
321 auto const result_ordinal = stored_key_ordinal(result.stored_keys(), item.result_key);
322 bindings.push_back({.logical = item,
323 .left_ordinal = stored_key_ordinal(
left.stored_keys(), item.left_key),
324 .right_ordinal = stored_key_ordinal(
right.stored_keys(), item.right_key),
325 .result_ordinal = result_ordinal,
326 .initializes_result = !result_initialized[result_ordinal]});
327 result_initialized[result_ordinal] =
true;
332template <
class Traits>
consteval auto make_pairwise_output_dense_permutation()
334 constexpr std::size_t left_domain_count =
335 BoundaryBlockShape<typename Traits::left_type::domain_type>::dense_block_order;
336 constexpr std::size_t left_codomain_count =
337 BoundaryBlockShape<typename Traits::left_type::codomain_type>::dense_block_order;
338 constexpr std::size_t right_domain_count =
339 BoundaryBlockShape<typename Traits::right_type::domain_type>::dense_block_order;
340 constexpr std::size_t right_codomain_count =
341 BoundaryBlockShape<typename Traits::right_type::codomain_type>::dense_block_order;
342 constexpr std::size_t left_codomain_external = left_codomain_count - Traits::contracted_dense_count;
343 constexpr std::size_t right_domain_external = right_domain_count - Traits::contracted_dense_count;
344 constexpr std::size_t result_count =
345 left_domain_count + right_domain_external + left_codomain_external + right_codomain_count;
347 std::array<std::size_t, result_count> permutation{};
348 std::size_t output = 0;
349 auto append = [&](std::size_t first, std::size_t last) {
350 for (std::size_t axis = first; axis < last; ++axis)
351 permutation[output++] = axis;
353 append(0, left_domain_count);
354 append(left_domain_count + right_domain_external, left_domain_count + right_domain_external + left_codomain_external);
355 append(left_domain_count, left_domain_count + right_domain_external);
356 append(left_domain_count + right_domain_external + left_codomain_external, result_count);
357 if (output != result_count)
throw "invalid pairwise contraction output permutation";
361template <
class Traits>
consteval auto is_async_matrix_product_geometry() ->
bool
363 if constexpr (!Traits::contracted_has_dense_axis || Traits::left_type::dense_block_order() != 2 ||
364 Traits::right_type::dense_block_order() != 2 ||
365 BoundaryBlockShape<typename Traits::domain_type>::dense_block_order +
366 BoundaryBlockShape<typename Traits::codomain_type>::dense_block_order !=
373 constexpr auto permutation = make_pairwise_output_dense_permutation<Traits>();
374 return permutation == make_identity_axis_permutation<2>();
378template <
class Tensor>
379concept AsyncMatrixContractionSource =
380 AsyncBlockTensorView<Tensor> &&
381 AsyncLocalBlockStorageFor<
382 typename std::remove_cvref_t<Tensor>::storage_policy,
typename std::remove_cvref_t<Tensor>::element_type,
383 std::remove_cvref_t<Tensor>::key_coordinate_count(), std::remove_cvref_t<Tensor>::dense_block_order()>;
385template <
class OutputStorage,
class LeftTensor,
class RightTensor>
386concept AsyncMatrixContractionOutput =
387 PairwiseContractionSources<LeftTensor, RightTensor> && SparseBlockStorage<OutputStorage> &&
388 std::same_as<block_tensor_value_t<LeftTensor>, block_tensor_value_t<RightTensor>> &&
389 std::same_as<pairwise_contraction_value_t<LeftTensor, RightTensor>, block_tensor_value_t<LeftTensor>> &&
390 AsyncLocalBlockStorageFor<
391 OutputStorage,
typename PairwiseContractionOutputTraits<OutputStorage, LeftTensor, RightTensor>::value_type,
392 PairwiseContractionOutputTraits<OutputStorage, LeftTensor, RightTensor>::key_count,
393 PairwiseContractionOutputTraits<OutputStorage, LeftTensor, RightTensor>::dense_order>;
395template <
class OutputTensor,
class LeftTensor,
class RightTensor, std::
size_t ContractedCount = 1>
396concept CompatibleFixedPairwiseContractionOutput =
397 PairwiseContractionSources<LeftTensor, RightTensor> && MutableBlockTensorView<OutputTensor> &&
398 std::same_as<typename std::remove_cvref_t<OutputTensor>::value_type,
399 pairwise_contraction_value_t<LeftTensor, RightTensor>> &&
400 std::same_as<typename std::remove_cvref_t<OutputTensor>::key_type,
401 typename PairwiseContractionTraits<LeftTensor, RightTensor, ContractedCount>::result_key_type> &&
402 std::same_as<typename std::remove_cvref_t<OutputTensor>::domain_type,
403 typename PairwiseContractionTraits<LeftTensor, RightTensor, ContractedCount>::domain_type> &&
404 std::same_as<typename std::remove_cvref_t<OutputTensor>::codomain_type,
405 typename PairwiseContractionTraits<LeftTensor, RightTensor, ContractedCount>::codomain_type>;
407template <
class OutputTensor,
class LeftTensor,
class RightTensor, std::
size_t ContractedCount = 1>
408concept LocalFixedPairwiseContractionOutput =
409 CompatibleFixedPairwiseContractionOutput<OutputTensor, LeftTensor, RightTensor, ContractedCount> &&
410 LocalBlockStorageFor<typename std::remove_cvref_t<OutputTensor>::storage_policy,
411 typename std::remove_cvref_t<OutputTensor>::value_type,
412 std::remove_cvref_t<OutputTensor>::key_coordinate_count(),
413 std::remove_cvref_t<OutputTensor>::dense_block_order()> &&
414 !MutableAsyncBlockTensorView<OutputTensor>;
416template <
class OutputTensor,
class LeftTensor,
class RightTensor>
417concept AsyncMatrixFixedPairwiseContractionOutput =
418 CompatibleFixedPairwiseContractionOutput<OutputTensor, LeftTensor, RightTensor> &&
419 std::same_as<block_tensor_value_t<LeftTensor>, block_tensor_value_t<RightTensor>> &&
420 std::same_as<pairwise_contraction_value_t<LeftTensor, RightTensor>, block_tensor_value_t<LeftTensor>> &&
421 MutableAsyncBlockTensorView<OutputTensor>;
423template <
class Lhs,
class Rhs>
constexpr auto same_pairwise_tensor_object(Lhs
const& lhs, Rhs
const& rhs) ->
bool
425 if constexpr (std::same_as<std::remove_cvref_t<Lhs>, std::remove_cvref_t<Rhs>>)
427 return std::addressof(lhs) == std::addressof(rhs);
432template <
class Traits,
class LeftTensor,
class RightTensor, std::size_t... Axis>
433auto pairwise_contracted_spaces_equal(LeftTensor
const& left, RightTensor
const& right,
434 std::index_sequence<Axis...>) ->
bool
436 return ((
left.codomain().template space<Traits::left_codomain_size - Traits::contracted_count + Axis>() ==
437 right.domain().template space<Axis>()) &&
441template <
class Traits,
class LeftTensor,
class RightTensor>
442void validate_pairwise_contraction(LeftTensor
const& left, RightTensor
const& right)
444 using left_type =
typename Traits::left_type;
445 using right_type =
typename Traits::right_type;
446 static_assert(Scalar<pairwise_contraction_value_t<left_type, right_type>>,
447 "BlockTensor contraction requires a scalar multiplication result");
448 static_assert(left_type::order() + right_type::order() >= 2 * Traits::contracted_count);
449 static_assert(left_type::order() + right_type::order() - 2 * Traits::contracted_count <= 4,
450 "the first BlockTensor contraction supports result order at most four");
454 throw std::invalid_argument(
"BlockTensor contraction requires matching symmetries");
456 if (!pairwise_contracted_spaces_equal<Traits>(left, right, std::make_index_sequence<Traits::contracted_count>{}))
458 throw std::invalid_argument(
"BlockTensor contraction requires exactly equal contracted spaces");
462template <
class Traits,
class OutputTensor,
class LeftTensor,
class RightTensor>
463void validate_fixed_pairwise_contraction_output(OutputTensor
const& output, LeftTensor
const& left,
464 RightTensor
const& right,
465 std::span<typename Traits::result_key_type const> result_keys)
467 if (same_pairwise_tensor_object(output, left) || same_pairwise_tensor_object(output, right))
469 throw std::invalid_argument(
"fixed BlockTensor contraction output must not be an input tensor");
471 if (output.symmetry() !=
left.symmetry() ||
472 output.domain() != make_pairwise_contraction_domain<Traits::contracted_count>(left, right) ||
473 output.codomain() != make_pairwise_contraction_codomain<Traits::contracted_count>(left, right))
475 throw std::invalid_argument(
"fixed BlockTensor contraction output has incompatible symmetry or boundaries");
477 if (!std::ranges::includes(output.stored_keys(), result_keys))
479 throw std::invalid_argument(
"fixed BlockTensor contraction output omits a required result block");
483template <
class Traits,
class OutputTensor,
class LeftTensor,
class RightTensor>
484void execute_local_pairwise_contraction(OutputTensor& output, LeftTensor
const& left, RightTensor
const& right,
485 std::vector<LocalPairwiseContractionBinding<Traits>> bindings)
487 using left_type =
typename Traits::left_type;
488 using value_type =
typename std::remove_cvref_t<OutputTensor>::value_type;
489 std::ranges::stable_sort(bindings, {}, &LocalPairwiseContractionBinding<Traits>::result_ordinal);
490 std::vector<std::size_t> group_offsets;
491 group_offsets.reserve(output.stored_block_count() + 1);
492 group_offsets.push_back(0);
493 for (std::size_t index = 1; index < bindings.size(); ++index)
495 if (bindings[index - 1].result_ordinal != bindings[index].result_ordinal) group_offsets.push_back(index);
497 if (!bindings.empty()) group_offsets.push_back(bindings.size());
498 constexpr auto output_permutation = make_pairwise_output_dense_permutation<Traits>();
500 auto execute_group = [&](std::size_t group) {
501 std::size_t
const first = group_offsets[group];
502 std::size_t
const last = group_offsets[group + 1];
503 auto output_block = permute_block(output.block_by_ordinal(bindings[first].result_ordinal), output_permutation);
505 for (std::size_t index = first; index < last; ++index)
507 auto const& binding = bindings[index];
508 auto const left_block = std::as_const(left).block_by_ordinal(binding.left_ordinal);
509 auto const right_block = std::as_const(right).block_by_ordinal(binding.right_ordinal);
511 constexpr auto dimensions = [] {
512 std::array<std::pair<std::size_t, std::size_t>, Traits::contracted_dense_count> result{};
513 for (std::size_t axis = 0; axis < result.size(); ++axis)
514 result[axis] = {left_type::dense_block_order() - Traits::contracted_dense_count + axis, axis};
520 execute_block_batch(
typename std::remove_cvref_t<OutputTensor>::storage_policy::block_execution_policy{},
521 group_offsets.size() - 1, execute_group);
524template <
class OutputTensor,
class Bindings>
525void zero_unbound_pairwise_output_blocks(OutputTensor& output, Bindings
const& bindings)
527 std::vector<bool> bound(output.stored_block_count(),
false);
528 for (
auto const& binding : bindings)
529 bound[binding.result_ordinal] = true;
531 std::vector<std::size_t> unbound_ordinals;
532 unbound_ordinals.reserve(output.stored_block_count());
533 for (std::size_t ordinal = 0; ordinal < bound.size(); ++ordinal)
535 if (!bound[ordinal]) unbound_ordinals.push_back(ordinal);
537 if (unbound_ordinals.empty())
return;
539 if constexpr (!MutableAsyncBlockTensorView<OutputTensor>)
541 execute_block_batch(
typename std::remove_cvref_t<OutputTensor>::storage_policy::block_execution_policy{},
542 unbound_ordinals.size(), [&](std::size_t index) {
543 auto block = output.block_by_ordinal(unbound_ordinals[index]);
544 using value_type =
typename std::remove_cvref_t<OutputTensor>::value_type;
550 using value_type =
typename std::remove_cvref_t<OutputTensor>::value_type;
551 for (
auto const ordinal : unbound_ordinals)
553 uni20::fill(output.async_block_by_ordinal(ordinal), value_type{});
576template <std::size_t ContractedCount,
class OutputStorage = detail::DefaultPairwiseContractionStorage,
577 class LeftTensor,
class RightTensor>
578 requires detail::PairwiseContractionSources<LeftTensor, RightTensor> && (ContractedCount > 0) &&
579 (ContractedCount <= std::remove_cvref_t<LeftTensor>::codomain_type::size()) &&
580 (ContractedCount <= std::remove_cvref_t<RightTensor>::domain_type::size()) &&
581 detail::LocalContractionSource<LeftTensor> && detail::LocalContractionSource<RightTensor> &&
582 detail::LocalContractionOutput<
583 detail::selected_pairwise_storage_t<OutputStorage, detail::default_pairwise_storage_t<LeftTensor>>,
584 LeftTensor, RightTensor, ContractedCount>
587 using selected_output_storage =
588 detail::selected_pairwise_storage_t<OutputStorage, detail::default_pairwise_storage_t<LeftTensor>>;
589 using traits = detail::PairwiseContractionTraits<LeftTensor, RightTensor, ContractedCount>;
590 using value_type = detail::pairwise_contraction_value_t<LeftTensor, RightTensor>;
591 detail::validate_pairwise_contraction<traits>(left, right);
593 auto const worklist = detail::make_pairwise_contraction_worklist<traits>(left, right);
594 auto result_keys = detail::pairwise_contraction_result_keys<traits>(worklist);
595 auto result_domain = detail::make_pairwise_contraction_domain<ContractedCount>(left, right);
596 auto result_codomain = detail::make_pairwise_contraction_codomain<ContractedCount>(left, right);
599 auto result = detail::make_pairwise_contraction_result<result_type>(
600 left, left.symmetry(), std::move(result_domain), std::move(result_codomain), std::move(result_keys));
601 auto bindings = detail::bind_pairwise_contraction_worklist<traits>(left, right, result, worklist);
603 detail::zero_unbound_pairwise_output_blocks(result, bindings);
604 detail::execute_local_pairwise_contraction<traits>(result, left, right, std::move(bindings));
617template <std::
size_t ContractedCount,
class OutputTensor,
class LeftTensor,
class RightTensor>
618 requires detail::PairwiseContractionSources<LeftTensor, RightTensor> && (ContractedCount > 0) &&
619 (ContractedCount <= std::remove_cvref_t<LeftTensor>::codomain_type::size()) &&
620 (ContractedCount <= std::remove_cvref_t<RightTensor>::domain_type::size()) &&
621 detail::LocalContractionSource<LeftTensor> && detail::LocalContractionSource<RightTensor> &&
622 detail::LocalFixedPairwiseContractionOutput<OutputTensor, LeftTensor, RightTensor, ContractedCount>
625 using traits = detail::PairwiseContractionTraits<LeftTensor, RightTensor, ContractedCount>;
626 detail::validate_pairwise_contraction<traits>(left, right);
627 auto const worklist = detail::make_pairwise_contraction_worklist<traits>(left, right);
628 auto const result_keys = detail::pairwise_contraction_result_keys<traits>(worklist);
629 detail::validate_fixed_pairwise_contraction_output<traits>(output, left, right, result_keys);
631 auto bindings = detail::bind_pairwise_contraction_worklist<traits>(left, right, output, worklist);
632 detail::zero_unbound_pairwise_output_blocks(output, bindings);
633 detail::execute_local_pairwise_contraction<traits>(output, left, right, std::move(bindings));
652template <std::size_t LeftAxis, std::size_t RightAxis,
class OutputStorage = detail::DefaultPairwiseContractionStorage,
653 class LeftTensor,
class RightTensor>
654 requires detail::PairwiseContractionSources<LeftTensor, RightTensor> && detail::LocalContractionSource<LeftTensor> &&
655 detail::LocalContractionSource<RightTensor> &&
656 detail::LocalContractionOutput<
657 detail::selected_pairwise_storage_t<OutputStorage, detail::default_pairwise_storage_t<LeftTensor>>,
658 LeftTensor, RightTensor> &&
659 (LeftAxis == std::remove_cvref_t<LeftTensor>::order() - 1) && (RightAxis == 0)
660auto
contract(LeftTensor const& left, RightTensor const& right)
662 using selected_output_storage =
663 detail::selected_pairwise_storage_t<OutputStorage, detail::default_pairwise_storage_t<LeftTensor>>;
664 using traits = detail::PairwiseContractionTraits<LeftTensor, RightTensor>;
665 using value_type = detail::pairwise_contraction_value_t<LeftTensor, RightTensor>;
666 detail::validate_pairwise_contraction<traits>(left, right);
668 auto const worklist = detail::make_pairwise_contraction_worklist<traits>(left, right);
669 auto result_keys = detail::pairwise_contraction_result_keys<traits>(worklist);
671 auto result_domain = detail::make_pairwise_contraction_domain(left, right);
672 auto result_codomain = detail::make_pairwise_contraction_codomain(left, right);
675 auto result = detail::make_pairwise_contraction_result<result_type>(
676 left, left.symmetry(), std::move(result_domain), std::move(result_codomain), std::move(result_keys));
677 auto bindings = detail::bind_pairwise_contraction_worklist<traits>(left, right, result, worklist);
679 detail::zero_unbound_pairwise_output_blocks(result, bindings);
680 detail::execute_local_pairwise_contraction<traits>(result, left, right, std::move(bindings));
697template <std::
size_t LeftAxis, std::
size_t RightAxis,
class OutputTensor,
class LeftTensor,
class RightTensor>
698 requires detail::PairwiseContractionSources<LeftTensor, RightTensor> && detail::LocalContractionSource<LeftTensor> &&
699 detail::LocalContractionSource<RightTensor> &&
700 detail::LocalFixedPairwiseContractionOutput<OutputTensor, LeftTensor, RightTensor> &&
701 (LeftAxis == std::remove_cvref_t<LeftTensor>::order() - 1) && (RightAxis == 0)
702void
contract(OutputTensor& output, LeftTensor const& left, RightTensor const& right)
704 using traits = detail::PairwiseContractionTraits<LeftTensor, RightTensor>;
705 detail::validate_pairwise_contraction<traits>(left, right);
706 auto const worklist = detail::make_pairwise_contraction_worklist<traits>(left, right);
707 auto const result_keys = detail::pairwise_contraction_result_keys<traits>(worklist);
708 detail::validate_fixed_pairwise_contraction_output<traits>(output, left, right, result_keys);
710 auto bindings = detail::bind_pairwise_contraction_worklist<traits>(left, right, output, worklist);
711 detail::zero_unbound_pairwise_output_blocks(output, bindings);
712 detail::execute_local_pairwise_contraction<traits>(output, left, right, std::move(bindings));
733template <std::size_t LeftAxis, std::size_t RightAxis,
class OutputStorage = detail::DefaultPairwiseContractionStorage,
734 class LeftTensor,
class RightTensor>
735 requires detail::PairwiseContractionSources<LeftTensor, RightTensor> &&
736 detail::AsyncMatrixContractionSource<LeftTensor> && detail::AsyncMatrixContractionSource<RightTensor> &&
737 detail::AsyncMatrixContractionOutput<
738 detail::selected_pairwise_storage_t<OutputStorage,
739 typename std::remove_cvref_t<LeftTensor>::storage_policy>,
740 LeftTensor, RightTensor> &&
741 (detail::is_async_matrix_product_geometry<detail::PairwiseContractionTraits<LeftTensor, RightTensor>>()) &&
742 (LeftAxis == std::remove_cvref_t<LeftTensor>::order() - 1) && (RightAxis == 0)
743auto contract(LeftTensor
const& left, RightTensor
const& right)
745 using selected_output_storage =
746 detail::selected_pairwise_storage_t<OutputStorage, typename std::remove_cvref_t<LeftTensor>::storage_policy>;
747 using traits = detail::PairwiseContractionTraits<LeftTensor, RightTensor>;
748 using value_type = detail::pairwise_contraction_value_t<LeftTensor, RightTensor>;
749 detail::validate_pairwise_contraction<traits>(left, right);
751 auto const worklist = detail::make_pairwise_contraction_worklist<traits>(left, right);
752 auto result_keys = detail::pairwise_contraction_result_keys<traits>(worklist);
754 auto result_domain = detail::make_pairwise_contraction_domain(left, right);
755 auto result_codomain = detail::make_pairwise_contraction_codomain(left, right);
758 result_type result(
uninitialized, left.symmetry(), std::move(result_domain), std::move(result_codomain),
759 std::move(result_keys));
760 auto const bindings = detail::bind_pairwise_contraction_worklist<traits>(left, right, result, worklist);
762 for (
auto const& binding : bindings)
764 auto& output_block = result.async_block_by_ordinal(binding.result_ordinal);
765 auto const& left_block = left.async_block_by_ordinal(binding.left_ordinal);
766 auto const& right_block = right.async_block_by_ordinal(binding.right_ordinal);
767 if (binding.initializes_result)
769 linalg::assign_product(selected_output_storage::backend_selector(), output_block, left_block, right_block);
773 linalg::add_product(selected_output_storage::backend_selector(), output_block, left_block, right_block);
788template <std::
size_t LeftAxis, std::
size_t RightAxis,
class OutputTensor,
class LeftTensor,
class RightTensor>
789 requires detail::PairwiseContractionSources<LeftTensor, RightTensor> &&
790 detail::AsyncMatrixContractionSource<LeftTensor> && detail::AsyncMatrixContractionSource<RightTensor> &&
791 detail::AsyncMatrixFixedPairwiseContractionOutput<OutputTensor, LeftTensor, RightTensor> &&
792 (detail::is_async_matrix_product_geometry<detail::PairwiseContractionTraits<LeftTensor, RightTensor>>()) &&
793 (LeftAxis == std::remove_cvref_t<LeftTensor>::order() - 1) && (RightAxis == 0)
794void contract(OutputTensor& output, LeftTensor
const& left, RightTensor
const& right)
796 using traits = detail::PairwiseContractionTraits<LeftTensor, RightTensor>;
797 detail::validate_pairwise_contraction<traits>(left, right);
798 auto const worklist = detail::make_pairwise_contraction_worklist<traits>(left, right);
799 auto const result_keys = detail::pairwise_contraction_result_keys<traits>(worklist);
800 detail::validate_fixed_pairwise_contraction_output<traits>(output, left, right, result_keys);
802 auto const bindings = detail::bind_pairwise_contraction_worklist<traits>(left, right, output, worklist);
803 detail::zero_unbound_pairwise_output_blocks(output, bindings);
804 using storage_policy =
typename std::remove_cvref_t<OutputTensor>::storage_policy;
805 for (
auto const& binding : bindings)
807 auto& output_block = output.async_block_by_ordinal(binding.result_ordinal);
808 auto const& left_block = left.async_block_by_ordinal(binding.left_ordinal);
809 auto const& right_block = right.async_block_by_ordinal(binding.right_ordinal);
810 if (binding.initializes_result)
816 linalg::add_product(storage_policy::backend_selector(), output_block, left_block, right_block);
Concepts and associated types for BlockTensor-level views.
Defines shared zero-copy BlockTensor axis-mapping machinery.
Fixed-order tensor whose blocks obey a bosonic abelian selection rule.
Definition block_tensor.hpp:161
BlockTensor storage which contains every symmetry-legal block.
Definition block_tensor_storage.hpp:63
Coroutine scheduler for AsyncTask.
Named backend-neutral function objects for elementwise transforms.
Opt-in asynchronous Tensor wrappers for dense linear algebra operations.
Fixed-output pairwise Tensor contraction front end.
std::pair< int, int > size(std::FILE *stream)
Return the terminal size associated with a C stream.
Definition terminal.cpp:504
void execute_batch(std::size_t size, Function &&function)
Execute a synchronous lightweight batch on the global scheduler.
Definition debug_scheduler.hpp:245
void contract(BackendSelector selector, async::Async< OutputTensor > &output, Alpha &&alpha, async::Async< LhsTensor > const &lhs, async::Async< RhsTensor > const &rhs, ContractionAxes< LhsRank, RhsRank, ContractedRank > axes, Beta &&beta)
Schedule a fixed-output pairwise contraction through an explicit selector.
Definition linalg/async/contract.hpp:84
void assign_product(BackendSelector selector, async::Async< OutputTensor > &output, async::Async< LhsTensor > const &lhs, async::Async< RhsTensor > const &rhs, Alpha &&alpha, std::optional< Precision > p={})
Schedule output = alpha * lhs * rhs with an explicit backend selector.
Definition async/matrix_product.hpp:198
void add_product(BackendSelector selector, async::Async< OutputTensor > &output, async::Async< LhsTensor > const &lhs, async::Async< RhsTensor > const &rhs, Alpha &&alpha, std::optional< Precision > p={})
Schedule output += alpha * lhs * rhs with an explicit backend selector.
Definition async/matrix_product.hpp:234
typename optional_traits< T >::value_type value_type
Definition data_table.hpp:42
Definition async/async.hpp:26
constexpr uninitialized_t uninitialized
Explicit allocation tag for overwrite outputs and scratch space.
Definition common/initialization.hpp:22
auto contract(LeftTensor const &left, RightTensor const &right)
Contract one adjacent BlockTensor factor pair into an owning result.
Definition block_tensor_contract.hpp:660
auto contract_adjacent(LeftTensor const &left, RightTensor const &right)
Contract an adjacent group of BlockTensor boundary factors.
Definition block_tensor_contract.hpp:585
void fill(BackendSelector selector, async::Async< OutputTensor > &output, Value &&value)
Fill an existing async Tensor without reading its old values.
Definition linalg/async/transform.hpp:205
mpreal beta(mpreal const &a, mpreal const &x)
Real Beta function; positive rational/integer pairs retain exact values.
Definition mpreal_special.hpp:164