From 8a4735efcbb894f869f6166c3be2b06c4fddffe1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A5rd=20Skaflestad?= Date: Wed, 4 Oct 2023 09:19:10 +0200 Subject: [PATCH] Make Self Connection Behaviour Configurable This commit adds a new class template argument, bool PermitSelfConnections with a default value of 'false', that enables client code to configure whether or not function addConnection(v1, v2) creates a connection from v1 to v2 if v1 == v2. The default state preserves the current behaviour which ignores such connections. Permitting self connections makes the class slightly more general to uses cases outside inter-region flow accumulation. --- .../utility/CSRGraphFromCoordinates.hpp | 12 +- .../utility/CSRGraphFromCoordinates_impl.hpp | 162 +- tests/test_CSRGraphFromCoordinates.cpp | 1563 ++++++++++++++++- 3 files changed, 1651 insertions(+), 86 deletions(-) diff --git a/opm/common/utility/CSRGraphFromCoordinates.hpp b/opm/common/utility/CSRGraphFromCoordinates.hpp index 9fe8d57a5..ff060b907 100644 --- a/opm/common/utility/CSRGraphFromCoordinates.hpp +++ b/opm/common/utility/CSRGraphFromCoordinates.hpp @@ -47,7 +47,11 @@ namespace Opm { namespace utility { /// \tparam TrackCompressedIdx Whether or not to form a mapping relation /// for vertex pairs to compressed indices. Default value, false, /// bypasses this mapping relation and conserves memory. - template + /// + /// \tparam PermitSelfConnections Whether or not to allow connections of + /// the form i->i--i.e., diagonal elements. Default value, \c false, + /// does not generate connections from a vertex to itself. + template class CSRGraphFromCoordinates { private: @@ -73,9 +77,11 @@ namespace Opm { namespace utility { /// /// \param[in] v1 First vertex in vertex pair. Used as row index. /// - /// \param[in] r2 Second vertex in vertex pair. Used as column index. + /// \param[in] v2 Second vertex in vertex pair. Used as column index. /// - /// If both vertex IDs are the same then this function does nothing. + /// If both vertex IDs are the same, and class template argument \c + /// PermitSelfConnections is in its default state of \c false, then + /// this function does nothing. void addConnection(VertexID v1, VertexID v2); /// Form CSR adjacency matrix representation of input graph from diff --git a/opm/common/utility/CSRGraphFromCoordinates_impl.hpp b/opm/common/utility/CSRGraphFromCoordinates_impl.hpp index 7899ed9af..c8bf8548e 100644 --- a/opm/common/utility/CSRGraphFromCoordinates_impl.hpp +++ b/opm/common/utility/CSRGraphFromCoordinates_impl.hpp @@ -33,9 +33,9 @@ // Class Opm::utility::CSRGraphFromCoordinates::Connections // --------------------------------------------------------------------- -template +template void -Opm::utility::CSRGraphFromCoordinates:: +Opm::utility::CSRGraphFromCoordinates:: Connections::add(const VertexID v1, const VertexID v2) { this->i_.push_back(v1); @@ -45,9 +45,9 @@ Connections::add(const VertexID v1, const VertexID v2) this->max_j_ = std::max(this->max_j_.value_or(BaseVertexID{}), this->j_.back()); } -template +template void -Opm::utility::CSRGraphFromCoordinates:: +Opm::utility::CSRGraphFromCoordinates:: Connections::add(VertexID maxRowIdx, VertexID maxColIdx, const Neighbours& rows, @@ -67,9 +67,9 @@ Connections::add(VertexID maxRowIdx, this->max_j_ = std::max(this->max_j_.value_or(BaseVertexID{}), maxColIdx); } -template +template void -Opm::utility::CSRGraphFromCoordinates:: +Opm::utility::CSRGraphFromCoordinates:: Connections::clear() { this->j_.clear(); @@ -79,57 +79,57 @@ Connections::clear() this->max_j_.reset(); } -template +template bool -Opm::utility::CSRGraphFromCoordinates:: +Opm::utility::CSRGraphFromCoordinates:: Connections::empty() const { return this->i_.empty(); } -template +template bool -Opm::utility::CSRGraphFromCoordinates:: +Opm::utility::CSRGraphFromCoordinates:: Connections::isValid() const { return this->i_.size() == this->j_.size(); } -template -std::optional::BaseVertexID> -Opm::utility::CSRGraphFromCoordinates:: +template +std::optional::BaseVertexID> +Opm::utility::CSRGraphFromCoordinates:: Connections::maxRow() const { return this->max_i_; } -template -std::optional::BaseVertexID> -Opm::utility::CSRGraphFromCoordinates:: +template +std::optional::BaseVertexID> +Opm::utility::CSRGraphFromCoordinates:: Connections::maxCol() const { return this->max_j_; } -template -typename Opm::utility::CSRGraphFromCoordinates::Neighbours::size_type -Opm::utility::CSRGraphFromCoordinates:: +template +typename Opm::utility::CSRGraphFromCoordinates::Neighbours::size_type +Opm::utility::CSRGraphFromCoordinates:: Connections::numContributions() const { return this->i_.size(); } -template -const typename Opm::utility::CSRGraphFromCoordinates::Neighbours& -Opm::utility::CSRGraphFromCoordinates:: +template +const typename Opm::utility::CSRGraphFromCoordinates::Neighbours& +Opm::utility::CSRGraphFromCoordinates:: Connections::rowIndices() const { return this->i_; } -template -const typename Opm::utility::CSRGraphFromCoordinates::Neighbours& -Opm::utility::CSRGraphFromCoordinates:: +template +const typename Opm::utility::CSRGraphFromCoordinates::Neighbours& +Opm::utility::CSRGraphFromCoordinates:: Connections::columnIndices() const { return this->j_; @@ -141,9 +141,9 @@ Connections::columnIndices() const // Class Opm::utility::CSRGraphFromCoordinates::CSR // --------------------------------------------------------------------- -template +template void -Opm::utility::CSRGraphFromCoordinates:: +Opm::utility::CSRGraphFromCoordinates:: CSR::merge(const Connections& conns, const Offset maxNumVertices, const bool expandExistingIdxMap) @@ -169,50 +169,50 @@ CSR::merge(const Connections& conns, this->compress(maxNumVertices); } -template -typename Opm::utility::CSRGraphFromCoordinates::Offset -Opm::utility::CSRGraphFromCoordinates:: +template +typename Opm::utility::CSRGraphFromCoordinates::Offset +Opm::utility::CSRGraphFromCoordinates:: CSR::numRows() const { return this->startPointers().empty() ? 0 : this->startPointers().size() - 1; } -template -typename Opm::utility::CSRGraphFromCoordinates::BaseVertexID -Opm::utility::CSRGraphFromCoordinates:: +template +typename Opm::utility::CSRGraphFromCoordinates::BaseVertexID +Opm::utility::CSRGraphFromCoordinates:: CSR::maxRowID() const { return this->numRows_ - 1; } -template -typename Opm::utility::CSRGraphFromCoordinates::BaseVertexID -Opm::utility::CSRGraphFromCoordinates:: +template +typename Opm::utility::CSRGraphFromCoordinates::BaseVertexID +Opm::utility::CSRGraphFromCoordinates:: CSR::maxColID() const { return this->numCols_ - 1; } -template -const typename Opm::utility::CSRGraphFromCoordinates::Start& -Opm::utility::CSRGraphFromCoordinates:: +template +const typename Opm::utility::CSRGraphFromCoordinates::Start& +Opm::utility::CSRGraphFromCoordinates:: CSR::startPointers() const { return this->ia_; } -template -const typename Opm::utility::CSRGraphFromCoordinates::Neighbours& -Opm::utility::CSRGraphFromCoordinates:: +template +const typename Opm::utility::CSRGraphFromCoordinates::Neighbours& +Opm::utility::CSRGraphFromCoordinates:: CSR::columnIndices() const { return this->ja_; } -template -typename Opm::utility::CSRGraphFromCoordinates::Neighbours -Opm::utility::CSRGraphFromCoordinates:: +template +typename Opm::utility::CSRGraphFromCoordinates::Neighbours +Opm::utility::CSRGraphFromCoordinates:: CSR::coordinateFormatRowIndices() const { auto rowIdx = Neighbours{}; @@ -235,9 +235,9 @@ CSR::coordinateFormatRowIndices() const return rowIdx; } -template +template void -Opm::utility::CSRGraphFromCoordinates:: +Opm::utility::CSRGraphFromCoordinates:: CSR::clear() { this->ia_.clear(); @@ -251,9 +251,9 @@ CSR::clear() this->numCols_ = 0; } -template +template void -Opm::utility::CSRGraphFromCoordinates:: +Opm::utility::CSRGraphFromCoordinates:: CSR::assemble(const Neighbours& rows, const Neighbours& cols, const BaseVertexID maxRowIdx, @@ -286,9 +286,9 @@ CSR::assemble(const Neighbours& rows, this->numCols_ = thisNumCols; } -template +template void -Opm::utility::CSRGraphFromCoordinates:: +Opm::utility::CSRGraphFromCoordinates:: CSR::compress(const Offset maxNumVertices) { if (this->numRows() > maxNumVertices) { @@ -313,9 +313,9 @@ CSR::compress(const Offset maxNumVertices) } } -template +template void -Opm::utility::CSRGraphFromCoordinates:: +Opm::utility::CSRGraphFromCoordinates:: CSR::sortColumnIndicesPerRow() { // Transposition is, in this context, effectively a linear time (O(nnz)) @@ -327,9 +327,9 @@ CSR::sortColumnIndicesPerRow() this->transpose(); } -template +template void -Opm::utility::CSRGraphFromCoordinates:: +Opm::utility::CSRGraphFromCoordinates:: CSR::condenseDuplicates() { // Note: Must be called *after* sortColumnIndicesPerRow(). @@ -365,9 +365,9 @@ CSR::condenseDuplicates() this->ia_.back() = this->ja_.size(); } -template +template void -Opm::utility::CSRGraphFromCoordinates:: +Opm::utility::CSRGraphFromCoordinates:: CSR::preparePushbackRowGrouping(const int numRows, const Neighbours& rowIdx) { @@ -400,9 +400,9 @@ CSR::preparePushbackRowGrouping(const int numRows, assert (this->ia_[0] == rowIdx.size()); } -template +template void -Opm::utility::CSRGraphFromCoordinates:: +Opm::utility::CSRGraphFromCoordinates:: CSR::groupAndTrackColumnIndicesByRow(const Neighbours& rowIdx, const Neighbours& colIdx) { @@ -447,9 +447,9 @@ CSR::groupAndTrackColumnIndicesByRow(const Neighbours& rowIdx, this->ia_[0] = 0; } -template +template void -Opm::utility::CSRGraphFromCoordinates:: +Opm::utility::CSRGraphFromCoordinates:: CSR::transpose() { [[maybe_unused]] auto compressedIdx = this->compressedIdx_; @@ -472,9 +472,9 @@ CSR::transpose() std::swap(this->numRows_, this->numCols_); } -template +template void -Opm::utility::CSRGraphFromCoordinates:: +Opm::utility::CSRGraphFromCoordinates:: CSR::condenseAndTrackUniqueColumnsForSingleRow(typename Neighbours::const_iterator begin, typename Neighbours::const_iterator end) { @@ -517,9 +517,9 @@ CSR::condenseAndTrackUniqueColumnsForSingleRow(typename Neighbours::const_iterat } } -template +template void -Opm::utility::CSRGraphFromCoordinates::CSR:: +Opm::utility::CSRGraphFromCoordinates::CSR:: remapCompressedIndex([[maybe_unused]] Start&& compressedIdx, [[maybe_unused]] std::optional numOrig) { @@ -547,16 +547,16 @@ remapCompressedIndex([[maybe_unused]] Start&& c // Class Opm::utility::CSRGraphFromCoordinates // --------------------------------------------------------------------- -template -void Opm::utility::CSRGraphFromCoordinates::clear() +template +void Opm::utility::CSRGraphFromCoordinates::clear() { this->uncompressed_.clear(); this->csr_.clear(); } -template +template void -Opm::utility::CSRGraphFromCoordinates:: +Opm::utility::CSRGraphFromCoordinates:: addConnection(const VertexID v1, const VertexID v2) { if ((v1 < 0) || (v2 < 0)) { @@ -567,17 +567,19 @@ addConnection(const VertexID v1, const VertexID v2) }; } - if (v1 == v2) { - // Ignore self connections. - return; + if constexpr (! PermitSelfConnections) { + if (v1 == v2) { + // Ignore self connections. + return; + } } this->uncompressed_.add(v1, v2); } -template +template void -Opm::utility::CSRGraphFromCoordinates:: +Opm::utility::CSRGraphFromCoordinates:: compress(const Offset maxNumVertices, const bool expandExistingIdxMap) { if (! this->uncompressed_.isValid()) { @@ -591,16 +593,16 @@ compress(const Offset maxNumVertices, const bool expandExistingIdxMap) this->uncompressed_.clear(); } -template -typename Opm::utility::CSRGraphFromCoordinates::Offset -Opm::utility::CSRGraphFromCoordinates::numVertices() const +template +typename Opm::utility::CSRGraphFromCoordinates::Offset +Opm::utility::CSRGraphFromCoordinates::numVertices() const { return this->csr_.numRows(); } -template -typename Opm::utility::CSRGraphFromCoordinates::Offset -Opm::utility::CSRGraphFromCoordinates::numEdges() const +template +typename Opm::utility::CSRGraphFromCoordinates::Offset +Opm::utility::CSRGraphFromCoordinates::numEdges() const { const auto& ia = this->startPointers(); diff --git a/tests/test_CSRGraphFromCoordinates.cpp b/tests/test_CSRGraphFromCoordinates.cpp index 8c6f08000..b27249c8f 100644 --- a/tests/test_CSRGraphFromCoordinates.cpp +++ b/tests/test_CSRGraphFromCoordinates.cpp @@ -26,10 +26,12 @@ #include #include +BOOST_AUTO_TEST_SUITE(No_Self_Connections) + BOOST_AUTO_TEST_SUITE(Untracked) namespace { - // Vertex = int, TrackCompressedIdx = false + // Vertex = int, TrackCompressedIdx = false, PermitSelfConnections = false using CSRGraph = Opm::utility::CSRGraphFromCoordinates<>; } @@ -502,11 +504,10 @@ BOOST_AUTO_TEST_SUITE_END() // Untracked // --------------------------------------------------------------------------- - BOOST_AUTO_TEST_SUITE(Tracked) namespace { - // Vertex = int, TrackCompressedIdx = true + // Vertex = int, TrackCompressedIdx = true, PermitSelfConnections = false using CSRGraph = Opm::utility::CSRGraphFromCoordinates; } @@ -1447,3 +1448,1559 @@ BOOST_AUTO_TEST_CASE(Linear_4x1x1_Symmetric_Multiple_Repeated_Add_3x1x1_Expand_N } BOOST_AUTO_TEST_SUITE_END() // Tracked + +BOOST_AUTO_TEST_SUITE_END() // No_Self_Connections + +// --------------------------------------------------------------------------- + +BOOST_AUTO_TEST_SUITE(Permit_Self_Connections) + +BOOST_AUTO_TEST_SUITE(Untracked) + +namespace { + // Vertex = int, TrackCompressedIdx = false, PermitSelfConnections = true + using CSRGraph = Opm::utility::CSRGraphFromCoordinates; +} + +BOOST_AUTO_TEST_CASE(Clear_Empty_is_Valid) +{ + auto graph = CSRGraph{}; + + BOOST_CHECK_EQUAL(graph.numVertices(), std::size_t{0}); + BOOST_CHECK_EQUAL(graph.numEdges(), std::size_t{0}); + + graph.clear(); + + BOOST_CHECK_EQUAL(graph.numVertices(), std::size_t{0}); + BOOST_CHECK_EQUAL(graph.numEdges(), std::size_t{0}); +} + +BOOST_AUTO_TEST_CASE(Negative_Vertex_ID) +{ + auto graph = CSRGraph{}; + + BOOST_CHECK_THROW(graph.addConnection( 0, - 1), std::invalid_argument); + BOOST_CHECK_THROW(graph.addConnection(-1, 10), std::invalid_argument); +} + +BOOST_AUTO_TEST_CASE(Linear_4x1x1_Symmetric) +{ + auto graph = CSRGraph{}; + + // +-----+-----+-----+-----+ + // | 0 | 1 | 2 | 3 | + // +-----+-----+-----+-----+ + // + // => Laplacian + // [ 1 1 0 0 ] + // [ 1 1 1 0 ] + // [ 0 1 1 1 ] + // [ 0 0 1 1 ] + // + // => CSR: IA = [ 0, 2, 5, 8, 10 ] + // JA = [ 0, 1 | 0, 1, 2 | 1, 2, 3 | 2, 3 ] + + for (auto i = 0; i < 4 - 1; ++i) { + graph.addConnection(i, i + 1); + graph.addConnection(i + 1, i); + } + + for (auto i = 0; i < 4; ++i) { + graph.addConnection(i, i); + } + + graph.compress(4); + + BOOST_CHECK_EQUAL(graph.numVertices(), std::size_t{4}); + BOOST_CHECK_EQUAL(graph.numEdges(), std::size_t{10}); + + { + const auto& ia = graph.startPointers(); + const auto expect = std::vector { 0, 2, 5, 8, 10 }; + BOOST_CHECK_EQUAL_COLLECTIONS(ia.begin(), ia.end(), expect.begin(), expect.end()); + } + + { + const auto& ja = graph.columnIndices(); + const auto expect = std::vector { 0, 1, 0, 1, 2, 1, 2, 3, 2, 3, }; + BOOST_CHECK_EQUAL_COLLECTIONS(ja.begin(), ja.end(), expect.begin(), expect.end()); + } +} + +BOOST_AUTO_TEST_CASE(Linear_4x1x1_Symmetric_Clear) +{ + auto graph = CSRGraph{}; + + // +-----+-----+-----+-----+ + // | 0 | 1 | 2 | 3 | + // +-----+-----+-----+-----+ + // + // => Laplacian + // [ 1 1 0 0 ] + // [ 1 1 1 0 ] + // [ 0 1 1 1 ] + // [ 0 0 1 1 ] + // + // => CSR: IA = [ 0, 2, 5, 8, 10 ] + // JA = [ 0, 1 | 0, 1, 2 | 1, 2, 3 | 2, 3 ] + + for (auto i = 0; i < 4 - 1; ++i) { + graph.addConnection(i, i + 1); + graph.addConnection(i + 1, i); + } + + for (auto i = 0; i < 4; ++i) { + graph.addConnection(i, i); + } + + graph.compress(4); + + BOOST_CHECK_EQUAL(graph.numVertices(), std::size_t{4}); + BOOST_CHECK_EQUAL(graph.numEdges(), std::size_t{10}); + + graph.clear(); + + BOOST_CHECK_EQUAL(graph.numVertices(), std::size_t{0}); + BOOST_CHECK_EQUAL(graph.numEdges(), std::size_t{0}); + + BOOST_CHECK_MESSAGE(graph.startPointers().empty(), + "Start pointer array must be empty in cleared graph"); + + BOOST_CHECK_MESSAGE(graph.columnIndices().empty(), + "Column index array must be empty in cleared graph"); +} + +BOOST_AUTO_TEST_CASE(Linear_4x1x1_Symmetric_Compress_Small) +{ + auto graph = CSRGraph{}; + + // +-----+-----+-----+-----+ + // | 0 | 1 | 2 | 3 | + // +-----+-----+-----+-----+ + // + // => Laplacian + // [ 1 1 0 0 ] + // [ 1 1 1 0 ] + // [ 0 1 1 1 ] + // [ 0 0 1 1 ] + // + // => CSR: IA = [ 0, 2, 5, 8, 10 ] + // JA = [ 0, 1 | 0, 1, 2 | 1, 2, 3 | 2, 3 ] + + for (auto i = 0; i < 4 - 1; ++i) { + graph.addConnection(i, i + 1); + graph.addConnection(i + 1, i); + } + + for (auto i = 0; i < 4; ++i) { + graph.addConnection(i, i); + } + + // Argument 3 is too small. There are 4 vertices in the graph. + BOOST_CHECK_THROW(graph.compress(3), std::invalid_argument); +} + +BOOST_AUTO_TEST_CASE(Linear_4x1x1_Symmetric_Multiple) +{ + auto graph = CSRGraph{}; + + // +-----+-----+-----+-----+ + // | 0 | 1 | 2 | 3 | + // +-----+-----+-----+-----+ + // + // => Laplacian + // [ 1 1 0 0 ] + // [ 1 1 1 0 ] + // [ 0 1 1 1 ] + // [ 0 0 1 1 ] + // + // => CSR: IA = [ 0, 2, 5, 8, 10 ] + // JA = [ 0, 1 | 0, 1, 2 | 1, 2, 3 | 2, 3 ] + + for (auto i = 0; i < 4 - 1; ++i) { + graph.addConnection(i, i + 1); + graph.addConnection(i + 1, i); + graph.addConnection(i + 1, i); + graph.addConnection(i, i + 1); + graph.addConnection(i, i + 1); + } + + for (auto i = 0; i < 4; ++i) { + graph.addConnection(i, i); + } + + graph.compress(4); + + BOOST_CHECK_EQUAL(graph.numVertices(), std::size_t{4}); + BOOST_CHECK_EQUAL(graph.numEdges(), std::size_t{10}); + + { + const auto& ia = graph.startPointers(); + const auto expect = std::vector { 0, 2, 5, 8, 10 }; + BOOST_CHECK_EQUAL_COLLECTIONS(ia.begin(), ia.end(), expect.begin(), expect.end()); + } + + { + const auto& ja = graph.columnIndices(); + const auto expect = std::vector { 0, 1, 0, 1, 2, 1, 2, 3, 2, 3, }; + BOOST_CHECK_EQUAL_COLLECTIONS(ja.begin(), ja.end(), expect.begin(), expect.end()); + } +} + +BOOST_AUTO_TEST_CASE(Linear_4x1x1_Symmetric_Multiple_Repeated) +{ + auto graph = CSRGraph{}; + + // +-----+-----+-----+-----+ + // | 0 | 1 | 2 | 3 | + // +-----+-----+-----+-----+ + // + // => Laplacian + // [ 1 1 0 0 ] + // [ 1 1 1 0 ] + // [ 0 1 1 1 ] + // [ 0 0 1 1 ] + // + // => CSR: IA = [ 0, 2, 5, 8, 10 ] + // JA = [ 0, 1 | 0, 1, 2 | 1, 2, 3 | 2, 3 ] + + for (auto n = 0; n < 20; ++n) { + for (auto i = 0; i < 4 - 1; ++i) { + graph.addConnection(i, i + 1); + graph.addConnection(i + 1, i); + graph.addConnection(i + 1, i); + graph.addConnection(i, i + 1); + graph.addConnection(i, i + 1); + } + + for (auto i = 0; i < 4; ++i) { + graph.addConnection(i, i); + } + } + + graph.compress(4); + + BOOST_CHECK_EQUAL(graph.numVertices(), std::size_t{4}); + BOOST_CHECK_EQUAL(graph.numEdges(), std::size_t{10}); + + { + const auto& ia = graph.startPointers(); + const auto expect = std::vector { 0, 2, 5, 8, 10 }; + BOOST_CHECK_EQUAL_COLLECTIONS(ia.begin(), ia.end(), expect.begin(), expect.end()); + } + + { + const auto& ja = graph.columnIndices(); + const auto expect = std::vector { 0, 1, 0, 1, 2, 1, 2, 3, 2, 3, }; + BOOST_CHECK_EQUAL_COLLECTIONS(ja.begin(), ja.end(), expect.begin(), expect.end()); + } +} + +BOOST_AUTO_TEST_CASE(Linear_4x1x1_Symmetric_Add_3x1x1) +{ + auto graph = CSRGraph{}; + + // +-----+-----+-----+-----+-----+-----+-----+ + // | 0 | 1 | 2 | 3 | 4 | 5 | 6 | + // +-----+-----+-----+-----+-----+-----+-----+ + // + // => Laplacian + // [ 1 1 0 0 0 0 0 ] + // [ 1 1 1 0 0 0 0 ] + // [ 0 1 1 1 0 0 0 ] + // [ 0 0 1 1 0 0 0 ] + // [ 0 0 0 1 1 1 0 ] + // [ 0 0 0 0 1 1 1 ] + // [ 0 0 0 0 0 1 1 ] + // + // => CSR: IA = [ 0, 2, 5, 8, 11, 14, 17, 19 ] + // JA = [ 0, 1 | 0, 1, 2 | 1, 2, 3 | 2, 3, 4 | 3 , 4, 5 | 4 , 5, 6 | 5, 6 ] + + for (auto i = 0; i < 4 - 1; ++i) { + graph.addConnection(i, i + 1); + graph.addConnection(i + 1, i); + } + + for (auto i = 0; i < 4; ++i) { + graph.addConnection(i, i); + } + + graph.compress(4); + + for (auto i = 4 - 1; i < 7 - 1; ++i) { + graph.addConnection(i, i + 1); + graph.addConnection(i + 1, i); + } + + for (auto i = 4; i < 7; ++i) { + graph.addConnection(i, i); + } + + graph.compress(7); + + BOOST_CHECK_EQUAL(graph.numVertices(), std::size_t{7}); + BOOST_CHECK_EQUAL(graph.numEdges(), std::size_t{19}); + + { + const auto& ia = graph.startPointers(); + const auto expect = std::vector { 0, 2, 5, 8, 11, 14, 17, 19, }; + BOOST_CHECK_EQUAL_COLLECTIONS(ia.begin(), ia.end(), expect.begin(), expect.end()); + } + + { + const auto& ja = graph.columnIndices(); + const auto expect = std::vector { 0, 1, 0, 1, 2, 1, 2, 3, 2, 3, 4, 3, 4, 5, 4, 5, 6, 5, 6, }; + BOOST_CHECK_EQUAL_COLLECTIONS(ja.begin(), ja.end(), expect.begin(), expect.end()); + } +} + +BOOST_AUTO_TEST_CASE(Linear_4x1x1_Symmetric_Add_3x1x1_Expand) +{ + auto graph = CSRGraph{}; + + // +-----+-----+-----+-----+-----+-----+-----+ + // | 0 | 1 | 2 | 3 | 4 | 5 | 6 | + // +-----+-----+-----+-----+-----+-----+-----+ + // + // => Laplacian + // [ 1 1 0 0 0 0 0 ] + // [ 1 1 1 0 0 0 0 ] + // [ 0 1 1 1 0 0 0 ] + // [ 0 0 1 1 0 0 0 ] + // [ 0 0 0 1 1 1 0 ] + // [ 0 0 0 0 1 1 1 ] + // [ 0 0 0 0 0 1 1 ] + // + // => CSR: IA = [ 0, 2, 5, 8, 11, 14, 17, 19 ] + // JA = [ 0, 1 | 0, 1, 2 | 1, 2, 3 | 2, 3, 4 | 3 , 4, 5 | 4 , 5, 6 | 5, 6 ] + + for (auto i = 0; i < 4 - 1; ++i) { + graph.addConnection(i, i + 1); + graph.addConnection(i + 1, i); + } + + for (auto i = 0; i < 4; ++i) { + graph.addConnection(i, i); + } + + graph.compress(4, true); + + for (auto i = 4 - 1; i < 7 - 1; ++i) { + graph.addConnection(i, i + 1); + graph.addConnection(i + 1, i); + } + + for (auto i = 4; i < 7; ++i) { + graph.addConnection(i, i); + } + + graph.compress(7, true); + + BOOST_CHECK_EQUAL(graph.numVertices(), std::size_t{7}); + BOOST_CHECK_EQUAL(graph.numEdges(), std::size_t{19}); + + { + const auto& ia = graph.startPointers(); + const auto expect = std::vector { 0, 2, 5, 8, 11, 14, 17, 19, }; + BOOST_CHECK_EQUAL_COLLECTIONS(ia.begin(), ia.end(), expect.begin(), expect.end()); + } + + { + const auto& ja = graph.columnIndices(); + const auto expect = std::vector { 0, 1, 0, 1, 2, 1, 2, 3, 2, 3, 4, 3, 4, 5, 4, 5, 6, 5, 6, }; + BOOST_CHECK_EQUAL_COLLECTIONS(ja.begin(), ja.end(), expect.begin(), expect.end()); + } +} + +BOOST_AUTO_TEST_CASE(Linear_4x1x1_Symmetric_Add_3x1x1_And_Existing) +{ + auto graph = CSRGraph{}; + + // +-----+-----+-----+-----+-----+-----+-----+ + // | 0 | 1 | 2 | 3 | 4 | 5 | 6 | + // +-----+-----+-----+-----+-----+-----+-----+ + // + // => Laplacian + // [ 1 1 0 0 0 0 0 ] + // [ 1 1 1 0 0 0 0 ] + // [ 0 1 1 1 0 0 0 ] + // [ 0 0 1 1 0 0 0 ] + // [ 0 0 0 1 1 1 0 ] + // [ 0 0 0 0 1 1 1 ] + // [ 0 0 0 0 0 1 1 ] + // + // => CSR: IA = [ 0, 2, 5, 8, 11, 14, 17, 19 ] + // JA = [ 0, 1 | 0, 1, 2 | 1, 2, 3 | 2, 3, 4 | 3 , 4, 5 | 4 , 5, 6 | 5, 6 ] + + for (auto i = 0; i < 4 - 1; ++i) { + graph.addConnection(i, i + 1); + graph.addConnection(i + 1, i); + } + + for (auto i = 0; i < 4; ++i) { + graph.addConnection(i, i); + } + + graph.compress(4); + + for (auto i = 4 - 1; i < 7 - 1; ++i) { + graph.addConnection(i, i + 1); + graph.addConnection(i + 1, i); + } + + for (auto i = 0; i < 4; ++i) { + graph.addConnection(1, 2); + } + + for (auto i = 4; i < 7; ++i) { + graph.addConnection(i, i); + } + + graph.compress(7); + + BOOST_CHECK_EQUAL(graph.numVertices(), std::size_t{7}); + BOOST_CHECK_EQUAL(graph.numEdges(), std::size_t{19}); + + { + const auto& ia = graph.startPointers(); + const auto expect = std::vector { 0, 2, 5, 8, 11, 14, 17, 19, }; + BOOST_CHECK_EQUAL_COLLECTIONS(ia.begin(), ia.end(), expect.begin(), expect.end()); + } + + { + const auto& ja = graph.columnIndices(); + const auto expect = std::vector { 0, 1, 0, 1, 2, 1, 2, 3, 2, 3, 4, 3, 4, 5, 4, 5, 6, 5, 6, }; + BOOST_CHECK_EQUAL_COLLECTIONS(ja.begin(), ja.end(), expect.begin(), expect.end()); + } +} + +BOOST_AUTO_TEST_CASE(Linear_4x1x1_Symmetric_Add_3x1x1_And_Existing_Expand) +{ + auto graph = CSRGraph{}; + + // +-----+-----+-----+-----+-----+-----+-----+ + // | 0 | 1 | 2 | 3 | 4 | 5 | 6 | + // +-----+-----+-----+-----+-----+-----+-----+ + // + // => Laplacian + // [ 1 1 0 0 0 0 0 ] + // [ 1 1 1 0 0 0 0 ] + // [ 0 1 1 1 0 0 0 ] + // [ 0 0 1 1 0 0 0 ] + // [ 0 0 0 1 1 1 0 ] + // [ 0 0 0 0 1 1 1 ] + // [ 0 0 0 0 0 1 1 ] + // + // => CSR: IA = [ 0, 2, 5, 8, 11, 14, 17, 19 ] + // JA = [ 0, 1 | 0, 1, 2 | 1, 2, 3 | 2, 3, 4 | 3 , 4, 5 | 4 , 5, 6 | 5, 6 ] + + for (auto i = 0; i < 4 - 1; ++i) { + graph.addConnection(i, i + 1); + graph.addConnection(i + 1, i); + } + + for (auto i = 0; i < 4; ++i) { + graph.addConnection(i, i); + } + + graph.compress(4, true); + + for (auto i = 4 - 1; i < 7 - 1; ++i) { + graph.addConnection(i, i + 1); + graph.addConnection(i + 1, i); + } + + for (auto i = 0; i < 17; ++i) { + graph.addConnection(1, 2); + graph.addConnection(3, 2); + + for (auto k = 4; k < 7; ++k) { + graph.addConnection(k, k); + } + } + + graph.compress(7, true); + + BOOST_CHECK_EQUAL(graph.numVertices(), std::size_t{7}); + BOOST_CHECK_EQUAL(graph.numEdges(), std::size_t{19}); + + { + const auto& ia = graph.startPointers(); + const auto expect = std::vector { 0, 2, 5, 8, 11, 14, 17, 19, }; + BOOST_CHECK_EQUAL_COLLECTIONS(ia.begin(), ia.end(), expect.begin(), expect.end()); + } + + { + const auto& ja = graph.columnIndices(); + const auto expect = std::vector { 0, 1, 0, 1, 2, 1, 2, 3, 2, 3, 4, 3, 4, 5, 4, 5, 6, 5, 6, }; + BOOST_CHECK_EQUAL_COLLECTIONS(ja.begin(), ja.end(), expect.begin(), expect.end()); + } +} + +BOOST_AUTO_TEST_SUITE_END() // Untracked + +// --------------------------------------------------------------------------- + +BOOST_AUTO_TEST_SUITE(Tracked) + +namespace { + // Vertex = int, TrackCompressedIdx = true, PermitSelfConnections = true + using CSRGraph = Opm::utility::CSRGraphFromCoordinates; +} + +BOOST_AUTO_TEST_CASE(Clear_Empty_is_Valid) +{ + auto graph = CSRGraph{}; + + BOOST_CHECK_EQUAL(graph.numVertices(), std::size_t{0}); + BOOST_CHECK_EQUAL(graph.numEdges(), std::size_t{0}); + + graph.clear(); + + BOOST_CHECK_EQUAL(graph.numVertices(), std::size_t{0}); + BOOST_CHECK_EQUAL(graph.numEdges(), std::size_t{0}); +} + +BOOST_AUTO_TEST_CASE(Linear_4x1x1_Symmetric) +{ + auto graph = CSRGraph{}; + + for (auto i = 0; i < 4 - 1; ++i) { + graph.addConnection(i, i + 1); + graph.addConnection(i + 1, i); + } + + for (auto i = 0; i < 4; ++i) { + graph.addConnection(i, i); + } + + graph.compress(4); + + BOOST_CHECK_EQUAL(graph.numVertices(), std::size_t{4}); + BOOST_CHECK_EQUAL(graph.numEdges(), std::size_t{10}); + + { + const auto& nzMap = graph.compressedIndexMap(); + const auto expect = std::vector { 1, 2, 4, 5, 7, 8, 0, 3, 6, 9, }; + BOOST_CHECK_EQUAL_COLLECTIONS(nzMap .begin(), nzMap .end(), + expect.begin(), expect.end()); + } +} + +BOOST_AUTO_TEST_CASE(Linear_4x1x1_Symmetric_Clear) +{ + auto graph = CSRGraph{}; + + // Regular one-pass construction gives + // MAP = [ 0, 1, 2, 3, 4, 5 ] + + for (auto i = 0; i < 4 - 1; ++i) { + graph.addConnection(i, i + 1); + graph.addConnection(i + 1, i); + } + + for (auto i = 0; i < 4; ++i) { + graph.addConnection(i, i); + } + + graph.compress(4); + + graph.clear(); + + BOOST_CHECK_MESSAGE(graph.compressedIndexMap().empty(), + "Compressed index map must be empty in cleared graph"); +} + +BOOST_AUTO_TEST_CASE(Linear_4x1x1_Symmetric_Add_3x1x1) +{ + auto graph = CSRGraph{}; + + for (auto i = 0; i < 4 - 1; ++i) { + graph.addConnection(i, i + 1); + graph.addConnection(i + 1, i); + } + + for (auto i = 0; i < 4; ++i) { + graph.addConnection(i, i); + } + + graph.compress(4); + + for (auto i = 4 - 1; i < 7 - 1; ++i) { + graph.addConnection(i, i + 1); + graph.addConnection(i + 1, i); + } + + for (auto i = 4; i < 7; ++i) { + graph.addConnection(i, i); + } + + graph.compress(7); + + BOOST_CHECK_EQUAL(graph.numVertices(), std::size_t{7}); + BOOST_CHECK_EQUAL(graph.numEdges(), std::size_t{19}); + + { + const auto& nzMap = graph.compressedIndexMap(); + const auto expect = std::vector { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, // Original + 10, 11, 13, 14, 16, 17, 12, 15, 18, // Expanded/additional + }; + + BOOST_CHECK_EQUAL_COLLECTIONS(nzMap .begin(), nzMap .end(), + expect.begin(), expect.end()); + } +} + +BOOST_AUTO_TEST_CASE(Linear_4x1x1_Symmetric_Add_3x1x1_Expand) +{ + auto graph = CSRGraph{}; + + for (auto i = 0; i < 4 - 1; ++i) { + graph.addConnection(i, i + 1); + graph.addConnection(i + 1, i); + } + + for (auto i = 0; i < 4; ++i) { + graph.addConnection(i, i); + } + + graph.compress(4, true); + + for (auto i = 4 - 1; i < 7 - 1; ++i) { + graph.addConnection(i, i + 1); + graph.addConnection(i + 1, i); + } + + for (auto i = 4; i < 7; ++i) { + graph.addConnection(i, i); + } + + graph.compress(7, true); + + BOOST_CHECK_EQUAL(graph.numVertices(), std::size_t{7}); + BOOST_CHECK_EQUAL(graph.numEdges(), std::size_t{19}); + + { + const auto& nzMap = graph.compressedIndexMap(); + const auto expect = std::vector { + 1, 2, 4, 5, 7, 8, 0, 3, 6, 9, // Original + 10, 11, 13, 14, 16, 17, 12, 15, 18, // Expanded + }; + + BOOST_CHECK_EQUAL_COLLECTIONS(nzMap .begin(), nzMap .end(), + expect.begin(), expect.end()); + } +} + +BOOST_AUTO_TEST_CASE(Linear_4x1x1_Symmetric_Add_3x1x1_And_Existing) +{ + auto graph = CSRGraph{}; + + for (auto i = 0; i < 4 - 1; ++i) { + graph.addConnection(i, i + 1); + graph.addConnection(i + 1, i); + } + + for (auto i = 0; i < 4; ++i) { + graph.addConnection(i, i); + } + + graph.compress(4); + + for (auto i = 4 - 1; i < 7 - 1; ++i) { + graph.addConnection(i, i + 1); + graph.addConnection(i + 1, i); + } + + for (auto i = 4; i < 7; ++i) { + graph.addConnection(i, i); + } + + for (auto i = 0; i < 5; ++i) { + graph.addConnection(1, 2); + } + + for (auto i = 0; i < 3; ++i) { + graph.addConnection(3, 2); + } + + graph.compress(7); + + BOOST_CHECK_EQUAL(graph.numVertices(), std::size_t{7}); + BOOST_CHECK_EQUAL(graph.numEdges(), std::size_t{19}); + + { + const auto& nzMap = graph.compressedIndexMap(); + const auto expect = std::vector { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 13, 14, 16, 17, 12, 15, 18, + 4, 4, 4, 4, 4, // for i=0..4, 1 -> 2 + 8, 8, 8, // for i=0..2, 3 -> 2 + }; + + BOOST_CHECK_EQUAL_COLLECTIONS(nzMap .begin(), nzMap .end(), + expect.begin(), expect.end()); + } +} + +BOOST_AUTO_TEST_CASE(Linear_4x1x1_Symmetric_Add_3x1x1_And_Existing_Expand) +{ + auto graph = CSRGraph{}; + + // Recompress with preservation/expansion gives + // MAP = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 2, 2, 2, 2, 2 ] + + for (auto i = 0; i < 4 - 1; ++i) { + graph.addConnection(i, i + 1); + graph.addConnection(i + 1, i); + } + + graph.compress(4, true); + + for (auto i = 4 - 1; i < 7 - 1; ++i) { + graph.addConnection(i, i + 1); + graph.addConnection(i + 1, i); + } + + for (auto i = 0; i < 5; ++i) { + graph.addConnection(1, 2); + } + + graph.compress(7, true); + + BOOST_CHECK_EQUAL(graph.numVertices(), std::size_t{7}); + BOOST_CHECK_EQUAL(graph.numEdges(), std::size_t{12}); + + { + const auto& nzMap = graph.compressedIndexMap(); + const auto expect = std::vector { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, + 2, 2, 2, 2, 2, + }; + + BOOST_CHECK_EQUAL_COLLECTIONS(nzMap .begin(), nzMap .end(), + expect.begin(), expect.end()); + } +} + +BOOST_AUTO_TEST_CASE(Linear_4x1x1_Symmetric_Reverse) +{ + auto graph = CSRGraph{}; + + for (auto i = 2; i >= 0; --i) { + graph.addConnection(i, i + 1); + graph.addConnection(i + 1, i); + } + + for (auto i = 3; i >= 0; --i) { + graph.addConnection(i, i); + } + + graph.compress(4); + + BOOST_CHECK_EQUAL(graph.numVertices(), std::size_t{4}); + BOOST_CHECK_EQUAL(graph.numEdges(), std::size_t{10}); + + { + const auto& nzMap = graph.compressedIndexMap(); + const auto expect = std::vector { + 7, 8, 4, 5, 1, 2, 9, 6, 3, 0, + }; + + BOOST_CHECK_EQUAL_COLLECTIONS(nzMap .begin(), nzMap .end(), + expect.begin(), expect.end()); + } +} + +BOOST_AUTO_TEST_CASE(Linear_4x1x1_Symmetric_Reverse_Add_3x1x1) +{ + auto graph = CSRGraph{}; + + for (auto i = 2; i >= 0; --i) { + graph.addConnection(i, i + 1); + graph.addConnection(i + 1, i); + } + + for (auto i = 3; i >= 0; --i) { + graph.addConnection(i, i); + } + + graph.compress(4); + + for (auto i = 5; i >= 3; --i) { + graph.addConnection(i + 1, i); + graph.addConnection(i, i + 1); + } + + for (auto i = 6; i >= 4; --i) { + graph.addConnection(i, i); + } + + graph.compress(7); + + BOOST_CHECK_EQUAL(graph.numVertices(), std::size_t{7}); + BOOST_CHECK_EQUAL(graph.numEdges(), std::size_t{19}); + + { + const auto& nzMap = graph.compressedIndexMap(); + const auto expect = std::vector { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 17, 16, 14, 13, 11, 10, 18, 15, 12, + }; + + BOOST_CHECK_EQUAL_COLLECTIONS(nzMap .begin(), nzMap .end(), + expect.begin(), expect.end()); + } +} + +BOOST_AUTO_TEST_CASE(Linear_4x1x1_Symmetric_Reverse_Add_3x1x1_Expand) +{ + auto graph = CSRGraph{}; + + for (auto i = 2; i >= 0; --i) { + graph.addConnection(i, i + 1); + graph.addConnection(i + 1, i); + } + + for (auto i = 3; i >= 0; --i) { + graph.addConnection(i, i); + } + + graph.compress(4); + + for (auto i = 5; i >= 3; --i) { + graph.addConnection(i + 1, i); + graph.addConnection(i, i + 1); + } + + for (auto i = 6; i >= 4; --i) { + graph.addConnection(i, i); + } + + graph.compress(7, true); + + BOOST_CHECK_EQUAL(graph.numVertices(), std::size_t{7}); + BOOST_CHECK_EQUAL(graph.numEdges(), std::size_t{19}); + + { + const auto& nzMap = graph.compressedIndexMap(); + const auto expect = std::vector { + 7, 8, 4, 5, 1, 2, 9, 6, 3, 0, + 17, 16, 14, 13, 11, 10, 18, 15, 12, + }; + + BOOST_CHECK_EQUAL_COLLECTIONS(nzMap .begin(), nzMap .end(), + expect.begin(), expect.end()); + } +} + +BOOST_AUTO_TEST_CASE(Linear_4x1x1_Symmetric_Reverse_Add_3x1x1_And_Existing) +{ + auto graph = CSRGraph{}; + + for (auto i = 2; i >= 0; --i) { + graph.addConnection(i, i + 1); + graph.addConnection(i + 1, i); + } + + for (auto i = 3; i >= 0; --i) { + graph.addConnection(i, i); + } + + graph.compress(4); + + for (auto i = 5; i >= 3; --i) { + graph.addConnection(i + 1, i); + graph.addConnection(i, i + 1); + } + + for (auto i = 6; i >= 4; --i) { + graph.addConnection(i, i); + } + + for (auto i = 0; i < 5; ++i) { + graph.addConnection(5, 4); + } + + graph.compress(7); + + BOOST_CHECK_EQUAL(graph.numVertices(), std::size_t{7}); + BOOST_CHECK_EQUAL(graph.numEdges(), std::size_t{19}); + + { + const auto& nzMap = graph.compressedIndexMap(); + const auto expect = std::vector { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 17, 16, 14, 13, 11, 10, 18, 15, 12, + 14, 14, 14, 14, 14, + }; + + BOOST_CHECK_EQUAL_COLLECTIONS(nzMap .begin(), nzMap .end(), + expect.begin(), expect.end()); + } +} + +BOOST_AUTO_TEST_CASE(Linear_4x1x1_Symmetric_Reverse_Add_3x1x1_And_Existing_Expand) +{ + auto graph = CSRGraph{}; + + for (auto i = 2; i >= 0; --i) { + graph.addConnection(i, i + 1); + graph.addConnection(i + 1, i); + } + + for (auto i = 3; i >= 0; --i) { + graph.addConnection(i, i); + } + + graph.compress(4); + + for (auto i = 5; i >= 3; --i) { + graph.addConnection(i + 1, i); + graph.addConnection(i, i + 1); + } + + for (auto i = 6; i >= 4; --i) { + graph.addConnection(i, i); + } + + for (auto i = 0; i < 4; ++i) { + graph.addConnection(4, 5); + } + + graph.compress(7, true); + + for (auto i = 0; i < 6; ++i) { + graph.addConnection(3, 2); + } + + graph.compress(7, true); + + BOOST_CHECK_EQUAL(graph.numVertices(), std::size_t{7}); + BOOST_CHECK_EQUAL(graph.numEdges(), std::size_t{19}); + + { + const auto& nzMap = graph.compressedIndexMap(); + const auto expect = std::vector { + 7, 8, 4, 5, 1, 2, 9, 6, 3, 0, 17, 16, 14, 13, 11, 10, 18, 15, 12, + 13, 13, 13, 13, // for i=0..3: 4 -> 5 + 8, 8, 8, 8, 8, 8, // for i=0..5: 3 -> 2 + }; + + BOOST_CHECK_EQUAL_COLLECTIONS(nzMap .begin(), nzMap .end(), + expect.begin(), expect.end()); + } +} + +BOOST_AUTO_TEST_CASE(Linear_4x1x1_Symmetric_Reverse_Add_3x1x1_And_Existing_Expand_NoExpand) +{ + auto graph = CSRGraph{}; + + for (auto i = 2; i >= 0; --i) { + graph.addConnection(i, i + 1); + graph.addConnection(i + 1, i); + } + + for (auto i = 3; i >= 0; --i) { + graph.addConnection(i, i); + } + + graph.compress(4); + + for (auto i = 5; i >= 3; --i) { + graph.addConnection(i + 1, i); + graph.addConnection(i, i + 1); + } + + for (auto i = 0; i < 4; ++i) { + graph.addConnection(4, 5); + } + + for (auto i = 6; i >= 4; --i) { + graph.addConnection(i, i); + } + + graph.compress(7, true); + + BOOST_CHECK_EQUAL(graph.numVertices(), std::size_t{7}); + BOOST_CHECK_EQUAL(graph.numEdges(), std::size_t{19}); + + { + const auto& nzMap = graph.compressedIndexMap(); + const auto expect = std::vector { + 7, 8, 4, 5, 1, 2, 9, 6, 3, 0, 17, 16, 14, 13, 11, 10, + 13, 13, 13, 13, // for i=0..3: 4 -> 5 + 18, 15, 12, + }; + + BOOST_CHECK_EQUAL_COLLECTIONS(nzMap .begin(), nzMap .end(), + expect.begin(), expect.end()); + } + + for (auto i = 0; i < 6; ++i) { + graph.addConnection(3, 2); + } + + graph.compress(7, false); + + BOOST_CHECK_EQUAL(graph.numVertices(), std::size_t{7}); + BOOST_CHECK_EQUAL(graph.numEdges(), std::size_t{19}); + + { + const auto& nzMap = graph.compressedIndexMap(); + const auto expect = std::vector { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, + 8, 8, 8, 8, 8, 8, // for i=0..5: 3 -> 2 + }; + + BOOST_CHECK_EQUAL_COLLECTIONS(nzMap .begin(), nzMap .end(), + expect.begin(), expect.end()); + } +} + +BOOST_AUTO_TEST_CASE(Linear_4x1x1_Symmetric_Multiple) +{ + auto graph = CSRGraph{}; + + graph.addConnection(1, 1); + graph.addConnection(0, 0); + + for (auto i = 0; i < 4 - 1; ++i) { + graph.addConnection(i, i + 1); + graph.addConnection(i + 1, i); + graph.addConnection(i + 1, i); + graph.addConnection(i, i + 1); + graph.addConnection(i, i + 1); + } + + graph.addConnection(2, 2); + graph.addConnection(3, 3); + + graph.compress(4); + + BOOST_CHECK_EQUAL(graph.numVertices(), std::size_t{4}); + BOOST_CHECK_EQUAL(graph.numEdges(), std::size_t{10}); + + { + const auto& nzMap = graph.compressedIndexMap(); + const auto expect = std::vector { + 3, 0, + 1, 2, 2, 1, 1, // i = 0 + 4, 5, 5, 4, 4, // i = 1 + 7, 8, 8, 7, 7, // i = 2 + 6, 9, + }; + BOOST_CHECK_EQUAL_COLLECTIONS(nzMap .begin(), nzMap .end(), + expect.begin(), expect.end()); + } +} + +BOOST_AUTO_TEST_CASE(Linear_4x1x1_Symmetric_Multiple_Add_3x1x1_Reverse) +{ + auto graph = CSRGraph{}; + + graph.addConnection(1, 1); + graph.addConnection(0, 0); + graph.addConnection(0, 0); + graph.addConnection(1, 1); + + for (auto i = 0; i < 4 - 1; ++i) { + graph.addConnection(i, i + 1); + graph.addConnection(i + 1, i); + graph.addConnection(i + 1, i); + graph.addConnection(i, i + 1); + graph.addConnection(i, i + 1); + } + + graph.addConnection(2, 2); + graph.addConnection(3, 3); + graph.addConnection(3, 3); + graph.addConnection(2, 2); + + graph.compress(4); + + for (auto i = 5; i >= 3; --i) { + graph.addConnection(i + 1, i); + graph.addConnection(i, i + 1); + } + + for (auto i = 6; i >= 4; --i) { + graph.addConnection(i, i); + } + + graph.compress(7); + + BOOST_CHECK_EQUAL(graph.numVertices(), std::size_t{7}); + BOOST_CHECK_EQUAL(graph.numEdges(), std::size_t{19}); + + { + const auto& nzMap = graph.compressedIndexMap(); + const auto expect = std::vector { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, // Original + 17, 16, 14, 13, 11, 10, 18, 15, 12, // 3x1x1 reverse + }; + + BOOST_CHECK_EQUAL_COLLECTIONS(nzMap .begin(), nzMap .end(), + expect.begin(), expect.end()); + } +} + +BOOST_AUTO_TEST_CASE(Linear_4x1x1_Symmetric_Multiple_Add_3x1x1_Reverse_Expand) +{ + auto graph = CSRGraph{}; + + graph.addConnection(1, 1); + graph.addConnection(0, 0); + graph.addConnection(0, 0); + graph.addConnection(1, 1); + + for (auto i = 0; i < 4 - 1; ++i) { + graph.addConnection(i, i + 1); + graph.addConnection(i + 1, i); + graph.addConnection(i + 1, i); + graph.addConnection(i, i + 1); + graph.addConnection(i, i + 1); + } + + graph.addConnection(2, 2); + graph.addConnection(3, 3); + graph.addConnection(3, 3); + graph.addConnection(2, 2); + + graph.compress(4); + + for (auto i = 5; i >= 3; --i) { + graph.addConnection(i + 1, i); + graph.addConnection(i, i + 1); + } + + for (auto i = 6; i >= 4; --i) { + graph.addConnection(i, i); + } + + graph.compress(7, true); + + BOOST_CHECK_EQUAL(graph.numVertices(), std::size_t{7}); + BOOST_CHECK_EQUAL(graph.numEdges(), std::size_t{19}); + + { + const auto& nzMap = graph.compressedIndexMap(); + const auto expect = std::vector { + 3, 0, 0, 3, + 1, 2, 2, 1, 1, // i = 0 + 4, 5, 5, 4, 4, // i = 1 + 7, 8, 8, 7, 7, // i = 2 + 6, 9, 9, 6, + 17, 16, 14, 13, 11, 10, 18, 15, 12, // 3x1x1 reverse + }; + + BOOST_CHECK_EQUAL_COLLECTIONS(nzMap .begin(), nzMap .end(), + expect.begin(), expect.end()); + } +} + +BOOST_AUTO_TEST_CASE(Linear_4x1x1_Symmetric_Multiple_Add_3x1x1_Reverse_And_Existing) +{ + auto graph = CSRGraph{}; + + for (auto i = 0; i < 4 - 1; ++i) { + graph.addConnection(i, i + 1); + graph.addConnection(i + 1, i); + graph.addConnection(i + 1, i); + graph.addConnection(i, i + 1); + graph.addConnection(i, i + 1); + } + + for (auto i = 0; i < 4; ++i) { + graph.addConnection(i, i); + } + + graph.compress(4); + + for (auto i = 5; i >= 3; --i) { + graph.addConnection(i + 1, i); + graph.addConnection(i, i + 1); + } + + for (auto i = 4; i < 7; ++i) { + graph.addConnection(i, i); + } + + for (auto i = 0; i < 5; ++i) { + graph.addConnection(1, 2); + } + + graph.compress(7); + + BOOST_CHECK_EQUAL(graph.numVertices(), std::size_t{7}); + BOOST_CHECK_EQUAL(graph.numEdges(), std::size_t{19}); + + { + const auto& nzMap = graph.compressedIndexMap(); + const auto expect = std::vector { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, // Original + 17, 16, 14, 13, 11, 10, 12, 15, 18, // 3x1x1 reverse + 4, 4, 4, 4, 4, // for i=0..4: 1 -> 2 + }; + + BOOST_CHECK_EQUAL_COLLECTIONS(nzMap .begin(), nzMap .end(), + expect.begin(), expect.end()); + } +} + +BOOST_AUTO_TEST_CASE(Linear_4x1x1_Symmetric_Multiple_Add_3x1x1_Reverse_And_Existing_Expand) +{ + auto graph = CSRGraph{}; + + for (auto i = 0; i < 4 - 1; ++i) { + graph.addConnection(i, i + 1); + graph.addConnection(i + 1, i); + graph.addConnection(i + 1, i); + graph.addConnection(i, i + 1); + graph.addConnection(i, i + 1); + } + + for (auto i = 0; i < 4; ++i) { + graph.addConnection(i, i); + } + + graph.compress(4); + + for (auto i = 5; i >= 3; --i) { + graph.addConnection(i + 1, i); + graph.addConnection(i, i + 1); + } + + for (auto i = 0; i < 6; ++i) { + graph.addConnection(5, 6); + } + + for (auto i = 4; i < 7; ++i) { + graph.addConnection(i, i); + } + + graph.compress(7, true); + + BOOST_CHECK_EQUAL(graph.numVertices(), std::size_t{7}); + BOOST_CHECK_EQUAL(graph.numEdges(), std::size_t{19}); + + { + const auto& nzMap = graph.compressedIndexMap(); + const auto expect = std::vector { + 1, 2, 2, 1, 1, // i = 0 + 4, 5, 5, 4, 4, // i = 1 + 7, 8, 8, 7, 7, // i = 2 + 0, 3, 6, 9, + 17, 16, 14, 13, 11, 10, // 3x1x1 reverse + 16, 16, 16, 16, 16, 16, // for i=0..5: 5 -> 6 + 12, 15, 18, + }; + + BOOST_CHECK_EQUAL_COLLECTIONS(nzMap .begin(), nzMap .end(), + expect.begin(), expect.end()); + } +} + +BOOST_AUTO_TEST_CASE(Linear_4x1x1_Symmetric_Multiple_Add_3x1x1_Reverse_And_Existing_Expand_NoExpand) +{ + auto graph = CSRGraph{}; + + for (auto i = 0; i < 4 - 1; ++i) { + graph.addConnection(i, i + 1); + graph.addConnection(i + 1, i); + graph.addConnection(i + 1, i); + graph.addConnection(i, i + 1); + graph.addConnection(i, i + 1); + } + + for (auto i = 3; i >= 0; --i) { + graph.addConnection(i, i); + } + + graph.compress(4); + + for (auto i = 5; i >= 3; --i) { + graph.addConnection(i + 1, i); + graph.addConnection(i, i + 1); + } + + graph.compress(7, true); + + for (auto i = 4; i < 7; ++i) { + graph.addConnection(i, i); + } + + for (auto i = 0; i < 6; ++i) { + graph.addConnection(5, 6); + } + + graph.compress(7); + + BOOST_CHECK_EQUAL(graph.numVertices(), std::size_t{7}); + BOOST_CHECK_EQUAL(graph.numEdges(), std::size_t{19}); + + { + const auto& nzMap = graph.compressedIndexMap(); + const auto expect = std::vector { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, 16, 17, + 12, 15, 18, + 16, 16, 16, 16, 16, 16, // for i=0..5: 5 -> 6 + }; + + BOOST_CHECK_EQUAL_COLLECTIONS(nzMap .begin(), nzMap .end(), + expect.begin(), expect.end()); + } +} + +BOOST_AUTO_TEST_CASE(Linear_4x1x1_Symmetric_Multiple_Repeated) +{ + auto graph = CSRGraph{}; + + const auto nrep = 20; + + for (auto n = 0; n < nrep; ++n) { + for (auto i = 0; i < 4 - 1; ++i) { + graph.addConnection(i, i + 1); + graph.addConnection(i + 1, i); + graph.addConnection(i + 1, i); + graph.addConnection(i, i + 1); + graph.addConnection(i, i + 1); + } + + for (auto i = 0; i < 4; ++i) { + graph.addConnection(i, i); + } + } + + graph.compress(4); + + BOOST_CHECK_EQUAL(graph.numVertices(), std::size_t{4}); + BOOST_CHECK_EQUAL(graph.numEdges(), std::size_t{10}); + + { + const auto& nzMap = graph.compressedIndexMap(); + const auto expect0 = std::vector { + 1, 2, 2, 1, 1, // i = 0 + 4, 5, 5, 4, 4, // i = 1 + 7, 8, 8, 7, 7, // i = 2 + 0, 3, 6, 9, + }; + + auto expect = std::vector{}; + for (auto n = 0; n < nrep; ++n) { + expect.insert(expect.end(), expect0.begin(), expect0.end()); + } + + BOOST_CHECK_EQUAL_COLLECTIONS(nzMap .begin(), nzMap .end(), + expect.begin(), expect.end()); + } +} + +BOOST_AUTO_TEST_CASE(Linear_4x1x1_Symmetric_Multiple_Repeated_Add_3x1x1) +{ + auto graph = CSRGraph{}; + + const auto nrep = 20; + + for (auto n = 0; n < nrep; ++n) { + for (auto i = 0; i < 4 - 1; ++i) { + graph.addConnection(i, i + 1); + graph.addConnection(i + 1, i); + graph.addConnection(i + 1, i); + graph.addConnection(i, i + 1); + graph.addConnection(i, i + 1); + } + + for (auto i = 0; i < 4; ++i) { + graph.addConnection(i, i); + } + } + + graph.compress(4); + + for (auto n = 0; n < nrep; ++n) { + for (auto i = 5; i >= 3; --i) { + graph.addConnection(i + 1, i); + graph.addConnection(i, i + 1); + } + + graph.addConnection(6, 6); + graph.addConnection(5, 5); + + for (auto i = 3; i < 6; ++i) { + graph.addConnection(i, i + 1); + graph.addConnection(i + 1, i); + } + + graph.addConnection(4, 4); + } + + graph.compress(7); + + BOOST_CHECK_EQUAL(graph.numVertices(), std::size_t{7}); + BOOST_CHECK_EQUAL(graph.numEdges(), std::size_t{19}); + + { + const auto& nzMap = graph.compressedIndexMap(); + + auto expectAdd = std::vector { + 17, 16, 14, 13, 11, 10, + 18, 15, + 10, 11, 13, 14, 16, 17, + 12, + }; + + auto expect = std::vector { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, }; // Compressed original + for (auto n = 0; n < nrep; ++n) { + expect.insert(expect.end(), expectAdd.begin(), expectAdd.end()); + } + + BOOST_CHECK_EQUAL_COLLECTIONS(nzMap .begin(), nzMap .end(), + expect.begin(), expect.end()); + } +} + +BOOST_AUTO_TEST_CASE(Linear_4x1x1_Symmetric_Multiple_Repeated_Add_3x1x1_Expand) +{ + auto graph = CSRGraph{}; + + const auto nrep = 20; + + for (auto n = 0; n < nrep; ++n) { + for (auto i = 0; i < 4 - 1; ++i) { + graph.addConnection(i, i + 1); + graph.addConnection(i + 1, i); + graph.addConnection(i + 1, i); + graph.addConnection(i, i + 1); + graph.addConnection(i, i + 1); + } + + for (auto i = 0; i < 4; ++i) { + graph.addConnection(i, i); + graph.addConnection(3 - i, 3 - i); + } + } + + graph.compress(4); + + for (auto n = 0; n < nrep; ++n) { + for (auto i = 5; i >= 3; --i) { + graph.addConnection(i + 1, i); + graph.addConnection(i, i + 1); + } + + for (auto i = 6; i >= 4; --i) { + graph.addConnection(i, i); + } + + for (auto i = 3; i < 6; ++i) { + graph.addConnection(i, i + 1); + graph.addConnection(i + 1, i); + } + } + + graph.compress(7, true); + + BOOST_CHECK_EQUAL(graph.numVertices(), std::size_t{7}); + BOOST_CHECK_EQUAL(graph.numEdges(), std::size_t{19}); + + { + const auto& nzMap = graph.compressedIndexMap(); + const auto expect0 = std::vector { + 1, 2, 2, 1, 1, // i = 0 + 4, 5, 5, 4, 4, // i = 1 + 7, 8, 8, 7, 7, // i = 2 + 0, 9, + 3, 6, + 6, 3, + 9, 0, + }; + + auto expectAdd = std::vector { + 17, 16, 14, 13, 11, 10, + 18, 15, 12, + 10, 11, 13, 14, 16, 17, + }; + + auto expect = std::vector{}; + for (auto n = 0; n < nrep; ++n) { + expect.insert(expect.end(), expect0.begin(), expect0.end()); + } + + for (auto n = 0; n < nrep; ++n) { + expect.insert(expect.end(), expectAdd.begin(), expectAdd.end()); + } + + BOOST_CHECK_EQUAL_COLLECTIONS(nzMap .begin(), nzMap .end(), + expect.begin(), expect.end()); + } +} + +BOOST_AUTO_TEST_CASE(Linear_4x1x1_Symmetric_Multiple_Repeated_Add_3x1x1_Expand_NoExpand) +{ + auto graph = CSRGraph{}; + + const auto nrep = 20; + + for (auto n = 0; n < nrep; ++n) { + for (auto i = 0; i < 4 - 1; ++i) { + graph.addConnection(i, i + 1); + graph.addConnection(i + 1, i); + graph.addConnection(i + 1, i); + graph.addConnection(i, i + 1); + graph.addConnection(i, i + 1); + } + + for (auto i = 0; i < 4; ++i) { + graph.addConnection(i, i); + graph.addConnection(3 - i, 3 - i); + } + } + + graph.compress(4); + + for (auto n = 0; n < nrep; ++n) { + for (auto i = 5; i >= 3; --i) { + graph.addConnection(i + 1, i); + graph.addConnection(i, i + 1); + } + + for (auto i = 6; i >= 4; --i) { + graph.addConnection(i, i); + } + + for (auto i = 3; i < 6; ++i) { + graph.addConnection(i, i + 1); + graph.addConnection(i + 1, i); + } + } + + graph.compress(7, true); + + for (auto n = 0; n < nrep; ++n) { + graph.addConnection(1, 0); + } + + graph.compress(7); + + BOOST_CHECK_EQUAL(graph.numVertices(), std::size_t{7}); + BOOST_CHECK_EQUAL(graph.numEdges(), std::size_t{19}); + + { + const auto& nzMap = graph.compressedIndexMap(); + const auto expect0 = std::vector { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, + }; + + auto expect = expect0; + expect.insert(expect.end(), nrep, 2); + + BOOST_CHECK_EQUAL_COLLECTIONS(nzMap .begin(), nzMap .end(), + expect.begin(), expect.end()); + } +} + +BOOST_AUTO_TEST_SUITE_END() // Tracked + +BOOST_AUTO_TEST_SUITE_END() // Permit_Self_Connections