# 28 Numerics library [numerics]

## 28.9 Basic linear algebra algorithms [linalg]

### 28.9.7 Exposition-only helpers [linalg.helpers]

#### 28.9.7.1abs-if-needed[linalg.helpers.abs]

The name abs-if-needed denotes an exposition-only function object.
The expression abs-if-needed(E) for a subexpression E whose type is T is expression-equivalent to:
• E if T is an unsigned integer;
• otherwise, std​::​abs(E) if T is an arithmetic type,
• otherwise, abs(E), if that expression is valid, with overload resolution performed in a context that includes the declaration template<class T> T abs(T) = delete;
If the function selected by overload resolution does not return the absolute value of its input, the program is ill-formed, no diagnostic required.

#### 28.9.7.2conj-if-needed[linalg.helpers.conj]

The name conj-if-needed denotes an exposition-only function object.
The expression conj-if-needed(E) for a subexpression E whose type is T is expression-equivalent to:
• conj(E), if T is not an arithmetic type and the expression conj(E) is valid, with overload resolution performed in a context that includes the declaration template<class T> T conj(const T&) = delete;
If the function selected by overload resolution does not return the complex conjugate of its input, the program is ill-formed, no diagnostic required;
• otherwise, E.

#### 28.9.7.3real-if-needed[linalg.helpers.real]

The name real-if-needed denotes an exposition-only function object.
The expression real-if-needed(E) for a subexpression E whose type is T is expression-equivalent to:
• real(E), if T is not an arithmetic type and the expression real(E) is valid, with overload resolution performed in a context that includes the declaration template<class T> T real(const T&) = delete;
If the function selected by overload resolution does not return the real part of its input, the program is ill-formed, no diagnostic required;
• otherwise, E.

#### 28.9.7.4imag-if-needed[linalg.helpers.imag]

The name imag-if-needed denotes an exposition-only function object.
The expression imag-if-needed(E) for a subexpression E whose type is T is expression-equivalent to:
• imag(E), if T is not an arithmetic type and the expression imag(E) is valid, with overload resolution performed in a context that includes the declaration template<class T> T imag(const T&) = delete;
If the function selected by overload resolution does not return the imaginary part of its input, the program is ill-formed, no diagnostic required;
• otherwise, ((void)E, T{}).

#### 28.9.7.5 Argument concepts [linalg.helpers.concepts]

The exposition-only concepts defined in this section constrain the algorithms in [linalg].
template<class T> constexpr bool is-mdspan = false; template<class ElementType, class Extents, class Layout, class Accessor> constexpr bool is-mdspan<mdspan<ElementType, Extents, Layout, Accessor>> = true; template<class T> concept in-vector = is-mdspan<T> && T::rank() == 1; template<class T> concept out-vector = is-mdspan<T> && T::rank() == 1 && is_assignable_v<typename T::reference, typename T::element_type> && T::is_always_unique(); template<class T> concept inout-vector = is-mdspan<T> && T::rank() == 1 && is_assignable_v<typename T::reference, typename T::element_type> && T::is_always_unique(); template<class T> concept in-matrix = is-mdspan<T> && T::rank() == 2; template<class T> concept out-matrix = is-mdspan<T> && T::rank() == 2 && is_assignable_v<typename T::reference, typename T::element_type> && T::is_always_unique(); template<class T> concept inout-matrix = is-mdspan<T> && T::rank() == 2 && is_assignable_v<typename T::reference, typename T::element_type> && T::is_always_unique(); template<class T> constexpr bool is-layout-blas-packed = false; // exposition only template<class Triangle, class StorageOrder> constexpr bool is-layout-blas-packed<layout_blas_packed<Triangle, StorageOrder>> = true; template<class T> concept possibly-packed-inout-matrix = is-mdspan<T> && T::rank() == 2 && is_assignable_v<typename T::reference, typename T::element_type> && (T::is_always_unique() || is-layout-blas-packed<typename T::layout_type>); template<class T> concept in-object = is-mdspan<T> && (T::rank() == 1 || T::rank() == 2); template<class T> concept out-object = is-mdspan<T> && (T::rank() == 1 || T::rank() == 2) && is_assignable_v<typename T::reference, typename T::element_type> && T::is_always_unique(); template<class T> concept inout-object = is-mdspan<T> && (T::rank() == 1 || T::rank() == 2) && is_assignable_v<typename T::reference, typename T::element_type> && T::is_always_unique();
If a function in [linalg] accesses the elements of a parameter constrained by in-vector, in-matrix, or in-object, those accesses will not modify the elements.
Unless explicitly permitted, any inout-vector, inout-matrix, inout-object, out-vector, out-matrix, out-object, or possibly-packed-inout-matrix parameter of a function in [linalg] shall not overlap any other mdspan parameter of the function.

