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 assertion for sparsity pattern equality #416

Merged
merged 5 commits into from
Dec 11, 2019
Merged
Show file tree
Hide file tree
Changes from all 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
142 changes: 142 additions & 0 deletions core/test/utils/assertions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,15 @@ void print_componentwise_error(Ostream &os, const MatrixData1 &first,
}
}

template <typename Ostream, typename Iterator>
void print_columns(Ostream &os, const Iterator &begin, const Iterator &end)
{
for (auto it = begin; it != end; ++it) {
os << '\t' << it->column;
}
os << '\n';
}


template <typename MatrixData1, typename MatrixData2>
double get_relative_error(const MatrixData1 &first, const MatrixData2 &second)
Expand Down Expand Up @@ -166,6 +175,61 @@ ::testing::AssertionResult matrices_near_impl(
}


template <typename MatrixData1, typename MatrixData2>
::testing::AssertionResult matrices_equal_sparsity_impl(
const std::string &first_expression, const std::string &second_expression,
const MatrixData1 &first, const MatrixData2 &second)
{
auto num_rows = first.size[0];
auto num_cols = first.size[1];
if (num_rows != second.size[0] || num_cols != second.size[1]) {
return ::testing::AssertionFailure()
<< "Expected matrices of equal size\n\t" << first_expression
<< " is of size [" << num_rows << " x " << num_cols << "]\n\t"
<< second_expression << " is of size [" << second.size[0]
<< " x " << second.size[1] << "]";
}

auto fst_it = begin(first.nonzeros);
auto snd_it = begin(second.nonzeros);
auto fst_end = end(first.nonzeros);
auto snd_end = end(second.nonzeros);
using nz_type_f = typename std::decay<decltype(*fst_it)>::type;
using nz_type_s = typename std::decay<decltype(*snd_it)>::type;
for (size_type row = 0; row < num_rows; ++row) {
auto cmp_l_f = [](nz_type_f nz, size_type row) { return nz.row < row; };
auto cmp_u_f = [](size_type row, nz_type_f nz) { return row < nz.row; };
auto cmp_l_s = [](nz_type_s nz, size_type row) { return nz.row < row; };
auto cmp_u_s = [](size_type row, nz_type_s nz) { return row < nz.row; };
auto col_eq = [](nz_type_f a, nz_type_s b) {
return a.column == b.column;
};
auto fst_row_begin = std::lower_bound(fst_it, fst_end, row, cmp_l_f);
auto snd_row_begin = std::lower_bound(snd_it, snd_end, row, cmp_l_s);
auto fst_row_end =
std::upper_bound(fst_row_begin, fst_end, row, cmp_u_f);
auto snd_row_end =
std::upper_bound(snd_row_begin, snd_end, row, cmp_u_s);
if (std::distance(fst_row_begin, fst_row_end) !=
pratikvn marked this conversation as resolved.
Show resolved Hide resolved
std::distance(snd_row_begin, snd_row_end) ||
!std::equal(fst_row_begin, fst_row_end, snd_row_begin, col_eq)) {
auto fail = ::testing::AssertionFailure();
fail << "Sparsity pattern differs between " << first_expression
<< " and " << second_expression << "\n\tIn row " << row << " "
<< first_expression << " has columns:\n";
detail::print_columns(fail, fst_row_begin, fst_row_end);
fail << " and " << second_expression << " has columns:\n";
detail::print_columns(fail, snd_row_begin, snd_row_end);
return fail;
}
fst_it = fst_row_end;
snd_it = snd_row_end;
}

return ::testing::AssertionSuccess();
}


template <typename ValueType>
::testing::AssertionResult array_equal_impl(
const std::string &first_expression, const std::string &second_expression,
Expand Down Expand Up @@ -361,6 +425,52 @@ ::testing::AssertionResult str_contains(const std::string &first_expression,
}


/**
* This is a gtest predicate which checks if two matrices have the same sparsity
* pattern.
*
* This means that hat mtx1 and mtx2 have exactly the same non-zero locations
* (including zero values!)
*
* This function should not be called directly, but used in conjunction with
* `ASSERT_PRED_FORMAT2` as follows:
*
* ```
* // Check if first and second are equal
* ASSERT_PRED_FORMAT2(gko::test::assertions::matrices_equal_sparsity,
* first, second);
* // Check if first and second are not equal
* ASSERT_PRED_FORMAT2(!gko::test::assertions::matrices_equal_sparsity,
* first, second);
* ```
*
* @see GKO_ASSERT_MTX_NEAR
* @see GKO_EXPECT_MTX_NEAR
*/
template <typename LinOp1, typename LinOp2>
::testing::AssertionResult matrices_equal_sparsity(
const std::string &first_expression, const std::string &second_expression,
const LinOp1 *first, const LinOp2 *second)
{
auto exec = first->get_executor()->get_master();
matrix_data<typename LinOp1::value_type, typename LinOp1::index_type>
first_data;
matrix_data<typename LinOp2::value_type, typename LinOp2::index_type>
second_data;

first->write(first_data);
second->write(second_data);

first_data.ensure_row_major_order();
second_data.ensure_row_major_order();

return detail::matrices_equal_sparsity_impl(
detail::remove_pointer_wrapper(first_expression),
detail::remove_pointer_wrapper(second_expression), first_data,
second_data);
}


namespace detail {


Expand Down Expand Up @@ -444,6 +554,38 @@ T plain_ptr(T ptr)
plain_ptr(_mtx1), plain_ptr(_mtx2), _tol); \
}

