Uni20
Asynchronous tensor and runtime library.
Loading...
Searching...
No Matches
block_tensor_contract.hpp
Go to the documentation of this file.
1
7#pragma once
8
16
17#include <algorithm>
18#include <array>
19#include <concepts>
20#include <cstddef>
21#include <memory>
22#include <ranges>
23#include <span>
24#include <stdexcept>
25#include <tuple>
26#include <type_traits>
27#include <utility>
28#include <vector>
29
30namespace uni20
31{
32namespace detail
33{
34
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>>())>>;
43
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>>())>;
48
49struct DefaultPairwiseContractionStorage
50{};
51
52template <class RequestedStorage, class DefaultStorage>
53using selected_pairwise_storage_t =
54 std::conditional_t<std::same_as<RequestedStorage, DefaultPairwiseContractionStorage>, DefaultStorage,
55 RequestedStorage>;
56
57template <class Tensor>
58using default_pairwise_storage_t =
59 PackedSparseBlockStorage<typename block_tensor_type_t<Tensor>::storage_policy::leaf_storage_policy>;
60
61template <class Tensor> using pairwise_const_block_t = block_tensor_const_block_t<Tensor>;
62
63template <class Tensor> using pairwise_mutable_block_t = block_tensor_mutable_block_t<Tensor>;
64
65template <class Tensor>
66concept LocalContractionSource =
67 BlockTensorView<Tensor> && !AsyncBlockTensorView<Tensor> &&
68 LocalBlockStorageFor<
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()>;
71
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)
75{
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())));
81}
82
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)
86{
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()));
91}
92
93template <class Boundary, std::size_t First, std::size_t... Axis>
94consteval auto boundary_key_coordinate_count(std::index_sequence<Axis...>) -> std::size_t
95{
96 return (
97 std::size_t{0} + ... +
98 (BlockTensorSpaceTraits<typename Boundary::template space_type<First + Axis>>::has_block_coordinate ? 1U : 0U));
99}
100
101template <class Boundary, std::size_t First, std::size_t... Axis>
102consteval auto boundary_dense_axis_count(std::index_sequence<Axis...>) -> std::size_t
103{
104 return (std::size_t{0} + ... +
105 (BlockTensorSpaceTraits<typename Boundary::template space_type<First + Axis>>::has_dense_axis ? 1U : 0U));
106}
107
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
110{
111 return (std::same_as<typename LeftBoundary::template space_type<LeftFirst + Axis>,
112 typename RightBoundary::template space_type<Axis>> &&
113 ...);
114}
115
116template <class LeftTensor, class RightTensor, std::size_t ContractedCount = 1>
117 requires PairwiseContractionSources<LeftTensor, RightTensor>
118struct PairwiseContractionTraits
119{
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);
128 static_assert(
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&>()));
155
156 static_assert(BoundaryBlockShape<domain_type>::key_coordinate_count +
157 BoundaryBlockShape<codomain_type>::key_coordinate_count ==
158 result_key_count);
159};
160
161template <class OutputStorage, class LeftTensor, class RightTensor, std::size_t ContractedCount = 1>
162 requires PairwiseContractionSources<LeftTensor, RightTensor>
163struct PairwiseContractionOutputTraits
164{
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;
173};
174
175template <class OutputStorage, class LeftTensor, class RightTensor, std::size_t ContractedCount = 1>
176concept LocalContractionOutput =
177 PairwiseContractionSources<LeftTensor, RightTensor> && BlockTensorStorage<OutputStorage> &&
178 LocalBlockStorageFor<
179 OutputStorage,
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<
184 OutputStorage,
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> &&
188 MutableTensorView<
189 typename PairwiseContractionOutputTraits<OutputStorage, LeftTensor, RightTensor, ContractedCount>::block_type>;
190
191template <class Traits, class LeftKey, class RightKey>
192auto pairwise_contracted_coordinates_match(LeftKey const& left_key, RightKey const& right_key) -> bool
193{
194 for (std::size_t coordinate = 0; coordinate < Traits::contracted_key_count; ++coordinate)
195 {
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;
198 }
199 return true;
200}
201
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
204{
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);
210 };
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};
217}
218
219template <class Traits> struct PairwiseContractionWorkItem
220{
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;
224};
225
226template <class Traits, class LeftTensor, class RightTensor>
227auto make_pairwise_contraction_worklist(LeftTensor const& left,
228 RightTensor const& right) -> std::vector<PairwiseContractionWorkItem<Traits>>
229{
230 std::vector<PairwiseContractionWorkItem<Traits>> worklist;
231 for (auto const& left_key : left.stored_keys())
232 {
233 for (auto const& right_key : right.stored_keys())
234 {
235 if (pairwise_contracted_coordinates_match<Traits>(left_key, right_key))
236 {
237 worklist.push_back({left_key, right_key, make_pairwise_result_key<Traits>(left_key, right_key)});
238 }
239 }
240 }
241 return worklist;
242}
243
244template <class Traits>
245auto pairwise_contraction_result_keys(std::span<PairwiseContractionWorkItem<Traits> const> worklist)
246 -> std::vector<typename Traits::result_key_type>
247{
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());
254 return result_keys;
255}
256
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
260{
261 if constexpr (CompleteBlockStorage<typename Result::storage_policy>)
262 {
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());
265 else
266 return Result(uninitialized, symmetry, std::move(domain), std::move(codomain));
267 }
268 else
269 {
270 if constexpr (requires {
271 Result(uninitialized, symmetry, domain, codomain, result_keys, prototype.allocation_context());
272 })
273 return Result(uninitialized, symmetry, std::move(domain), std::move(codomain), std::move(result_keys),
274 prototype.allocation_context());
275 else
276 return Result(uninitialized, symmetry, std::move(domain), std::move(codomain), std::move(result_keys));
277 }
278}
279
280template <class Traits> struct LocalPairwiseContractionBinding
281{
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;
287};
288
289template <class Function> void execute_block_batch(SerialBlockExecution, std::size_t size, Function&& function)
290{
291 // Serial storage deliberately executes inline without requiring an active scheduler.
292 for (std::size_t index = 0; index < size; ++index)
293 function(index);
294}
295
296template <class Function> void execute_block_batch(SchedulerBatchBlockExecution, std::size_t size, Function&& function)
297{
298 async::execute_batch(size, std::forward<Function>(function));
299}
300
301template <class Key> auto stored_key_ordinal(std::span<Key const> keys, Key const& key) -> std::size_t
302{
303 auto const found = std::ranges::lower_bound(keys, key);
304 if (found == keys.end() || *found != key)
305 {
306 throw std::logic_error("pairwise BlockTensor work item has no stored block binding");
307 }
308 return static_cast<std::size_t>(found - keys.begin());
309}
310
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>>
315{
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)
320 {
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;
328 }
329 return bindings;
330}
331
332template <class Traits> consteval auto make_pairwise_output_dense_permutation()
333{
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;
346
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;
352 };
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";
358 return permutation;
359}
360
361template <class Traits> consteval auto is_async_matrix_product_geometry() -> bool
362{
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 !=
367 2)
368 {
369 return false;
370 }
371 else
372 {
373 constexpr auto permutation = make_pairwise_output_dense_permutation<Traits>();
374 return permutation == make_identity_axis_permutation<2>();
375 }
376}
377
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()>;
384
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>;
394
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>;
406
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>;
415
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>;
422
423template <class Lhs, class Rhs> constexpr auto same_pairwise_tensor_object(Lhs const& lhs, Rhs const& rhs) -> bool
424{
425 if constexpr (std::same_as<std::remove_cvref_t<Lhs>, std::remove_cvref_t<Rhs>>)
426 {
427 return std::addressof(lhs) == std::addressof(rhs);
428 }
429 return false;
430}
431
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
435{
436 return ((left.codomain().template space<Traits::left_codomain_size - Traits::contracted_count + Axis>() ==
437 right.domain().template space<Axis>()) &&
438 ...);
439}
440
441template <class Traits, class LeftTensor, class RightTensor>
442void validate_pairwise_contraction(LeftTensor const& left, RightTensor const& right)
443{
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");
451
452 if (left.symmetry() != right.symmetry())
453 {
454 throw std::invalid_argument("BlockTensor contraction requires matching symmetries");
455 }
456 if (!pairwise_contracted_spaces_equal<Traits>(left, right, std::make_index_sequence<Traits::contracted_count>{}))
457 {
458 throw std::invalid_argument("BlockTensor contraction requires exactly equal contracted spaces");
459 }
460}
461
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)
466{
467 if (same_pairwise_tensor_object(output, left) || same_pairwise_tensor_object(output, right))
468 {
469 throw std::invalid_argument("fixed BlockTensor contraction output must not be an input tensor");
470 }
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))
474 {
475 throw std::invalid_argument("fixed BlockTensor contraction output has incompatible symmetry or boundaries");
476 }
477 if (!std::ranges::includes(output.stored_keys(), result_keys))
478 {
479 throw std::invalid_argument("fixed BlockTensor contraction output omits a required result block");
480 }
481}
482
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)
486{
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)
494 {
495 if (bindings[index - 1].result_ordinal != bindings[index].result_ordinal) group_offsets.push_back(index);
496 }
497 if (!bindings.empty()) group_offsets.push_back(bindings.size());
498 constexpr auto output_permutation = make_pairwise_output_dense_permutation<Traits>();
499
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);
504
505 for (std::size_t index = first; index < last; ++index)
506 {
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);
510 value_type const beta = index == first ? value_type{0} : value_type{1};
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};
515 return result;
516 }();
517 linalg::contract(output_block, value_type{1}, left_block, right_block, dimensions, beta);
518 }
519 };
520 execute_block_batch(typename std::remove_cvref_t<OutputTensor>::storage_policy::block_execution_policy{},
521 group_offsets.size() - 1, execute_group);
522}
523
524template <class OutputTensor, class Bindings>
525void zero_unbound_pairwise_output_blocks(OutputTensor& output, Bindings const& bindings)
526{
527 std::vector<bool> bound(output.stored_block_count(), false);
528 for (auto const& binding : bindings)
529 bound[binding.result_ordinal] = true;
530
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)
534 {
535 if (!bound[ordinal]) unbound_ordinals.push_back(ordinal);
536 }
537 if (unbound_ordinals.empty()) return;
538
539 if constexpr (!MutableAsyncBlockTensorView<OutputTensor>)
540 {
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;
545 uni20::fill(block, value_type{});
546 });
547 }
548 else
549 {
550 using value_type = typename std::remove_cvref_t<OutputTensor>::value_type;
551 for (auto const ordinal : unbound_ordinals)
552 {
553 uni20::fill(output.async_block_by_ordinal(ordinal), value_type{});
554 }
555 }
556}
557
558} // namespace detail
559
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>
585auto contract_adjacent(LeftTensor const& left, RightTensor const& right)
586{
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);
592
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);
597 using result_type =
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));
605 return result;
606}
607
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>
623void contract_adjacent(OutputTensor& output, LeftTensor const& left, RightTensor const& right)
624{
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);
630
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));
634}
635
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)
661{
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);
667
668 auto const worklist = detail::make_pairwise_contraction_worklist<traits>(left, right);
669 auto result_keys = detail::pairwise_contraction_result_keys<traits>(worklist);
670
671 auto result_domain = detail::make_pairwise_contraction_domain(left, right);
672 auto result_codomain = detail::make_pairwise_contraction_codomain(left, right);
673 using result_type =
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));
681 return result;
682}
683
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)
703{
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);
709
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));
713}
714
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)
744{
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);
750
751 auto const worklist = detail::make_pairwise_contraction_worklist<traits>(left, right);
752 auto result_keys = detail::pairwise_contraction_result_keys<traits>(worklist);
753
754 auto result_domain = detail::make_pairwise_contraction_domain(left, right);
755 auto result_codomain = detail::make_pairwise_contraction_codomain(left, right);
756 using result_type =
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);
761
762 for (auto const& binding : bindings)
763 {
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)
768 {
769 linalg::assign_product(selected_output_storage::backend_selector(), output_block, left_block, right_block);
770 }
771 else
772 {
773 linalg::add_product(selected_output_storage::backend_selector(), output_block, left_block, right_block);
774 }
775 }
776 return result;
777}
778
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)
795{
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);
801
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)
806 {
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)
811 {
812 linalg::assign_product(storage_policy::backend_selector(), output_block, left_block, right_block);
813 }
814 else
815 {
816 linalg::add_product(storage_policy::backend_selector(), output_block, left_block, right_block);
817 }
818 }
819}
820
821} // namespace uni20
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
Backend-dispatched elementwise overwrite and update operations.