#### 28.9.7.6 Mandates [linalg.helpers.mandates]

[Note 1:
These exposition-only helper functions use the less constraining input concepts even for the output arguments, because the additional constraint for assignability of elements is not necessary, and they are sometimes used in a context where the third argument is an input type too.
— end note]
template<class MDS1, class MDS2> requires(is-mdspan<MDS1> && is-mdspan<MDS2>) constexpr bool compatible-static-extents(size_t r1, size_t r2) { // exposition only return MDS1::static_extent(r1) == dynamic_extent || MDS2::static_extent(r2) == dynamic_extent || MDS1::static_extent(r1) == MDS2::static_extent(r2)); } template<in-vector In1, in-vector In2, in-vector Out> constexpr bool possibly-addable() { // exposition only return compatible-static-extents<Out, In1>(0, 0) && compatible-static-extents<Out, In2>(0, 0) && compatible-static-extents<In1, In2>(0, 0); } template<in-matrix In1, in-matrix In2, in-matrix Out> constexpr bool possibly-addable() { // exposition only return compatible-static-extents<Out, In1>(0, 0) && compatible-static-extents<Out, In1>(1, 1) && compatible-static-extents<Out, In2>(0, 0) && compatible-static-extents<Out, In2>(1, 1) && compatible-static-extents<In1, In2>(0, 0) && compatible-static-extents<In1, In2>(1, 1); } template<in-matrix InMat, in-vector InVec, in-vector OutVec> constexpr bool possibly-multipliable() { // exposition only return compatible-static-extents<OutVec, InMat>(0, 0) && compatible-static-extents<InMat, InVec>(1, 0); } template<in-vector InVec, in-matrix InMat, in-vector OutVec> constexpr bool possibly-multipliable() { // exposition only return compatible-static-extents<OutVec, InMat>(0, 1) && compatible-static-extents<InMat, InVec>(0, 0); } template<in-matrix InMat1, in-matrix InMat2, in-matrix OutMat> constexpr bool possibly-multipliable() { // exposition only return compatible-static-extents<OutMat, InMat1>(0, 0) && compatible-static-extents<OutMat, InMat2>(1, 1) && compatible-static-extents<InMat1, InMat2>(1, 0); }

#### 28.9.7.7 Preconditions [linalg.helpers.precond]

[Note 1:
These exposition-only helper functions use the less constraining input concepts even for the output arguments, because the additional constraint for assignability of elements is not necessary, and they are sometimes used in a context where the third argument is an input type too.
— end note]
constexpr bool addable( // exposition only const in-vector auto& in1, const in-vector auto& in2, const in-vector auto& out) { return out.extent(0) == in1.extent(0) && out.extent(0) == in2.extent(0); } constexpr bool addable( // exposition only const in-matrix auto& in1, const in-matrix auto& in2, const in-matrix auto& out) { return out.extent(0) == in1.extent(0) && out.extent(1) == in1.extent(1) && out.extent(0) == in2.extent(0) && out.extent(1) == in2.extent(1); } constexpr bool multipliable( // exposition only const in-matrix auto& in_mat, const in-vector auto& in_vec, const in-vector auto& out_vec) { return out_vec.extent(0) == in_mat.extent(0) && in_mat.extent(1) == in_vec.extent(0); } constexpr bool multipliable( // exposition only const in-vector auto& in_vec, const in-matrix auto& in_mat, const in-vector auto& out_vec) { return out_vec.extent(0) == in_mat.extent(1) && in_mat.extent(0) == in_vec.extent(0); } constexpr bool multipliable( // exposition only const in-matrix auto& in_mat1, const in-matrix auto& in_mat2, const in-matrix auto& out_mat) { return out_mat.extent(0) == in_mat1.extent(0) && out_mat.extent(1) == in_mat2.extent(1) && in1_mat.extent(1) == in_mat2.extent(0); }