10#include <uni20/config.hpp>
44template <
class Lhs,
class Rhs>
45concept CompatibleBlockTensorValues =
46 BlockTensorView<Lhs> && BlockTensorView<Rhs> &&
47 std::same_as<typename block_tensor_type_t<Lhs>::value_type,
typename block_tensor_type_t<Rhs>::value_type> &&
48 std::same_as<typename block_tensor_type_t<Lhs>::key_type,
typename block_tensor_type_t<Rhs>::key_type> &&
49 std::same_as<typename block_tensor_type_t<Lhs>::domain_type,
typename block_tensor_type_t<Rhs>::domain_type> &&
50 std::same_as<typename block_tensor_type_t<Lhs>::codomain_type,
typename block_tensor_type_t<Rhs>::codomain_type>;
52template <
class Output,
class... Inputs>
53concept CompatibleLinearOutputRepresentation =
54 BlockTensorView<Output> && (BlockTensorView<Inputs> && ...) &&
55 (!DiagonalBlockTensorView<Output> || (DiagonalBlockTensorView<Inputs> && ...));
57template <
class OutputStorage,
class FirstInput,
class... OtherInputs>
58concept CompatibleLinearOutputStorage =
59 BlockTensorStorage<OutputStorage> && BlockTensorView<FirstInput> && (BlockTensorView<OtherInputs> && ...) &&
60 (!DiagonalBlockStorage<OutputStorage> ||
61 (DiagonalBlockTensorView<FirstInput> && (DiagonalBlockTensorView<OtherInputs> && ...))) &&
62 ((ImmediateBlockTensorView<FirstInput> && (ImmediateBlockTensorView<OtherInputs> && ...) &&
63 ImmediateLocalBlockStorageFor<OutputStorage, block_tensor_value_t<FirstInput>,
64 block_tensor_type_t<FirstInput>::key_coordinate_count(),
65 block_tensor_type_t<FirstInput>::dense_block_order()>) ||
66 (AsyncBlockTensorView<FirstInput> && (AsyncBlockTensorView<OtherInputs> && ...) &&
67 AsyncLocalBlockStorageFor<OutputStorage, block_tensor_value_t<FirstInput>,
68 block_tensor_type_t<FirstInput>::key_coordinate_count(),
69 block_tensor_type_t<FirstInput>::dense_block_order()>));
71template <
class Lhs,
class Rhs>
void require_compatible_block_tensor_values(Lhs
const& lhs, Rhs
const& rhs)
73 if (lhs.symmetry() != rhs.symmetry())
75 throw std::invalid_argument(
"BlockTensor linear operation requires matching symmetries");
77 if (lhs.domain() != rhs.domain() || lhs.codomain() != rhs.codomain())
79 throw std::invalid_argument(
"BlockTensor linear operation requires exactly equal boundaries");
83template <
class Superset,
class Subset>
void require_stored_key_superset(Superset
const& superset, Subset
const& subset)
85 if (!std::ranges::includes(superset.stored_keys(), subset.stored_keys()))
87 throw std::invalid_argument(
"fixed BlockTensor output does not store every required input block");
91template <BlockTensorView Tensor>
92auto find_stored_key_ordinal(Tensor
const& tensor, block_tensor_key_t<Tensor>
const& key) -> std::optional<std::size_t>
94 auto const found = std::ranges::lower_bound(tensor.stored_keys(), key);
95 if (found == tensor.stored_keys().end() || *found != key)
return std::nullopt;
96 return static_cast<std::size_t
>(found - tensor.stored_keys().begin());
99template <
class Lhs,
class Rhs>
100auto stored_key_union(Lhs
const& lhs, Rhs
const& rhs) -> std::vector<typename block_tensor_type_t<Lhs>::key_type>
102 using key_type =
typename block_tensor_type_t<Lhs>::key_type;
103 std::vector<key_type> result;
104 result.reserve(lhs.stored_block_count() + rhs.stored_block_count());
105 std::ranges::set_union(lhs.stored_keys(), rhs.stored_keys(), std::back_inserter(result));
109struct BlockIntersectionBinding
111 std::size_t lhs_ordinal;
112 std::size_t rhs_ordinal;
115template <
class Lhs,
class Rhs>
116auto stored_key_intersection_bindings(Lhs
const& lhs, Rhs
const& rhs) -> std::vector<BlockIntersectionBinding>
118 std::vector<BlockIntersectionBinding> result;
119 result.reserve(std::min(lhs.stored_block_count(), rhs.stored_block_count()));
120 auto const lhs_keys = lhs.stored_keys();
121 auto const rhs_keys = rhs.stored_keys();
122 std::size_t lhs_ordinal = 0;
123 std::size_t rhs_ordinal = 0;
124 while (lhs_ordinal < lhs_keys.size() && rhs_ordinal < rhs_keys.size())
126 if (lhs_keys[lhs_ordinal] < rhs_keys[rhs_ordinal])
130 else if (rhs_keys[rhs_ordinal] < lhs_keys[lhs_ordinal])
136 result.push_back({lhs_ordinal++, rhs_ordinal++});
142template <
class Function>
void execute_linear_block_batch(SerialBlockExecution, std::size_t size, Function&& function)
144 for (std::size_t index = 0; index <
size; ++index)
148template <
class Function>
149void execute_linear_block_batch(SchedulerBatchBlockExecution, std::size_t size, Function&& function)
154template <
class Tensor>
155using block_execution_policy_t =
typename block_tensor_type_t<Tensor>::storage_policy::block_execution_policy;
157template <
class Lhs,
class Rhs>
158using reduction_execution_policy_t =
159 std::conditional_t<std::same_as<block_execution_policy_t<Lhs>, SchedulerBatchBlockExecution> ||
160 std::same_as<block_execution_policy_t<Rhs>, SchedulerBatchBlockExecution>,
161 SchedulerBatchBlockExecution, SerialBlockExecution>;
163template <
class Lhs,
class Rhs>
constexpr auto same_block_tensor_object(Lhs
const& lhs, Rhs
const& rhs)
noexcept ->
bool
165 if constexpr (std::same_as<block_tensor_type_t<Lhs>, block_tensor_type_t<Rhs>>)
167 return std::addressof(lhs) == std::addressof(rhs);
175#if UNI20_BACKEND_CUDA
177template <
class Tensor>
178concept CudaPartitionedBlockTensor = BlockTensorView<Tensor> &&
requires(
Tensor& tensor,
Tensor const& const_tensor) {
179 { tensor.storage().buffer() } -> std::same_as<cuda::PartitionedCudaBuffer<block_tensor_value_t<Tensor>>&>;
180 { const_tensor.storage().buffer() } -> std::same_as<cuda::PartitionedCudaBuffer<block_tensor_value_t<Tensor>>
const&>;
181 { const_tensor.storage().offsets() } -> std::same_as<std::span<std::size_t const>>;
184template <CudaPartitionedBlockTensor Lhs, CudaPartitionedBlockTensor Rhs>
185[[nodiscard]]
bool same_packed_layout(Lhs
const& lhs, Rhs
const& rhs)
187 return std::ranges::equal(lhs.stored_keys(), rhs.stored_keys()) &&
188 std::ranges::equal(lhs.storage().offsets(), rhs.storage().offsets()) &&
189 lhs.storage().buffer().size() == rhs.storage().buffer().size();
194template <
class Block>
void set_zero_block(Block&& block)
198 if constexpr (MutableDiagonalMdspecLike<
decltype(
span)>)
201 uni20::fill(std::forward<Block>(block), value_type{});
204template <
class Block,
class Scalar>
void scale_block(Block&& block, Scalar
const& factor)
207 if constexpr (MutableDiagonalMdspecLike<
decltype(
span)>)
213template <Str
idedMdspecLike Span> [[nodiscard]]
auto dense_diagonal_components(Span
const&
span)
215 using span_type = std::remove_cvref_t<Span>;
216 using element_type =
typename span_type::element_type;
217 using index_type =
typename span_type::index_type;
218 using extents_type = stdex::dextents<index_type, 1>;
219 using layout_type = stdex::layout_stride;
220 using mapping_type =
typename layout_type::template mapping<extents_type>;
221 using accessor_type =
typename span_type::accessor_type;
225 if constexpr (span_type::rank() > 0)
227 extent =
span.extent(0);
228 stride =
span.stride(0);
229 for (std::size_t axis = 1; axis < span_type::rank(); ++axis)
231 extent = std::min(extent,
span.extent(axis));
232 stride +=
span.stride(axis);
235 auto mapping = mapping_type{extents_type{extent}, std::array<index_type, 1>{stride}};
236 if constexpr (MdspanLike<span_type>)
238 using result_type = stdex::mdspan<element_type, extents_type, layout_type, accessor_type>;
239 return result_type{
span.data_handle(), std::move(mapping),
span.accessor()};
244 mdspec<element_type, extents_type, layout_type, accessor_type, typename span_type::data_descriptor_type>;
245 return result_type{
span.data_descriptor(), std::move(mapping),
span.accessor()};
249template <
class LhsBlock,
class RhsBlock> [[nodiscard]]
auto inner_product_block_host(LhsBlock&& lhs, RhsBlock&& rhs)
253 if constexpr (DiagonalMdspecLike<
decltype(lhs_span)> && DiagonalMdspecLike<
decltype(rhs_span)>)
258 else if constexpr (DiagonalMdspecLike<
decltype(lhs_span)> && StridedMdspecLike<
decltype(rhs_span)>)
261 dense_diagonal_components(rhs_span));
263 else if constexpr (StridedMdspecLike<
decltype(lhs_span)> && DiagonalMdspecLike<
decltype(rhs_span)>)
274template <
class OutputBlock,
class InputBlock,
class Scalar>
275void assign_scale_block(OutputBlock&& output, Scalar
const& factor, InputBlock&& input)
279 if constexpr (MutableDiagonalMdspecLike<
decltype(output_span)> && DiagonalMdspecLike<
decltype(input_span)>)
282 else if constexpr (MutableStridedMdspecLike<
decltype(output_span)> && DiagonalMdspecLike<
decltype(input_span)> &&
287 set_zero_block(output);
288 uni20::assign_transform(output.backend_selector(), dense_diagonal_components(output_span), linalg::scale{factor},
292 uni20::assign_transform(std::forward<OutputBlock>(output), linalg::scale{factor}, std::forward<InputBlock>(input));
295 uni20::assign_transform(std::forward<OutputBlock>(output), linalg::scale{factor}, std::forward<InputBlock>(input));
298template <
class Block> [[nodiscard]]
auto norm_block_host(Block&& block)
301 if constexpr (DiagonalMdspecLike<
decltype(
span)>)
307template <
class OutputBlock,
class LhsBlock,
class RhsBlock>
308void add_block(OutputBlock&& output, LhsBlock&& lhs, RhsBlock&& rhs)
313 if constexpr (MutableDiagonalMdspecLike<
decltype(output_span)> && DiagonalMdspecLike<
decltype(lhs_span)> &&
314 DiagonalMdspecLike<
decltype(rhs_span)>)
319 std::forward<RhsBlock>(rhs));
322template <
class OutputBlock,
class InputBlock>
void add_inplace_block(OutputBlock&& output, InputBlock&& input)
326 if constexpr (MutableDiagonalMdspecLike<
decltype(output_span)> && DiagonalMdspecLike<
decltype(input_span)>)
333template <
class OutputBlock,
class Scalar,
class InputBlock>
334void axpy_block(OutputBlock&& output, Scalar
const& factor, InputBlock&& input)
338 if constexpr (MutableDiagonalMdspecLike<
decltype(output_span)> && DiagonalMdspecLike<
decltype(input_span)>)
341 else if constexpr (MutableStridedMdspecLike<
decltype(output_span)> && DiagonalMdspecLike<
decltype(input_span)> &&
351 std::forward<InputBlock>(input));
355 std::forward<InputBlock>(input));
358template <
class RequestedStorage,
class Tensor>
359using selected_linear_storage_t =
360 std::conditional_t<std::is_void_v<RequestedStorage>,
typename block_tensor_type_t<Tensor>::storage_policy,
371#if UNI20_BACKEND_CUDA
372 if constexpr (detail::CudaPartitionedBlockTensor<Tensor> &&
373 linalg::cuda_partitioned_fill_scalar<block_tensor_value_t<Tensor>>)
381 detail::execute_linear_block_batch(detail::block_execution_policy_t<Tensor>{}, tensor.stored_block_count(),
382 [&](std::size_t ordinal) {
383 auto block = tensor.block_by_ordinal(ordinal);
384 detail::set_zero_block(block);
389 for (std::size_t ordinal = 0; ordinal < tensor.stored_block_count(); ++ordinal)
391 auto& block = tensor.async_block_by_ordinal(ordinal);
402#if UNI20_BACKEND_CUDA
403 if constexpr (detail::CudaPartitionedBlockTensor<Tensor> &&
404 linalg::cuda_partitioned_scale_scalars<block_tensor_value_t<Tensor>,
Scalar>)
415 detail::execute_linear_block_batch(detail::block_execution_policy_t<Tensor>{}, tensor.stored_block_count(),
416 [&](std::size_t ordinal) {
417 auto block = tensor.block_by_ordinal(ordinal);
418 detail::scale_block(block, factor);
423 for (std::size_t ordinal = 0; ordinal < tensor.stored_block_count(); ++ordinal)
438template <MutableBlockTensorView Output,
class Scalar, BlockTensorView Input>
439 requires detail::CompatibleBlockTensorValues<Output, Input> &&
440 detail::CompatibleLinearOutputRepresentation<Output, Input> &&
441 ((!MutableAsyncBlockTensorView<Output> && !AsyncBlockTensorView<Input>) ||
442 (MutableAsyncBlockTensorView<Output> && AsyncBlockTensorView<Input>))
445 detail::require_compatible_block_tensor_values(output, input);
446 detail::require_stored_key_superset(output, input);
447 if (detail::same_block_tensor_object(output, input))
449 scale(output, std::move(factor));
453#if UNI20_BACKEND_CUDA
454 if constexpr (detail::CudaPartitionedBlockTensor<Output> && detail::CudaPartitionedBlockTensor<Input> &&
455 linalg::cuda_partitioned_scale_scalars<block_tensor_value_t<Output>,
Scalar>)
457 if (detail::same_packed_layout(output, input))
470 detail::execute_linear_block_batch(detail::block_execution_policy_t<Output>{}, output.stored_block_count(),
471 [&](std::size_t ordinal) {
472 auto output_block = output.block_by_ordinal(ordinal);
473 auto const& key = output.stored_keys()[ordinal];
474 if (
auto input_ordinal = detail::find_stored_key_ordinal(input, key))
476 auto input_block = input.block_by_ordinal(*input_ordinal);
477 detail::assign_scale_block(output_block, factor, input_block);
480 detail::set_zero_block(output_block);
486 for (std::size_t ordinal = 0; ordinal < output.stored_block_count(); ++ordinal)
488 auto& output_block = output.async_block_by_ordinal(ordinal);
489 auto const& key = output.stored_keys()[ordinal];
490 if (
auto const input_ordinal = std::ranges::lower_bound(input.stored_keys(), key);
491 input_ordinal != input.stored_keys().end() && *input_ordinal == key)
493 auto const index =
static_cast<std::size_t
>(input_ordinal - input.stored_keys().begin());
509template <MutableBlockTensorView Output, BlockTensorView Input>
510 requires detail::CompatibleBlockTensorValues<Output, Input> &&
511 detail::CompatibleLinearOutputRepresentation<Output, Input> &&
512 ((!MutableAsyncBlockTensorView<Output> && !AsyncBlockTensorView<Input>) ||
513 (MutableAsyncBlockTensorView<Output> && AsyncBlockTensorView<Input>))
514void copy(Output& output, Input
const& input)
516 if (detail::same_block_tensor_object(output, input))
return;
528template <MutableBlockTensorView Output, BlockTensorView Lhs, BlockTensorView Rhs>
529 requires detail::CompatibleBlockTensorValues<Output, Lhs> && detail::CompatibleBlockTensorValues<Output, Rhs> &&
530 detail::CompatibleLinearOutputRepresentation<Output, Lhs, Rhs> &&
531 ((!MutableAsyncBlockTensorView<Output> && !AsyncBlockTensorView<Lhs> && !AsyncBlockTensorView<Rhs>) ||
532 (MutableAsyncBlockTensorView<Output> && AsyncBlockTensorView<Lhs> && AsyncBlockTensorView<Rhs>))
533void add(Output& output, Lhs
const& lhs, Rhs
const& rhs)
535 detail::require_compatible_block_tensor_values(output, lhs);
536 detail::require_compatible_block_tensor_values(output, rhs);
537 detail::require_stored_key_superset(output, lhs);
538 detail::require_stored_key_superset(output, rhs);
540 if (detail::same_block_tensor_object(output, lhs))
542 if (detail::same_block_tensor_object(output, rhs))
545 scale(output, value_type{2});
553 if (detail::same_block_tensor_object(output, rhs))
561 detail::execute_linear_block_batch(detail::block_execution_policy_t<Output>{}, output.stored_block_count(),
562 [&](std::size_t ordinal) {
563 auto output_block = output.block_by_ordinal(ordinal);
564 auto const& key = output.stored_keys()[ordinal];
565 auto lhs_ordinal = detail::find_stored_key_ordinal(lhs, key);
566 auto rhs_ordinal = detail::find_stored_key_ordinal(rhs, key);
567 if (lhs_ordinal && rhs_ordinal)
569 auto lhs_block = lhs.block_by_ordinal(*lhs_ordinal);
570 auto rhs_block = rhs.block_by_ordinal(*rhs_ordinal);
571 detail::add_block(output_block, lhs_block, rhs_block);
573 else if (lhs_ordinal)
575 auto lhs_block = lhs.block_by_ordinal(*lhs_ordinal);
578 else if (rhs_ordinal)
580 auto rhs_block = rhs.block_by_ordinal(*rhs_ordinal);
584 detail::set_zero_block(output_block);
590 for (std::size_t ordinal = 0; ordinal < output.stored_block_count(); ++ordinal)
592 auto& output_block = output.async_block_by_ordinal(ordinal);
593 auto const& key = output.stored_keys()[ordinal];
594 auto const lhs_found = std::ranges::lower_bound(lhs.stored_keys(), key);
595 auto const rhs_found = std::ranges::lower_bound(rhs.stored_keys(), key);
596 bool const has_lhs = lhs_found != lhs.stored_keys().end() && *lhs_found == key;
597 bool const has_rhs = rhs_found != rhs.stored_keys().end() && *rhs_found == key;
598 if (has_lhs && has_rhs)
600 auto const lhs_ordinal =
static_cast<std::size_t
>(lhs_found - lhs.stored_keys().begin());
601 auto const rhs_ordinal =
static_cast<std::size_t
>(rhs_found - rhs.stored_keys().begin());
603 rhs.async_block_by_ordinal(rhs_ordinal));
607 auto const lhs_ordinal =
static_cast<std::size_t
>(lhs_found - lhs.stored_keys().begin());
612 auto const rhs_ordinal =
static_cast<std::size_t
>(rhs_found - rhs.stored_keys().begin());
630template <MutableBlockTensorView Output, BlockTensorView Input>
631 requires detail::CompatibleBlockTensorValues<Output, Input> &&
632 detail::CompatibleLinearOutputRepresentation<Output, Input> &&
633 ((!MutableAsyncBlockTensorView<Output> && !AsyncBlockTensorView<Input>) ||
634 (MutableAsyncBlockTensorView<Output> && AsyncBlockTensorView<Input>))
637 detail::require_compatible_block_tensor_values(output, input);
638 detail::require_stored_key_superset(output, input);
639 if (detail::same_block_tensor_object(output, input))
642 scale(output, value_type{2});
645#if UNI20_BACKEND_CUDA
646 if constexpr (detail::CudaPartitionedBlockTensor<Output> && detail::CudaPartitionedBlockTensor<Input> &&
649 if (detail::same_packed_layout(output, input))
660 detail::execute_linear_block_batch(detail::block_execution_policy_t<Output>{}, input.stored_block_count(),
661 [&](std::size_t input_ordinal) {
662 auto const& key = input.stored_keys()[input_ordinal];
663 auto const output_ordinal = *detail::find_stored_key_ordinal(output, key);
664 auto output_block = output.block_by_ordinal(output_ordinal);
665 auto input_block = input.block_by_ordinal(input_ordinal);
666 detail::add_inplace_block(output_block, input_block);
671 for (std::size_t input_ordinal = 0; input_ordinal < input.stored_block_count(); ++input_ordinal)
673 auto const& key = input.stored_keys()[input_ordinal];
674 auto const output_found = std::ranges::lower_bound(output.stored_keys(), key);
675 auto const output_ordinal =
static_cast<std::size_t
>(output_found - output.stored_keys().begin());
677 input.async_block_by_ordinal(input_ordinal));
687template <MutableBlockTensorView Output,
class Scalar, BlockTensorView Input>
688 requires detail::CompatibleBlockTensorValues<Output, Input> &&
689 detail::CompatibleLinearOutputRepresentation<Output, Input> &&
690 ((!MutableAsyncBlockTensorView<Output> && !AsyncBlockTensorView<Input>) ||
691 (MutableAsyncBlockTensorView<Output> && AsyncBlockTensorView<Input>))
694 detail::require_compatible_block_tensor_values(output, input);
695 detail::require_stored_key_superset(output, input);
696 if (detail::same_block_tensor_object(output, input))
699 scale(output, value_type{1} + factor);
702#if UNI20_BACKEND_CUDA
703 if constexpr (detail::CudaPartitionedBlockTensor<Output> && detail::CudaPartitionedBlockTensor<Input> &&
704 linalg::cuda_partitioned_scale_scalars<block_tensor_value_t<Output>,
Scalar>)
706 if (detail::same_packed_layout(output, input))
719 detail::execute_linear_block_batch(detail::block_execution_policy_t<Output>{}, input.stored_block_count(),
720 [&](std::size_t input_ordinal) {
721 auto const& key = input.stored_keys()[input_ordinal];
722 auto const output_ordinal = *detail::find_stored_key_ordinal(output, key);
723 auto output_block = output.block_by_ordinal(output_ordinal);
724 auto input_block = input.block_by_ordinal(input_ordinal);
725 detail::axpy_block(output_block, factor, input_block);
730 for (std::size_t input_ordinal = 0; input_ordinal < input.stored_block_count(); ++input_ordinal)
732 auto const& key = input.stored_keys()[input_ordinal];
733 auto const output_found = std::ranges::lower_bound(output.stored_keys(), key);
734 auto const output_ordinal =
static_cast<std::size_t
>(output_found - output.stored_keys().begin());
736 input.async_block_by_ordinal(input_ordinal));
747template <
class OutputStorage =
void, BlockTensorView Lhs, BlockTensorView Rhs>
748 requires detail::CompatibleBlockTensorValues<Lhs, Rhs> &&
749 SparseBlockStorage<detail::selected_linear_storage_t<OutputStorage, Lhs>> &&
750 detail::CompatibleLinearOutputStorage<detail::selected_linear_storage_t<OutputStorage, Lhs>, Lhs, Rhs>
751[[nodiscard]]
auto add(Lhs
const& lhs, Rhs
const& rhs)
753 detail::require_compatible_block_tensor_values(lhs, rhs);
754 using storage_type = detail::selected_linear_storage_t<OutputStorage, Lhs>;
756 using result_type =
BlockTensor<
typename lhs_type::value_type,
typename lhs_type::domain_type,
757 typename lhs_type::codomain_type, storage_type>;
759 result_type(
uninitialized, lhs.symmetry(), lhs.domain(), lhs.codomain(), detail::stored_key_union(lhs, rhs));
760 add(result, lhs, rhs);
770template <BlockTensorView Lhs, BlockTensorView Rhs>
771 requires detail::CompatibleBlockTensorValues<Lhs, Rhs> && RealOrComplex<block_tensor_value_t<Lhs>> &&
772 ((!AsyncBlockTensorView<Lhs> && !AsyncBlockTensorView<Rhs>) ||
773 (AsyncBlockTensorView<Lhs> && AsyncBlockTensorView<Rhs>))
776 detail::require_compatible_block_tensor_values(lhs, rhs);
778#if UNI20_BACKEND_CUBLAS
779 if constexpr (detail::CudaPartitionedBlockTensor<Lhs> && detail::CudaPartitionedBlockTensor<Rhs> &&
782 if (detail::same_packed_layout(lhs, rhs))
784 auto const size = lhs.storage().buffer().size();
785 if (linalg::cuda_partitioned_reduction_size_supported(size))
786 return linalg::cuda_partitioned_inner_product_host(lhs.storage().buffer(), rhs.storage().buffer());
790 auto const bindings = detail::stored_key_intersection_bindings(lhs, rhs);
791 std::vector<value_type> partials(bindings.size());
794 detail::execute_linear_block_batch(detail::reduction_execution_policy_t<Lhs, Rhs>{}, bindings.size(),
795 [&](std::size_t index) {
796 auto const& binding = bindings[index];
797 auto lhs_block = lhs.block_by_ordinal(binding.lhs_ordinal);
798 auto rhs_block = rhs.block_by_ordinal(binding.rhs_ordinal);
799 partials[index] = detail::inner_product_block_host(lhs_block, rhs_block);
804 for (std::size_t index = 0; index < bindings.size(); ++index)
806 auto const& binding = bindings[index];
807 auto lhs_read = lhs.async_block_by_ordinal(binding.lhs_ordinal).read();
808 auto rhs_read = rhs.async_block_by_ordinal(binding.rhs_ordinal).read();
809 partials[index] = detail::inner_product_block_host(lhs_read.get_wait(), rhs_read.get_wait());
814 value_type compensation{};
815 for (
auto const&
partial : partials)
820 compensation = value_type{};
823 auto const corrected =
partial - compensation;
824 auto const updated = result + corrected;
825 compensation = (updated - result) - corrected;
833template <BlockTensorView Lhs, BlockTensorView Rhs>
834 requires detail::CompatibleBlockTensorValues<Lhs, Rhs> && RealOrComplex<block_tensor_value_t<Lhs>> &&
835 ((!AsyncBlockTensorView<Lhs> && !AsyncBlockTensorView<Rhs>) ||
836 (AsyncBlockTensorView<Lhs> && AsyncBlockTensorView<Rhs>))
848template <BlockTensorView Tensor>
849 requires RealOrComplex<block_tensor_value_t<Tensor>>
852#if UNI20_BACKEND_CUBLAS
853 if constexpr (detail::CudaPartitionedBlockTensor<Tensor> &&
856 auto const size = tensor.storage().buffer().size();
857 if (linalg::cuda_partitioned_reduction_size_supported(size))
858 return linalg::cuda_partitioned_norm_host(tensor.storage().buffer());
862 std::vector<real_type> partials(tensor.stored_block_count());
865 detail::execute_linear_block_batch(detail::block_execution_policy_t<Tensor>{}, tensor.stored_block_count(),
866 [&](std::size_t ordinal) {
867 auto block = tensor.block_by_ordinal(ordinal);
868 partials[ordinal] = detail::norm_block_host(block);
873 for (std::size_t ordinal = 0; ordinal < tensor.stored_block_count(); ++ordinal)
875 auto read = tensor.async_block_by_ordinal(ordinal).read();
876 partials[ordinal] = detail::norm_block_host(read.get_wait());
881 real_type scaled_sum{};
882 for (
auto const partial : partials)
884 if (
partial == real_type{})
continue;
889 scaled_sum = real_type{1} + scaled_sum * ratio * ratio;
895 scaled_sum += ratio * ratio;
898 if (
scale == real_type{})
return real_type{};
904template <BlockTensorView Tensor>
905 requires RealOrComplex<block_tensor_value_t<Tensor>>
Defines the first bosonic abelian BlockTensor container.
Concepts and associated types for BlockTensor-level views.
Fixed-order tensor whose blocks obey a bosonic abelian selection rule.
Definition block_tensor.hpp:161
General-purpose owning tensor with immediate or deferred mdspan metadata.
Definition basic_tensor.hpp:109
storage_type & storage() noexcept
Access the owned storage container.
Definition basic_tensor.hpp:542
BlockTensorView exposing an independently scheduled read timeline per block.
Definition block_tensor_concepts.hpp:107
MutableBlockTensorView exposing an independently scheduled write timeline per block.
Definition block_tensor_concepts.hpp:114
Concept for numeric scalar types (integer, real, or complex).
Definition scalar_concepts.hpp:81
Scalar types supported by the cuBLAS level-one wrappers.
Definition level1.hpp:27
Backend-dispatched copies into existing mdspan and tensor outputs.
Coroutine scheduler for AsyncTask.
Named backend-neutral function objects for elementwise transforms.
typename make_real< T >::type make_real_t
Alias for the underlying real-valued type of T.
Definition scalar_traits.hpp:168
constexpr bool isfinite(I const &x) noexcept
Returns whether an integer scalar is finite.
Definition math.hpp:121
Scalar math helper utilities.
Mdspan-shaped metadata for storage whose data handle requires acquisition.
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 cuda_partitioned_axpy(cuda::PartitionedCudaBuffer< Scalar > &output, Factor factor, cuda::PartitionedCudaBuffer< Scalar > const &input)
Add a scaled partitioned CUDA allocation to another in place.
Definition partitioned_buffer_linear.hpp:142
void cuda_partitioned_set_zero(cuda::PartitionedCudaBuffer< Scalar > &output)
Set a complete partitioned CUDA allocation to numerical zero.
Definition partitioned_buffer_linear.hpp:84
void cuda_partitioned_scale(cuda::PartitionedCudaBuffer< Scalar > &output, Factor factor)
Scale a complete partitioned CUDA allocation in place.
Definition partitioned_buffer_linear.hpp:102
void cuda_partitioned_assign_scale(cuda::PartitionedCudaBuffer< Scalar > &output, Factor factor, cuda::PartitionedCudaBuffer< Scalar > const &input)
Overwrite one partitioned CUDA allocation with a scaled peer allocation.
Definition partitioned_buffer_linear.hpp:120
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
std::remove_cvref_t< Tensor > block_tensor_type_t
Cvref-stripped BlockTensor-level object type.
Definition block_tensor_concepts.hpp:22
typename block_tensor_type_t< Tensor >::value_type block_tensor_value_t
Scalar value type exposed by a BlockTensor-level object.
Definition block_tensor_concepts.hpp:25
void inner_product(BackendSelector selector, async::Async< OutputTensor > &output, async::Async< LhsTensor > const &lhs, async::Async< RhsTensor > const &rhs)
Schedule an inner product into an explicit async scalar Tensor.
Definition linalg/async/reductions.hpp:455
exact_rational sqrt(X const &value)
Return the nonnegative rational square root, or require finite precision.
Definition exact_rational.hpp:355
void assign_scale(Output &output, Scalar factor, Input const &input)
Overwrite a fixed BlockTensor output with a scaled input value.
Definition block_tensor_linear.hpp:443
mpreal norm(mpcomplex const &z)
Definition mpcomplex.hpp:494
auto inner_product_host(BackendSelector selector, async::Async< LhsTensor > const &lhs, async::Async< RhsTensor > const &rhs)
Return a nonblocking async host C++ scalar inner product through an explicit selector.
Definition linalg/async/reductions.hpp:496
void axpy(Output &output, Scalar factor, Input const &input)
Compute output += factor * input without changing either key pattern.
Definition block_tensor_linear.hpp:692
constexpr decltype(auto) mdspec_of(Tensor &tensor) noexcept(noexcept(tensor.mdspec()))
Return a tensor's immediate or descriptor-backed multidimensional metadata.
Definition tensor/concepts.hpp:50
auto norm_host(BackendSelector selector, async::Async< InputTensor > const &input)
Return a nonblocking async host C++ scalar norm through an explicit selector.
Definition linalg/async/reductions.hpp:554
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
void scale(Tensor &tensor, Scalar factor)
Multiply every stored numerical block by a scalar in place.
Definition block_tensor_linear.hpp:400
void copy(BackendSelector selector, async::Async< OutputTensor > &output, async::Async< InputTensor > const &input)
Schedule a Tensor copy through an explicit backend selector.
Definition linalg/async/copy.hpp:174
void add_inplace(Output &output, Input const &input)
Add one BlockTensor value to an existing output in place.
Definition block_tensor_linear.hpp:635
std::ptrdiff_t index_type
Definition types.hpp:19
int add(int a, int b)
Definition dummy.cpp:6
Tensor(InputTensor const &) -> Tensor< tensor_element_t< InputTensor >, tensor_mdspec_t< InputTensor >::rank(), HostStorage, detail::materialized_layout_t< void, tensor_mdspec_t< InputTensor > >, DefaultAccessorFactory >
Deduce a runtime-extents host tensor that materializes a tensor view.
void transform_inplace(BackendSelector selector, async::Async< OutputTensor > &output, Function &&function, async::Async< InputTensors > const &... inputs)
Schedule a variadic elementwise update with an explicit backend selector.
Definition linalg/async/transform.hpp:258
constexpr auto diagonal_components(stdex::mdspan< ElementType, Extents, LayoutPolicy, diagonal_accessor< ElementType, AccessorExtents > > const &span)
Expose the physically stored values of a generalized-diagonal mdspan.
Definition diagonal_accessor.hpp:252
void set_zero(Tensor &tensor)
Set every stored numerical block to zero without changing structure.
Definition block_tensor_linear.hpp:369
void assign_transform(BackendSelector selector, async::Async< OutputTensor > &output, Function &&function)
Schedule a nullary overwrite of an existing async Tensor.
Definition linalg/async/transform.hpp:188
Allocation-wide CUDA linear operations for partitioned buffers.
std::size_t span
Definition presentation.cpp:2017
Add one retained scalar multiple to an existing element value.
Definition elementwise_functions.hpp:86
Return the sum of two element values.
Definition elementwise_functions.hpp:102
Multiply one element value by a retained scalar factor.
Definition elementwise_functions.hpp:68
Tensor sums, inner products, and Euclidean norms with explicit result residency.