/**
* Checks if two matrices have the same sparsity pattern.
*
* This means that mtx1 and mtx2 have exactly the same non-zero locations
* (including zero values!)
*
* Has to be called from within a google test unit test.
* Internally calls gko::test::assertions::matrices_equal_sparsity().
*
* @param _mtx1 first matrix
* @param _mtx2 second matrix
*/
#define GKO_ASSERT_MTX_EQ_SPARSITY(_mtx1, _mtx2) \
{ \
using ::gko::test::assertions::detail::l; \
using ::gko::test::assertions::detail::plain_ptr; \
ASSERT_PRED_FORMAT2(::gko::test::assertions::matrices_equal_sparsity, \
plain_ptr(_mtx1), plain_ptr(_mtx2)); \
}


/**
* @copydoc GKO_ASSERT_MTX_EQ_SPARSITY
*/
#define GKO_EXPECT_MTX_EQ_SPARSITY(_mtx1, _mtx2) \
{ \
using ::gko::test::assertions::detail::l; \
using ::gko::test::assertions::detail::plain_ptr; \
EXPECT_PRED_FORMAT2(::gko::test::assertions::matrices_equal_sparsity, \
plain_ptr(_mtx1), plain_ptr(_mtx2)); \
}


/**
* Checks if two `gko::Array`s are equal.
Expand Down
55 changes: 48 additions & 7 deletions core/test/utils/assertions_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <gtest/gtest.h>


#include <ginkgo/core/matrix/csr.hpp>
#include <ginkgo/core/matrix/dense.hpp>


Expand All @@ -45,34 +46,70 @@ namespace {
class MatricesNear : public ::testing::Test {
protected:
using Mtx = gko::matrix::Dense<>;
using Sparse = gko::matrix::Csr<>;

template <typename Type, std::size_t size>
gko::Array<Type> make_view(std::array<Type, size> &array)
{
return gko::Array<Type>::view(exec, size, array.data());
}

MatricesNear()
: exec(gko::ReferenceExecutor::create()),
mtx1(gko::initialize<gko::matrix::Dense<>>(
{{1.0, 2.0, 3.0}, {0.0, 4.0, 0.0}}, exec)),
mtx2(gko::initialize<gko::matrix::Dense<>>(
{{1.0, 2.0, 3.0}, {4.0, 0.0, 4.0}}, exec)),
mtx3(gko::initialize<gko::matrix::Dense<>>(
{{1.0, 2.0, 3.0}, {0.0, 4.1, 0.0}}, exec))
{}
mtx1(gko::initialize<Mtx>({{1.0, 2.0, 3.0}, {0.0, 4.0, 0.0}}, exec)),
mtx2(gko::initialize<Mtx>({{1.0, 2.0, 3.0}, {4.0, 0.0, 4.0}}, exec)),
mtx3(gko::initialize<Mtx>({{1.0, 2.0, 3.0}, {0.0, 4.1, 0.0}}, exec)),
mtx13_row_ptrs{0, 3, 4},
mtx2_row_ptrs{0, 3, 5},
mtx13_col_idxs{0, 1, 2, 1},
mtx2_col_idxs{0, 1, 2, 0, 2},
mtx1_vals{1.0, 2.0, 3.0, 4.0},
mtx2_vals{1.0, 2.0, 3.0, 4.0, 4.0},
mtx3_vals{1.0, 2.0, 3.0, 4.1}
{
mtx1_sp = Sparse::create(exec, mtx1->get_size(), make_view(mtx1_vals),
make_view(mtx13_col_idxs),
make_view(mtx13_row_ptrs));
mtx2_sp =
Sparse::create(exec, mtx2->get_size(), make_view(mtx2_vals),
make_view(mtx2_col_idxs), make_view(mtx2_row_ptrs));
mtx3_sp = Sparse::create(exec, mtx3->get_size(), make_view(mtx3_vals),
make_view(mtx13_col_idxs),
make_view(mtx13_row_ptrs));
}

std::shared_ptr<const gko::Executor> exec;
std::unique_ptr<Mtx> mtx1;
std::unique_ptr<Mtx> mtx2;
std::unique_ptr<Mtx> mtx3;
std::array<Sparse::index_type, 3> mtx13_row_ptrs;
std::array<Sparse::index_type, 3> mtx2_row_ptrs;
std::array<Sparse::index_type, 4> mtx13_col_idxs;
std::array<Sparse::index_type, 5> mtx2_col_idxs;
std::array<Sparse::value_type, 4> mtx1_vals;
std::array<Sparse::value_type, 5> mtx2_vals;
std::array<Sparse::value_type, 4> mtx3_vals;
std::unique_ptr<Sparse> mtx1_sp;
std::unique_ptr<Sparse> mtx2_sp;
std::unique_ptr<Sparse> mtx3_sp;
};


TEST_F(MatricesNear, SuceedsIfSame)
{
ASSERT_PRED_FORMAT3(gko::test::assertions::matrices_near, mtx1.get(),
mtx1.get(), 0.0);
ASSERT_PRED_FORMAT2(gko::test::assertions::matrices_equal_sparsity,
mtx1_sp.get(), mtx1_sp.get());
}


TEST_F(MatricesNear, FailsIfDifferent)
{
ASSERT_PRED_FORMAT3(!gko::test::assertions::matrices_near, mtx1.get(),
mtx2.get(), 0.0);
ASSERT_PRED_FORMAT2(!gko::test::assertions::matrices_equal_sparsity,
mtx1_sp.get(), mtx2_sp.get());
}


Expand All @@ -82,13 +119,17 @@ TEST_F(MatricesNear, SucceedsIfClose)
mtx3.get(), 0.0);
ASSERT_PRED_FORMAT3(gko::test::assertions::matrices_near, mtx1.get(),
mtx3.get(), 0.1);
ASSERT_PRED_FORMAT2(gko::test::assertions::matrices_equal_sparsity,
mtx1_sp.get(), mtx3_sp.get());
}


TEST_F(MatricesNear, CanUseShortNotation)
{
GKO_EXPECT_MTX_NEAR(mtx1, mtx1, 0.0);
GKO_ASSERT_MTX_NEAR(mtx1, mtx3, 0.1);
GKO_EXPECT_MTX_EQ_SPARSITY(mtx1_sp, mtx3_sp);
GKO_ASSERT_MTX_EQ_SPARSITY(mtx1_sp, mtx3_sp);
}


Expand Down
6 changes: 6 additions & 0 deletions cuda/test/factorization/par_ilu_kernels.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,8 @@ TEST_F(ParIlu, KernelInitializeParILUIsEquivalentToRef)

GKO_ASSERT_MTX_NEAR(l_ref, l_cuda, 1e-14);
GKO_ASSERT_MTX_NEAR(u_ref, u_cuda, 1e-14);
GKO_ASSERT_MTX_EQ_SPARSITY(l_ref, l_cuda);
GKO_ASSERT_MTX_EQ_SPARSITY(u_ref, u_cuda);
}


Expand All @@ -218,6 +220,8 @@ TEST_F(ParIlu, KernelComputeParILUIsEquivalentToRef)

GKO_ASSERT_MTX_NEAR(l_ref, l_cuda, 5e-2);
GKO_ASSERT_MTX_NEAR(u_ref, u_cuda, 5e-2);
GKO_ASSERT_MTX_EQ_SPARSITY(l_ref, l_cuda);
GKO_ASSERT_MTX_EQ_SPARSITY(u_ref, u_cuda);
}


Expand All @@ -233,6 +237,8 @@ TEST_F(ParIlu, KernelComputeParILUWithMoreIterationsIsEquivalentToRef)

GKO_ASSERT_MTX_NEAR(l_ref, l_cuda, 1e-14);
GKO_ASSERT_MTX_NEAR(u_ref, u_cuda, 1e-14);
GKO_ASSERT_MTX_EQ_SPARSITY(l_ref, l_cuda);
GKO_ASSERT_MTX_EQ_SPARSITY(u_ref, u_cuda);
}


Expand Down
2 changes: 2 additions & 0 deletions cuda/test/matrix/csr_kernels.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,7 @@ TEST_F(Csr, AdvancedApplyToCsrMatrixIsEquivalentToRef)
dmtx->apply(dalpha.get(), d_trans.get(), dbeta.get(), square_dmtx.get());

GKO_ASSERT_MTX_NEAR(square_dmtx, square_mtx, 1e-14);
GKO_ASSERT_MTX_EQ_SPARSITY(square_dmtx, square_mtx);
}


Expand All @@ -377,6 +378,7 @@ TEST_F(Csr, SimpleApplyToCsrMatrixIsEquivalentToRef)
dmtx->apply(d_trans.get(), square_dmtx.get());

GKO_ASSERT_MTX_NEAR(square_dmtx, square_mtx, 1e-14);
GKO_ASSERT_MTX_EQ_SPARSITY(square_dmtx, square_mtx);
}


Expand Down
6 changes: 6 additions & 0 deletions hip/test/factorization/par_ilu_kernels.hip.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,8 @@ TEST_F(ParIlu, KernelInitializeParILUIsEquivalentToRef)

GKO_ASSERT_MTX_NEAR(l_ref, l_hip, 1e-14);
GKO_ASSERT_MTX_NEAR(u_ref, u_hip, 1e-14);
GKO_ASSERT_MTX_EQ_SPARSITY(l_ref, l_hip);
GKO_ASSERT_MTX_EQ_SPARSITY(u_ref, u_hip);
}


Expand All @@ -217,6 +219,8 @@ TEST_F(ParIlu, KernelComputeParILUIsEquivalentToRef)

GKO_ASSERT_MTX_NEAR(l_ref, l_hip, 5e-2);
GKO_ASSERT_MTX_NEAR(u_ref, u_hip, 5e-2);
GKO_ASSERT_MTX_EQ_SPARSITY(l_ref, l_hip);
GKO_ASSERT_MTX_EQ_SPARSITY(u_ref, u_hip);
}


Expand All @@ -232,6 +236,8 @@ TEST_F(ParIlu, KernelComputeParILUWithMoreIterationsIsEquivalentToRef)

GKO_ASSERT_MTX_NEAR(l_ref, l_hip, 1e-14);
GKO_ASSERT_MTX_NEAR(u_ref, u_hip, 1e-14);
GKO_ASSERT_MTX_EQ_SPARSITY(l_ref, l_hip);
GKO_ASSERT_MTX_EQ_SPARSITY(u_ref, u_hip);
}


Expand Down
6 changes: 6 additions & 0 deletions omp/test/factorization/par_ilu_kernels.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,8 @@ TEST_F(ParIlu, KernelInitializeParILUIsEquivalentToRef)

GKO_ASSERT_MTX_NEAR(l_ref, l_omp, 1e-14);
GKO_ASSERT_MTX_NEAR(u_ref, u_omp, 1e-14);
GKO_ASSERT_MTX_EQ_SPARSITY(l_ref, l_omp);
GKO_ASSERT_MTX_EQ_SPARSITY(u_ref, u_omp);
}


Expand All @@ -222,6 +224,8 @@ TEST_F(ParIlu, KernelComputeParILUIsEquivalentToRef)

GKO_ASSERT_MTX_NEAR(l_ref, l_omp, 5e-2);
GKO_ASSERT_MTX_NEAR(u_ref, u_omp, 5e-2);
GKO_ASSERT_MTX_EQ_SPARSITY(l_ref, l_omp);
GKO_ASSERT_MTX_EQ_SPARSITY(u_ref, u_omp);
}


Expand All @@ -237,6 +241,8 @@ TEST_F(ParIlu, KernelComputeParILUWithMoreIterationsIsEquivalentToRef)

GKO_ASSERT_MTX_NEAR(l_ref, l_omp, 1e-14);
GKO_ASSERT_MTX_NEAR(u_ref, u_omp, 1e-14);
GKO_ASSERT_MTX_EQ_SPARSITY(l_ref, l_omp);
GKO_ASSERT_MTX_EQ_SPARSITY(u_ref, u_omp);
}


Expand Down
2 changes: 2 additions & 0 deletions omp/test/matrix/csr_kernels.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@ TEST_F(Csr, AdvancedApplyToCsrMatrixIsEquivalentToRef)
dmtx->apply(dalpha.get(), d_trans.get(), dbeta.get(), square_dmtx.get());

GKO_ASSERT_MTX_NEAR(square_dmtx, square_mtx, 1e-14);
GKO_ASSERT_MTX_EQ_SPARSITY(square_dmtx, square_mtx);
}


Expand All @@ -250,6 +251,7 @@ TEST_F(Csr, SimpleApplyToCsrMatrixIsEquivalentToRef)
dmtx->apply(d_trans.get(), square_dmtx.get());

GKO_ASSERT_MTX_NEAR(square_dmtx, square_mtx, 1e-14);
GKO_ASSERT_MTX_EQ_SPARSITY(square_dmtx, square_mtx);
}


Expand Down