28template <
class Span,
class Scalar, std::size_t... Axis>
29consteval bool contraction_input_expressions(std::index_sequence<Axis...>)
31 using span_type = std::remove_cvref_t<Span>;
32 return requires(Span&
span,
typename span_type::index_type index) {
33 static_cast<Scalar
>(
span.operator[](((void)Axis, index)...));
37template <
class Span,
class Scalar, std::size_t... Axis>
38consteval bool contraction_output_expressions(std::index_sequence<Axis...>)
40 using span_type = std::remove_cvref_t<Span>;
41 return requires(Span&
span,
typename span_type::index_type index, Scalar
value) {
42 static_cast<Scalar
>(
span.operator[](((void)Axis, index)...));
43 span.operator[](((void)Axis, index)...) =
value;
47template <
class Span,
class Scalar>
48concept ReadableDiagonalComponents =
51 contraction_input_expressions<
decltype(
diagonal_components(std::declval<std::remove_cvref_t<Span>
const&>())),
52 Scalar>(std::make_index_sequence<1>{});
54template <
class OutputMdspan,
class LhsMdspan,
class RhsMdspan, std::size_t LhsRank, std::size_t RhsRank,
55 std::size_t ContractedRank>
56consteval bool contraction_preserves_output_structure()
61 else if constexpr (LhsRank == 0)
63 else if constexpr (RhsRank == 0)
69template <
class Span, std::size_t Rank, std::size_t... Axis>
70constexpr decltype(
auto) element_at(Span&
span, std::array<uni20::index_type, Rank>
const& indices,
71 std::index_sequence<Axis...>)
73 using index_type =
typename std::remove_cvref_t<Span>::index_type;
77template <
class OutputMdspan,
class Scalar,
class LhsMdspan,
class RhsMdspan, std::size_t LhsRank, std::size_t RhsRank,
78 std::size_t ContractedRank>
79class ContractionLoop {
81 using axes_type = ContractionAxes<LhsRank, RhsRank, ContractedRank>;
84 ContractionLoop(OutputMdspan& output, Scalar alpha, LhsMdspan& lhs, RhsMdspan& rhs, Scalar
beta,
85 axes_type
const& axes)
86 : output_(output), alpha_(alpha), lhs_(lhs), rhs_(rhs), beta_(
beta), axes_(axes)
89 void run() { this->loop_output(0); }
92 void loop_output(std::size_t axis)
94 if (axis == output_rank)
96 this->evaluate_output_element();
103 output_indices_[axis] = index;
105 lhs_indices_[axes_.lhs_surviving[axis]] = index;
108 this->loop_output(axis + 1);
112 void evaluate_output_element()
115 if (alpha_ != Scalar{}) this->accumulate_contracted(0,
product);
117 auto&& output_value = element_at(output_, output_indices_, std::make_index_sequence<output_rank>{});
118 if (beta_ == Scalar{})
119 output_value = alpha_ *
product;
121 output_value = beta_ *
static_cast<Scalar
>(output_value) + alpha_ *
product;
124 void accumulate_contracted(std::size_t axis, Scalar&
product)
126 if (axis == ContractedRank)
128 product +=
static_cast<Scalar
>(element_at(lhs_, lhs_indices_, std::make_index_sequence<LhsRank>{})) *
129 static_cast<Scalar
>(element_at(rhs_, rhs_indices_, std::make_index_sequence<RhsRank>{}));
133 auto const lhs_axis = axes_.lhs_contracted[axis];
134 auto const rhs_axis = axes_.rhs_contracted[axis];
138 lhs_indices_[lhs_axis] = index;
139 rhs_indices_[rhs_axis] = index;
140 this->accumulate_contracted(axis + 1,
product);
144 OutputMdspan& output_;
149 axes_type
const& axes_;
150 std::array<uni20::index_type, output_rank> output_indices_{};
151 std::array<uni20::index_type, LhsRank> lhs_indices_{};
152 std::array<uni20::index_type, RhsRank> rhs_indices_{};
155template <
class OutputMdspan,
class Scalar,
class Product>
156void contract_matrix_elements(OutputMdspan& output, Scalar alpha, Scalar
beta, Product&& product_at)
160 for (
uni20::index_type column = 0; column < static_cast<uni20::index_type>(output.extent(1)); ++column)
163 if (alpha != Scalar{})
product = product_at(
row, column);
165 std::array<uni20::index_type, 2>
const output_indices{
row, column};
166 auto&& output_value = element_at(output, output_indices, std::make_index_sequence<2>{});
167 if (
beta == Scalar{})
168 output_value = alpha *
product;
170 output_value =
beta *
static_cast<Scalar
>(output_value) + alpha *
product;
175template <
class OutputMdspan,
class Scalar,
class LhsMdspan,
class RhsMdspan>
176void contract_two_diagonal_matrices(OutputMdspan& output, Scalar alpha, LhsMdspan& lhs, RhsMdspan& rhs, Scalar
beta)
180 auto update_components = [&](
auto output_components) {
181 auto const extent = std::min({
static_cast<uni20::index_type>(output_components.extent(0)),
184 for (
uni20::index_type index = 0; index < static_cast<uni20::index_type>(output_components.extent(0)); ++index)
186 auto&& output_value = output_components[index];
187 if (
beta == Scalar{})
188 output_value = Scalar{};
190 output_value =
beta *
static_cast<Scalar
>(output_value);
192 if (alpha == Scalar{})
return;
194 output_components[index] =
195 static_cast<Scalar
>(output_components[index]) +
196 alpha *
static_cast<Scalar
>(lhs_components[index]) *
static_cast<Scalar
>(rhs_components[index]);
207 for (
uni20::index_type column = 0; column < static_cast<uni20::index_type>(output.extent(1)); ++column)
209 std::array<uni20::index_type, 2>
const indices{
row, column};
210 auto&& output_value = element_at(output, indices, std::make_index_sequence<2>{});
211 if (
beta == Scalar{})
212 output_value = Scalar{};
214 output_value =
beta *
static_cast<Scalar
>(output_value);
221 if (alpha == Scalar{})
return;
223 output[index, index] =
224 static_cast<Scalar
>(output[index, index]) +
225 alpha *
static_cast<Scalar
>(lhs_components[index]) *
static_cast<Scalar
>(rhs_components[index]);
229template <
class OutputMdspan,
class Scalar,
class LhsMdspan,
class RhsMdspan>
230void contract_diagonal_matrices(OutputMdspan& output, Scalar alpha, LhsMdspan& lhs, RhsMdspan& rhs, Scalar
beta,
231 ContractionAxes<2, 2, 1>
const& axes)
233 if constexpr (ReadableDiagonalComponents<LhsMdspan, Scalar> && ReadableDiagonalComponents<RhsMdspan, Scalar>)
235 contract_two_diagonal_matrices(output, alpha, lhs, rhs,
beta);
237 else if constexpr (ReadableDiagonalComponents<LhsMdspan, Scalar>)
242 std::array<uni20::index_type, 2> rhs_indices{};
243 rhs_indices[axes.rhs_contracted[0]] =
row;
244 rhs_indices[axes.rhs_surviving[0]] = column;
245 return static_cast<Scalar
>(components[
static_cast<typename decltype(components)::
index_type>(
row)]) *
246 static_cast<Scalar
>(element_at(rhs, rhs_indices, std::make_index_sequence<2>{}));
254 std::array<uni20::index_type, 2> lhs_indices{};
255 lhs_indices[axes.lhs_surviving[0]] =
row;
256 lhs_indices[axes.lhs_contracted[0]] = column;
257 return static_cast<Scalar
>(element_at(lhs, lhs_indices, std::make_index_sequence<2>{})) *
258 static_cast<Scalar
>(components[
static_cast<typename decltype(components)::
index_type>(column)]);
266template <
class OutputMdspan,
class Scalar,
class LhsMdspan,
class RhsMdspan, std::size_t LhsRank, std::size_t RhsRank,
267 std::size_t ContractedRank>
272 std::same_as<std::remove_cv_t<typename std::remove_cvref_t<OutputMdspan>::element_type>,
Scalar> &&
273 detail::contraction_output_expressions<OutputMdspan, Scalar>(
275 detail::contraction_input_expressions<LhsMdspan, Scalar>(std::make_index_sequence<LhsRank>{}) &&
276 detail::contraction_input_expressions<RhsMdspan, Scalar>(std::make_index_sequence<RhsRank>{}) &&
277 detail::contraction_preserves_output_structure<OutputMdspan, LhsMdspan, RhsMdspan, LhsRank, RhsRank,
281 {
value ==
Scalar{} } -> std::convertible_to<bool>;
282 {
value != Scalar{} } -> std::convertible_to<bool>;
291template <
class OutputMdspan,
uni20::Scalar Scalar,
class LhsMdspan,
class RhsMdspan, std::size_t LhsRank,
292 std::size_t RhsRank, std::size_t ContractedRank>
293 requires ContractionCompatible<OutputMdspan, Scalar, LhsMdspan, RhsMdspan, LhsRank, RhsRank, ContractedRank>
300 "contraction output extents do not match the surviving input axes");
302 if constexpr (LhsRank == 2 && RhsRank == 2 && ContractedRank == 1 &&
303 (detail::ReadableDiagonalComponents<LhsMdspan, Scalar> ||
304 detail::ReadableDiagonalComponents<RhsMdspan, Scalar>))
306 detail::contract_diagonal_matrices(output, alpha, lhs, rhs,
beta, axes);
310 detail::ContractionLoop<OutputMdspan, Scalar, LhsMdspan, RhsMdspan, LhsRank, RhsRank, ContractedRank> loop{
311 output, alpha, lhs, rhs,
beta, axes};
Full logical mdspec with a rank-one view of its generalized diagonal components.
Definition mdspan/concepts.hpp:465
Writable generalized-diagonal mdspec with writable rank-one components.
Definition mdspan/concepts.hpp:477
A mutable mdspan-like type with a specified static rank.
Definition mdspan/concepts.hpp:494
An mdspan-like type with a specified static rank.
Definition mdspan/concepts.hpp:447
Concept for numeric scalar types (integer, real, or complex).
Definition scalar_concepts.hpp:81
Report whether resolved mdspans support reference contraction expressions.
Definition linalg/cpu/contract.hpp:268
Normalized axis metadata for fixed-rank pairwise tensor contractions.
locale_t value
Definition data_table.cpp:49
Accessor support for compressed generalized diagonal tensors.
Mdspan concept and accessor extensions for Uni20.
T product(std::vector< T > const &values)
Definition mpreal_exact_example.cpp:13
Definition strided_transform.hpp:20
void contract(OutputMdspan &output, Scalar alpha, LhsMdspan &lhs, RhsMdspan &rhs, Scalar beta, ContractionAxes< LhsRank, RhsRank, ContractedRank > const &axes)
Execute a fixed-output pairwise contraction over resolved host mdspans.
Definition linalg/cpu/contract.hpp:294
KernelAttempt run(X &x, A &a, B &b, std::span< make_real_t< typename A::value_type > > residuals, LeastSquaresInfo &info, LeastSquaresOptions< make_real_t< typename A::value_type > > const &options, Precision p, Factor factor)
Definition least_squares_common.hpp:92
auto contraction_output_extents(Lhs const &lhs, Rhs const &rhs, ContractionAxes< LhsRank, RhsRank, ContractedRank > const &axes) -> stdex::dextents< uni20::index_type, ContractionAxes< LhsRank, RhsRank, ContractedRank >::output_rank >
Validate paired extents and return the canonical contraction output shape.
Definition contraction_axes.hpp:141
constexpr bool contraction_axes_are_valid(ContractionAxes< LhsRank, RhsRank, ContractedRank > const &axes) noexcept
Return whether contraction axes form canonical partitions of both inputs.
Definition contraction_axes.hpp:46
std::ptrdiff_t index_type
Definition types.hpp:19
mpreal beta(mpreal const &a, mpreal const &x)
Real Beta function; positive rational/integer pairs retain exact values.
Definition mpreal_special.hpp:164
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
constexpr bool tensor_extents_equal(LhsExtents const &lhs, RhsExtents const &rhs) noexcept
Compare two tensor extent objects by rank and per-axis extent.
Definition tensor/output.hpp:31
std::size_t span
Definition presentation.cpp:2017
Canonical contracted and surviving axes for two fixed-rank operands.
Definition contraction_axes.hpp:26
static constexpr std::size_t output_rank
Definition contraction_axes.hpp:35
static constexpr std::size_t lhs_surviving_rank
Definition contraction_axes.hpp:33
Shape preparation contracts for tensor operation outputs.
#define CHECK(cond,...)
Definition trace.hpp:299