Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add csc2csr #1342

Merged
merged 8 commits into from
Mar 25, 2022
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
249 changes: 249 additions & 0 deletions src/sparse/KokkosSparse_csc2csr.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,249 @@
/*
//@HEADER
// ************************************************************************
//
// Kokkos v. 3.0
// Copyright (2020) National Technology & Engineering
// Solutions of Sandia, LLC (NTESS).
//
// Under the terms of Contract DE-NA0003525 with NTESS,
// the U.S. Government retains certain rights in this software.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// 3. Neither the name of the Corporation nor the names of the
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY NTESS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NTESS OR THE
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Questions? Contact Siva Rajamanickam (srajama@sandia.gov)
//
// ************************************************************************
//@HEADER
*/

#include "KokkosKernels_Utils.hpp"
#include <std_algorithms/Kokkos_BeginEnd.hpp>
#include <std_algorithms/Kokkos_Numeric.hpp>

#ifndef _KOKKOSSPARSE_CSC2CSR_HPP
#define _KOKKOSSPARSE_CSC2CSR_HPP
namespace KokkosSparse {
namespace Impl {
template <class OrdinalType, class SizeType, class ValViewType,
class RowIdViewType, class ColMapViewType>
class Csc2Csr {
private:
using CrsST = typename ValViewType::value_type;
using CrsOT = OrdinalType;
using CrsET = typename ValViewType::execution_space;
using CrsMT = void;
using CrsSzT = SizeType;
using CrsType = CrsMatrix<CrsST, CrsOT, CrsET, CrsMT, CrsSzT>;
using CrsValsViewType = typename CrsType::values_type;
using CrsRowMapViewType = typename CrsType::row_map_type::non_const_type;
using CrsColIdViewType = typename CrsType::index_type;

OrdinalType __nrows;
OrdinalType __ncols;
SizeType __nnz;
ValViewType __vals;
RowIdViewType __row_ids;
ColMapViewType __col_map;

RowIdViewType __crs_row_cnt;

CrsValsViewType __crs_vals;
CrsRowMapViewType __crs_row_map;
CrsRowMapViewType __crs_row_map_scratch;
CrsColIdViewType __crs_col_ids;

struct AlgoTags {
struct s1RowCnt {};
struct s2RowMap {};
struct s3Copy {};
};

using s1RowCntTag = typename AlgoTags::s1RowCnt;
using s3CopyTag = typename AlgoTags::s3Copy;

using TeamPolicyType = Kokkos::TeamPolicy<s3CopyTag, CrsET>;

int __suggested_team_size, __suggested_vec_size, __league_size;

template <class FunctorType>
void __run(FunctorType &functor) {
// s1RowCntTag
{
Kokkos::parallel_for("Csc2Csr",
Kokkos::RangePolicy<s1RowCntTag, CrsET>(0, __nnz),
functor);
CrsET().fence();
}
// s2RowMapTag
{
namespace KE = Kokkos::Experimental;
CrsET crsET;
KE::inclusive_scan(crsET, KE::cbegin(__crs_row_cnt),
KE::cend(__crs_row_cnt), KE::begin(__crs_row_map) + 1);
__crs_row_map(0) = 0;
assert(__crs_row_map(__nrows) == __nnz);
CrsET().fence();
Kokkos::deep_copy(__crs_row_map_scratch, __crs_row_map);
CrsET().fence();
}
// s3CopyTag
{
TeamPolicyType teamPolicy(__ncols, __suggested_team_size,
__suggested_vec_size);
Kokkos::parallel_for("Csc2Csr", teamPolicy, functor);
CrsET().fence();
}
// TODO: s3CopySortCompressTag
}

public:
template <class MemberType>
class __Functor {
private:
OrdinalType __nrows;
OrdinalType __ncols;
SizeType __nnz;
ValViewType &__vals;
CrsValsViewType &__crs_vals;
RowIdViewType &__row_ids;
CrsRowMapViewType &__crs_row_map;
CrsRowMapViewType &__crs_row_map_scratch;
ColMapViewType &__col_map;
CrsColIdViewType &__crs_col_ids;
RowIdViewType &__crs_row_cnt;

public:
__Functor(OrdinalType nrows, OrdinalType ncols, SizeType nnz,
ValViewType &vals, CrsValsViewType &crs_vals,
RowIdViewType &row_ids, CrsRowMapViewType &crs_row_map,
CrsRowMapViewType &crs_row_map_scratch, ColMapViewType &col_map,
CrsColIdViewType &crs_col_ids, RowIdViewType &crs_row_cnt)
: __nrows(nrows),
__ncols(ncols),
__nnz(nnz),
__vals(vals),
__crs_vals(crs_vals),
__row_ids(row_ids),
__crs_row_map(crs_row_map),
__crs_row_map_scratch(crs_row_map_scratch),
__col_map(col_map),
__crs_col_ids(crs_col_ids),
__crs_row_cnt(crs_row_cnt){};

KOKKOS_INLINE_FUNCTION
void operator()(const s3CopyTag &, const MemberType &member) const {
auto j = member.league_rank();
auto col_start = __col_map(j);
auto col_len = __col_map(j + 1) - col_start;

Kokkos::parallel_for(
Kokkos::TeamVectorRange(member, 0, col_len), [&](const int &k) {
auto idx = col_start + k;
auto i = __row_ids(idx);
auto crs_idx =
Kokkos::atomic_fetch_inc(&__crs_row_map_scratch.data()[i]);
__crs_col_ids(crs_idx) = j;
__crs_vals(crs_idx) = __vals(idx);
});
}

KOKKOS_INLINE_FUNCTION
void operator()(const s1RowCntTag &, const int &thread_id) const {
Kokkos::atomic_inc(&__crs_row_cnt.data()[__row_ids(thread_id)]);
}
};

Csc2Csr(OrdinalType nrows, OrdinalType ncols, SizeType nnz, ValViewType vals,
RowIdViewType row_ids, ColMapViewType col_map, int league_size = 2)
: __nrows(nrows),
__ncols(ncols),
__nnz(nnz),
__vals(vals),
__row_ids(row_ids),
__col_map(col_map),
__league_size(league_size) {
__crs_vals = CrsValsViewType(
Kokkos::view_alloc(Kokkos::WithoutInitializing, "__crs_vals"), nnz);
__crs_row_map = CrsRowMapViewType(
Kokkos::view_alloc(Kokkos::WithoutInitializing, "__crs_row_map"),
nrows + 1);
__crs_row_map_scratch =
CrsRowMapViewType(Kokkos::view_alloc(Kokkos::WithoutInitializing,
"__crs_row_map_scratch"),
nrows + 1);
__crs_col_ids = CrsColIdViewType(
Kokkos::view_alloc(Kokkos::WithoutInitializing, "__crs_col_ids"), nnz);

__crs_row_cnt = RowIdViewType("__crs_row_cnt", __nrows);

__Functor<typename TeamPolicyType::member_type> functor(
__nrows, __ncols, __nnz, __vals, __crs_vals, __row_ids, __crs_row_map,
__crs_row_map_scratch, __col_map, __crs_col_ids, __crs_row_cnt);

KokkosKernels::Impl::get_suggested_vector_size<int64_t, CrsET>(
__suggested_vec_size, __nrows, __nnz);
__suggested_team_size =
KokkosKernels::Impl::get_suggested_team_size<TeamPolicyType>(
functor, __suggested_vec_size);

__run(functor);
}

CrsType get_csrMat() {
return CrsType("csc2csr", __nrows, __ncols, __nnz, __crs_vals,
__crs_row_map, __crs_col_ids);
}
};
} // namespace Impl
///
/// \brief Converts a csc matrix to a CrsMatrix.
/// \tparam OrdinalType The view value type associated with the RowIdViewType
/// \tparam SizeType The type of nnz
/// \tparam ValViewType The values view type
/// \tparam RowIdViewType The row ids view type
/// \tparam ColMapViewType The column map view type
/// \param nrows The number of rows in the csc matrix
/// \param ncols The number of columns in the csc matrix
/// \param nnz The number of non-zeros in the csc matrix
/// \param vals The values view of the csc matrix
/// \param row_ids The row ids view of the csc matrix
/// \param col_map The column map view of the csc matrix
/// \return A KokkosSparse::CrsMatrix.
template <class OrdinalType, class SizeType, class ValViewType,
class RowIdViewType, class ColMapViewType>
auto csc2csr(OrdinalType nrows, OrdinalType ncols, SizeType nnz,
ValViewType vals, RowIdViewType row_ids, ColMapViewType col_map,
int league_size) {
using Csc2csrType = Impl::Csc2Csr<OrdinalType, SizeType, ValViewType,
RowIdViewType, ColMapViewType>;
Csc2csrType csc2Csr(nrows, ncols, nnz, vals, row_ids, col_map, league_size);
return csc2Csr.get_csrMat();
}
} // namespace KokkosSparse
#endif // _KOKKOSSPARSE_CSC2CSR_HPP
4 changes: 2 additions & 2 deletions src/sparse/impl/KokkosSparse_spmv_impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ struct SPMV_Transpose_Functor {
AMatrix m_A;
XVector m_x;
YVector m_y;
ordinal_type rows_per_team;
ordinal_type rows_per_team = 0;

SPMV_Transpose_Functor(const coefficient_type& alpha_, const AMatrix& m_A_,
const XVector& m_x_, const YVector& m_y_)
Expand Down Expand Up @@ -725,7 +725,7 @@ struct SPMV_MV_Transpose_Functor {
YVector m_y;

const ordinal_type n;
ordinal_type rows_per_team;
ordinal_type rows_per_team = 0;

SPMV_MV_Transpose_Functor(const coefficient_type& alpha_, const AMatrix& m_A_,
const XVector& m_x_, const coefficient_type& beta_,
Expand Down
110 changes: 110 additions & 0 deletions test_common/KokkosKernels_TestUtils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@
#ifndef KOKKOSKERNELS_TEST_UTILS_HPP
#define KOKKOSKERNELS_TEST_UTILS_HPP

#include <random>

#include "KokkosKernels_Utils.hpp"
#include "Kokkos_ArithTraits.hpp"
#include "KokkosSparse_spmv.hpp"
Expand Down Expand Up @@ -488,5 +490,113 @@ int string_compare_no_case(const char* str1, const char* str2) {
return strcmp(str1_s.c_str(), str2_s.c_str());
}

/// /brief Csc matrix class for testing purposes.
/// \tparam ScalarType
/// \tparam LayoutType
/// \tparam ExeSpaceType
template <class ScalarType, class LayoutType, class ExeSpaceType>
class RandCscMat {
private:
using ValViewType = Kokkos::View<ScalarType*, LayoutType, ExeSpaceType>;
using RowIdViewType = Kokkos::View<int64_t*, LayoutType, ExeSpaceType>;
using ColMapViewType = Kokkos::View<int64_t*, LayoutType, ExeSpaceType>;
int64_t __nrows;
int64_t __ncols;
int64_t __nnz = 0;
ColMapViewType __col_map;
RowIdViewType __row_ids;
ValViewType __vals;
bool __fully_sparse;

/// Generates a random column map where:
/// 1. __col_map(i) is in [__row_ids.data(), &row_ids.data()[nnz - 1]
/// 2. __col_map(i) > col_map(i - 1) for i > 1
/// 3. __col_map(i) == col_map(j) iff __col_map(i) == col_map(j) == nullptr
/// 4. __col_map(i) - col_map(i - 1) is in [0, m]
void __populate_random_csc_mat(uint64_t ticks) {
std::srand(ticks);
for (int64_t col_idx = 0; col_idx < __ncols; col_idx++) {
int64_t r = std::rand() % (__nrows + 1);
if (r == 0 || __fully_sparse) { // 100% sparse column
__col_map(col_idx) = __nnz;
} else { // sparse column with r elements
// Populate r row ids
std::vector<int64_t> v(r);

for (int64_t i = 0; i < r; i++) v.at(i) = i;

std::shuffle(v.begin(), v.end(), std::mt19937(std::random_device()()));

for (int64_t i = 0; i < r; i++) __row_ids(i + __nnz) = v.at(i);

// Point to new column and accumulate number of non zeros
__col_map(col_idx) = __nnz;
__nnz += r;
}
}

// last entry in map points to end of row id list
__col_map(__ncols) = __nnz;
}

template <class T>
T __getter_copy_helper(T src) {
T dst(std::string("RandCscMat.") + typeid(T).name() + " copy",
src.extent(0));
Kokkos::deep_copy(dst, src);
ExeSpaceType().fence();
return dst;
}

public:
std::string info;
/// Constructs a random csc matrix.
/// \param m The number of rows.
/// \param n The number of columns.
/// \param min_val The minimum scalar value in the matrix.
/// \param max_val The maximum scalar value in the matrix.
RandCscMat(int64_t m, int64_t n, ScalarType min_val, ScalarType max_val,
bool fully_sparse = false) {
__ncols = n;
__nrows = m;
__fully_sparse = fully_sparse;
__col_map = ColMapViewType("RandCscMat.ColMapViewType", __ncols + 1);
__row_ids =
RowIdViewType("RandCscMat.RowIdViewType", m * n + 1); // over-allocated

uint64_t ticks =
std::chrono::high_resolution_clock::now().time_since_epoch().count() %
UINT32_MAX;

info = std::string(
std::string("RandCscMat<") + typeid(ScalarType).name() + ", " +
typeid(LayoutType).name() + ", " + typeid(ExeSpaceType).name() + ">(" +
std::to_string(m) + ", " + std::to_string(n) +
"...): rand seed: " + std::to_string(ticks) +
", fully sparse: " + (__fully_sparse ? "true" : "false") + "\n");
Kokkos::Random_XorShift64_Pool<ExeSpaceType> random(ticks);
__populate_random_csc_mat(ticks);

__vals = ValViewType("RandCscMat.ValViewType", __nnz + 1);
Kokkos::fill_random(__vals, random, min_val, max_val); // random scalars
ExeSpaceType().fence();
__vals(__nnz) = ScalarType(0);
}

// O(c), where c is a constant.
ScalarType operator()(int64_t idx) { return __vals(idx); }

int64_t get_nnz() { return __nnz; }
int64_t get_m() { return __nrows; }
int64_t get_n() { return __ncols; }
int64_t get_col_len(int64_t j) {
return j < __ncols ? (__col_map(j + 1) - __col_map(j)) : 0;
}
int64_t get_col_start(int64_t j) { return j < __ncols ? __col_map(j) : 0; }
ValViewType get_vals() { return __getter_copy_helper(__vals); }
RowIdViewType get_row_ids() { return __getter_copy_helper(__row_ids); }
ColMapViewType get_col_map() { return __getter_copy_helper(__col_map); }
};

} // namespace Test
#endif
Loading