7#include <guanaqo/export.h>
25 return static_cast<size_t>(i);
35 using std::logic_error::logic_error;
43template <
class From,
class To>
64 throw std::invalid_argument(
"Nonsquare matrix cannot be symmetric");
69 [[nodiscard]]
size_t work_size()
const {
return 0; }
74 [[nodiscard]] std::span<T>
76 std::span<std::remove_const_t<T>>)
const {
83template <
class Index,
class StorageIndex>
91 throw std::invalid_argument(
"Nonsquare matrix cannot be symmetric");
99 : from_sparsity(from),
sparsity(convert_sparsity(from, request)) {}
109 [[nodiscard]] std::span<T>
111 std::span<std::remove_const_t<T>> work)
const {
114 std::ranges::fill(work, T{});
122 for (
auto i = inner_start; i < inner_end; ++i) {
128 throw std::invalid_argument(
129 "Invalid symmetric CSC matrix: "
130 "upper-triangular matrix should not "
131 "have elements below the diagonal");
132 W(c, r) = W(r, c) = from_values[l];
136 throw std::invalid_argument(
137 "Invalid symmetric CSC matrix: "
138 "lower-triangular matrix should not "
139 "have elements above the diagonal");
140 W(c, r) = W(r, c) = from_values[l];
142 default:
throw std::invalid_argument(
"Invalid symmetry");
152template <
class Index>
158#if GUANAQO_SPARSE_SUPPORTS_SORTING
162 throw std::invalid_argument(
"Nonsquare matrix cannot be symmetric");
170 : from_sparsity(from),
sparsity(convert_sparsity(from, request)) {}
180 [[nodiscard]] std::span<T>
182 std::span<std::remove_const_t<T>> work)
const {
185 std::ranges::fill(work, T{});
186 auto W = [&](Index r, Index c) -> std::remove_const_t<T> & {
196 throw std::invalid_argument(
197 "Invalid symmetric COO matrix: upper-triangular "
199 "have elements below the diagonal");
200 W(c, r) = W(r, c) = from_values[l];
204 throw std::invalid_argument(
205 "Invalid symmetric COO matrix: lower-triangular "
207 "have elements above the diagonal");
208 W(c, r) = W(r, c) = from_values[l];
210 default:
throw std::invalid_argument(
"Invalid symmetry");
219template <
class Index>
226template <
class Index>
233 if (request.first_index)
234 Δ = *request.first_index;
251 throw std::invalid_argument(
252 "Nonsquare matrix cannot be symmetric");
258 for (
index_t r = 0; r <= c; ++r) {
266 throw std::invalid_argument(
267 "Lower-triangular symmetry currently not supported");
268 default:
throw std::invalid_argument(
"Invalid symmetry");
277 .first_index = request.first_index ? *request.first_index : 0,
281 :
sparsity(convert_sparsity(from, request)) {}
295 [[nodiscard]] std::span<T>
297 std::span<std::remove_const_t<T>> work)
const {
303 auto f_col_above_diag = [&](
index_t c) {
304 return std::span{from_values}.subspan(
307 auto t = work.begin();
309 std::ranges::copy_backward(f_col_above_diag(c), t += c + 1);
312 throw std::logic_error(
"SparsityConverter<Dense, "
313 "SparseCOO<Index>>: Unsupported symmetry");
319template <
class IndexFrom,
class StorageIndexFrom,
class IndexTo>
327 if (request.first_index)
328 Δ = *request.first_index;
335 for (
auto i = inner_start; i < inner_end; ++i) {
351 .first_index = request.first_index ? *request.first_index : 0,
355 :
sparsity(convert_sparsity(from, request)) {
356#if GUANAQO_SPARSE_SUPPORTS_SORTING
368 [[nodiscard]] std::span<T>
370 std::span<std::remove_const_t<T>>)
const {
377template <
class IndexFrom,
class IndexTo>
384 if (request.first_index)
385 Δ = *request.first_index -
static_cast<IndexTo
>(from.
first_index);
387 if constexpr (std::is_same_v<IndexFrom, IndexTo>)
393 auto cvt_idx = [Δ](
auto i) {
return static_cast<IndexTo
>(i) + Δ; };
403 .first_index = request.first_index
404 ? *request.first_index
409 :
sparsity(convert_sparsity(from, request)) {
410#if GUANAQO_SPARSE_SUPPORTS_SORTING
422 [[nodiscard]] std::span<T>
424 std::span<std::remove_const_t<T>>)
const {
431template <
class Index,
class StorageIndex>
434 std::optional<typename SparseCSC<Index, StorageIndex>::Order>
order =
439template <
class IndexFrom,
class IndexTo,
class StorageIndexTo>
446 [[maybe_unused]]
Request request) {
448 std::vector<IndexFrom> row_indices, col_indices;
449 [[maybe_unused]]
auto prepare_sort = [&] {
450 row_indices.resize(
cast_sz(from.nnz()));
451 col_indices.resize(
cast_sz(from.nnz()));
453 std::ranges::copy(from.row_indices, row_indices.begin());
454 std::ranges::copy(from.col_indices, col_indices.begin());
461 switch (from.order) {
469#if GUANAQO_SPARSE_SUPPORTS_SORTING
475 "Sorting is SparseCOO is not supported. Recompile with "
476 "C++23-compliant compiler");
478 default:
throw std::invalid_argument(
"Invalid order");
481 switch (from.order) {
493#if GUANAQO_SPARSE_SUPPORTS_SORTING
500 "Sorting is SparseCOO is not supported. Recompile with "
501 "C++23-compliant compiler");
503 default:
throw std::invalid_argument(
"Invalid order");
506 assert(!request.order || *request.order == order);
513 row_indices.empty() ? from.row_indices : std::span{row_indices},
514 col_indices.empty() ? from.col_indices : std::span{col_indices},
519 .symmetry = from.symmetry,
526 :
sparsity(convert_sparsity(from, request)) {
538 [[nodiscard]] std::span<T>
540 std::span<std::remove_const_t<T>> work)
const {
554template <
class IndexFrom,
class StorageIndexFrom,
class IndexTo,
555 class StorageIndexTo>
563 auto cvt_in = [](
auto i) {
return static_cast<IndexTo
>(i); };
564 auto cvt_out = [](
auto i) {
return static_cast<StorageIndexTo
>(i); };
565 auto convert_inner = [&] {
567 std::ranges::transform(from.inner_idx,
inner_idx.begin(), cvt_in);
569 auto convert_outer = [&] {
571 std::ranges::transform(from.outer_ptr,
outer_ptr.begin(), cvt_out);
573 auto sort_indices = [&] {
574#if GUANAQO_SPARSE_SUPPORTS_SORTING
580 "Sorting is SparseCSC is not supported. Recompile with "
581 "C++23-compliant compiler");
585 bool need_sorting =
false;
586 auto order =
static_cast<Order
>(from.order);
590 switch (from.order) {
593 default:
throw std::invalid_argument(
"Invalid order");
602 if constexpr (!std::is_same_v<IndexFrom, IndexTo>)
604 if constexpr (!std::is_same_v<StorageIndexFrom, StorageIndexTo>)
612 auto get_inner_idx = [&] {
613 if constexpr (std::is_same_v<IndexFrom, IndexTo>)
614 return inner_idx.empty() ? from.inner_idx
619 auto get_outer_ptr = [&] {
620 if constexpr (std::is_same_v<StorageIndexFrom, StorageIndexTo>)
621 return outer_ptr.empty() ? from.outer_ptr
629 .symmetry = from.symmetry,
630 .inner_idx = get_inner_idx(),
631 .outer_ptr = get_outer_ptr(),
636 :
sparsity(convert_sparsity(from, request)) {
648 [[nodiscard]] std::span<T>
650 std::span<std::remove_const_t<T>> work)
const {
664template <
class Index,
class StorageIndex>
671 auto cvt_out = [](
auto i) {
return static_cast<StorageIndex
>(i); };
672 auto cvt_in = [](
auto i) {
return static_cast<Index
>(i); };
673 switch (from.symmetry) {
679 for (
index_t c = 0; c < from.cols; ++c) {
681 for (
index_t r = 0; r < from.rows; ++r) {
689 if (from.rows != from.cols)
690 throw std::invalid_argument(
691 "Nonsquare matrix cannot be symmetric");
696 for (
index_t c = 0; c < from.cols; ++c) {
698 for (
index_t r = 0; r <= c; ++r) {
706 throw std::invalid_argument(
707 "Lower-triangular symmetry currently not supported");
708 default:
throw std::invalid_argument(
"Invalid symmetry");
713 .symmetry = from.symmetry,
720 :
sparsity(convert_sparsity(from, request)) {}
735 [[nodiscard]] std::span<T>
737 std::span<std::remove_const_t<T>> work)
const {
743 auto f_col_above_diag = [&](
index_t c) {
744 return std::span{from_values}.subspan(
747 auto t = work.begin();
749 std::ranges::copy_backward(f_col_above_diag(c), t += c + 1);
761template <
class To,
class>
764template <
class To,
class... Froms>
766 using type = std::variant<SparsityConverter<Froms, To>...>;
785 : converter{std::visit(wrap_converter(request), from.value)} {}
794 return std::visit([](
const auto &c) {
return c.work_size(); },
798 return std::visit([](
const auto &c) {
return c.nnz_from(); },
802 [[nodiscard]] std::span<T>
804 std::span<std::remove_const_t<T>> work)
const {
805 const auto visitor = [&](
const auto &c) {
815 template <
class T,
class E>
817 std::vector<T> work = {})
const {
818 const auto eval_size =
cast_sz(nnz_from());
821 if (work_size() == 0 && result.size() >= eval_size) {
822 std::forward<E>(evaluator)(result.first(eval_size));
823 const auto visitor = [&](
const auto &c) {
824 return c.template convert_values<T>(result, work);
826 [[maybe_unused]]
auto r = std::visit(visitor, converter);
827 assert(r.data() == result.data());
833 work.resize(eval_size);
834 std::forward<E>(evaluator)(std::span<T>{work});
835 const auto visitor = [&](
const auto &c) {
836 return c.template convert_values<T>(work, result);
838 [[maybe_unused]]
auto r = std::visit(visitor, converter);
839 assert(r.data() == result.data());
841 return std::move(work);
844 [[nodiscard]] std::vector<std::remove_const_t<T>>
848 if (result.data() != work.data())
849 work = {result.begin(), result.end()};
Common linear algebra index types.
std::optional< Index > first_index
Convert the index offset (zero for C/C++, one for Fortran).
detail::ConverterVariantHelper< To, SparsityVariant >::type ConverterVariant
Additional options for the conversion performed by SparsityConverter.
Converts one matrix storage format to another.
void sort_triplets(Ts &&...triplets)
Sort the (row, column, value) triplets, by column index first, then row.
bool check_uniqueness_csc(const Outer &outer_ptr, const Inner inner)
Check that no two entries with the same row and column index exist in the given sparse compressed-col...
void convert_triplets_to_ccs(const auto &rows, const auto &cols, auto &&inner_idx, auto &&outer_ptr, auto idx_0=0)
bool check_uniqueness_triplets(Ts &&...triplets)
Check that no two entries with the same row and column index exist in the given sparse coordinate lis...
void sort_rows_csc(const Outer &outer_ptr, Inners &&...inners)
Given a sparse compressed-column storage matrix, sort all row indices within each column.
void sort_triplets_col(Ts &&...triplets)
Sort the (row, column, value) triplets by column index.
@ Unsymmetric
No symmetry.
@ Upper
Symmetric, upper-triangular part is stored.
@ Lower
Symmetric, lower-triangular part is stored.
std::variant< SparsityConverter< Froms, To >... > type
constexpr size_t cast_sz(auto i)
Helpers for sparse matrix index ordering and conversions.
Sparse and dense sparsity descriptors.
Sparse coordinate list structure (COO).
std::span< const index_t > row_indices
length_t nnz() const
Get the number of structurally nonzero elements.
std::span< const index_t > col_indices
index_t first_index
Zero for C/C++, one for Fortran.
Sparse compressed-column structure (CCS or CSC).
std::span< const index_t > inner_idx
length_t nnz() const
Get the number of structurally nonzero elements.
std::span< const storage_index_t > outer_ptr
std::optional< typename SparseCSC< Index, StorageIndex >::Order > order
Sort the indices.
length_t nnz_from() const
SparsityConversionRequest< to_sparsity_t > Request
std::span< T > convert_values(std::span< T > from_values, std::span< std::remove_const_t< T > >) const
SparsityConverter(from_sparsity_t from, Request={})
const to_sparsity_t & get_sparsity() const
length_t nnz_from() const
std::vector< Index > col_indices
SparseCOO< Index > to_sparsity_t
std::vector< Index > row_indices
const to_sparsity_t & get_sparsity() const
SparsityConversionRequest< to_sparsity_t > Request
SparsityConverter(from_sparsity_t from, Request request={})
std::span< T > convert_values(std::span< T > from_values, std::span< std::remove_const_t< T > > work) const
to_sparsity_t convert_sparsity(from_sparsity_t from, Request request)
std::vector< Index > inner_idx
SparsityConversionRequest< to_sparsity_t > Request
SparsityConverter(from_sparsity_t from, Request request={})
std::span< T > convert_values(std::span< T > from_values, std::span< std::remove_const_t< T > > work) const
SparseCSC< Index, StorageIndex > to_sparsity_t
std::vector< StorageIndex > outer_ptr
to_sparsity_t convert_sparsity(from_sparsity_t from, Request)
length_t nnz_from() const
const to_sparsity_t & get_sparsity() const
SparseCOO< IndexFrom > from_sparsity_t
std::vector< StorageIndexTo > outer_ptr
std::vector< StorageIndexTo > permutation
std::vector< IndexTo > inner_idx
to_sparsity_t convert_sparsity(from_sparsity_t from, Request request)
const to_sparsity_t & get_sparsity() const
SparseCSC< IndexTo, StorageIndexTo > to_sparsity_t
std::span< T > convert_values(std::span< T > from_values, std::span< std::remove_const_t< T > > work) const
SparsityConversionRequest< to_sparsity_t > Request
SparsityConverter(from_sparsity_t from, Request request={})
length_t nnz_from() const
to_sparsity_t convert_sparsity(from_sparsity_t from, Request request)
std::span< T > convert_values(std::span< T > from_values, std::span< std::remove_const_t< T > >) const
SparseCOO< IndexTo > to_sparsity_t
length_t nnz_from() const
SparsityConverter(from_sparsity_t from, Request request={})
SparseCOO< IndexFrom > from_sparsity_t
std::vector< IndexTo > row_indices
SparsityConversionRequest< to_sparsity_t > Request
const to_sparsity_t & get_sparsity() const
std::vector< IndexTo > col_indices
SparsityConversionRequest< to_sparsity_t > Request
std::span< T > convert_values(std::span< T > from_values, std::span< std::remove_const_t< T > > work) const
length_t nnz_from() const
to_sparsity_t convert_sparsity(from_sparsity_t from, Request)
SparseCOO< Index > from_sparsity_t
const to_sparsity_t & get_sparsity() const
from_sparsity_t from_sparsity
SparsityConverter(from_sparsity_t from, Request request={})
length_t nnz_from() const
SparseCSC< IndexFrom, StorageIndexFrom > from_sparsity_t
SparsityConverter(from_sparsity_t from, Request request={})
SparseCSC< IndexTo, StorageIndexTo > to_sparsity_t
std::vector< StorageIndexTo > permutation
to_sparsity_t convert_sparsity(from_sparsity_t from, Request request)
std::vector< IndexTo > inner_idx
const to_sparsity_t & get_sparsity() const
SparsityConversionRequest< to_sparsity_t > Request
std::vector< StorageIndexTo > outer_ptr
std::span< T > convert_values(std::span< T > from_values, std::span< std::remove_const_t< T > > work) const
const to_sparsity_t & get_sparsity() const
std::vector< IndexTo > row_indices
std::span< T > convert_values(std::span< T > from_values, std::span< std::remove_const_t< T > >) const
SparsityConverter(from_sparsity_t from, Request request={})
to_sparsity_t convert_sparsity(from_sparsity_t from, Request request)
SparsityConversionRequest< to_sparsity_t > Request
SparseCSC< IndexFrom, StorageIndexFrom > from_sparsity_t
length_t nnz_from() const
std::vector< IndexTo > col_indices
SparseCOO< IndexTo > to_sparsity_t
to_sparsity_t convert_sparsity(from_sparsity_t from, Request)
from_sparsity_t from_sparsity
SparsityConversionRequest< to_sparsity_t > Request
const to_sparsity_t & get_sparsity() const
SparsityConverter(from_sparsity_t from, Request request={})
SparseCSC< Index, StorageIndex > from_sparsity_t
length_t nnz_from() const
std::span< T > convert_values(std::span< T > from_values, std::span< std::remove_const_t< T > > work) const
std::vector< std::remove_const_t< T > > convert_values_copy(std::span< T > from_values) const
ConverterVariant< To > converter
SparsityConversionRequest< to_sparsity_t > Request
std::vector< T > convert_values_into(std::span< T > result, E &&evaluator, std::vector< T > work={}) const
Call evaluator, then convert the values and write the result into result, while minimizing the number...
std::span< T > convert_values(std::span< T > from_values, std::span< std::remove_const_t< T > > work) const
const to_sparsity_t & get_sparsity() const
SparsityConverter(Sparsity from, Request request={})
static auto wrap_converter(Request request)
length_t nnz_from() const
Stores any of the supported sparsity patterns.
The requested conversion between sparsity structures could not be performed (most likely because cert...