Skip to content

Commit

Permalink
Tpetra CrsGraph: Add method "getOffRankOffsets"
Browse files Browse the repository at this point in the history
  • Loading branch information
cgcgcg committed Jun 7, 2021
1 parent 5768b2c commit 0c49c3d
Show file tree
Hide file tree
Showing 6 changed files with 659 additions and 29 deletions.
12 changes: 12 additions & 0 deletions packages/tpetra/core/src/Tpetra_CrsGraph_decl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1387,6 +1387,9 @@ namespace Tpetra {
void
getLocalDiagOffsets (const Kokkos::View<size_t*, device_type, Kokkos::MemoryUnmanaged>& offsets) const;

void
getLocalOffRankOffsets (const Kokkos::View<size_t*, device_type, Kokkos::MemoryUnmanaged>& offsets) const;

/// \brief Backwards compatibility overload of the above method.
///
/// This method takes a Teuchos::ArrayRCP instead of a
Expand Down Expand Up @@ -2064,6 +2067,10 @@ namespace Tpetra {
/// </ul>
void computeGlobalConstants ();

bool haveLocalOffRankOffsets() const { return haveLocalOffRankOffsets_;}

void computeLocalOffRankOffsets ();

protected:
/// \brief Compute local constants, if they have not yet been computed.
///
Expand Down Expand Up @@ -2383,6 +2390,9 @@ namespace Tpetra {
/// The k_numRowEntries_ array has has length getNodeNumRows(),
/// again if it is allocated.

public:
typename local_graph_type::row_map_type::const_type k_rowPtrsOffRank_;
protected:
/// \brief The type of k_numRowEntries_ (see below).
///
/// This View gets used only on host. However, making this
Expand Down Expand Up @@ -2438,6 +2448,8 @@ namespace Tpetra {
bool haveLocalConstants_ = false;
//! Whether all processes have computed global constants.
bool haveGlobalConstants_ = false;
//!
bool haveLocalOffRankOffsets_ = false;

typedef typename std::map<global_ordinal_type, std::vector<global_ordinal_type> > nonlocals_type;

Expand Down
236 changes: 236 additions & 0 deletions packages/tpetra/core/src/Tpetra_CrsGraph_def.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
#include "Tpetra_Details_copyOffsets.hpp"
#include "Tpetra_Details_gathervPrint.hpp"
#include "Tpetra_Details_getGraphDiagOffsets.hpp"
#include "Tpetra_Details_getGraphOffRankOffsets.hpp"
#include "Tpetra_Details_makeColMap.hpp"
#include "Tpetra_Details_Profiling.hpp"
#include "Tpetra_Details_getEntryOnHost.hpp"
Expand Down Expand Up @@ -4444,6 +4445,24 @@ namespace Tpetra {
}


template <class LocalOrdinal, class GlobalOrdinal, class Node>
void
CrsGraph<LocalOrdinal, GlobalOrdinal, Node>::
computeLocalOffRankOffsets ()
{
using ::Tpetra::Details::ProfilingRegion;

ProfilingRegion regionCGC ("Tpetra::CrsGraph::computeLocalOffRankOffsets");

// release previous view
k_rowPtrsOffRank_ = typename local_graph_type::row_map_type::non_const_type("offRankOffset",0);
auto k_offRankOffset = typename local_graph_type::row_map_type::non_const_type("offRankOffset",this->getNodeNumRows());
this->getLocalOffRankOffsets(k_offRankOffset);
k_rowPtrsOffRank_ = k_offRankOffset;
this->haveLocalOffRankOffsets_ = true;
}


template <class LocalOrdinal, class GlobalOrdinal, class Node>
void
CrsGraph<LocalOrdinal, GlobalOrdinal, Node>::
Expand Down Expand Up @@ -6701,6 +6720,204 @@ namespace Tpetra {
} // debug_
}

template <class LocalOrdinal, class GlobalOrdinal, class Node>
void
CrsGraph<LocalOrdinal, GlobalOrdinal, Node>::
getLocalOffRankOffsets (const Kokkos::View<size_t*, device_type, Kokkos::MemoryUnmanaged>& offsets) const
{
using std::endl;
using LO = LocalOrdinal;
// using GO = GlobalOrdinal;
const char tfecfFuncName[] = "getLocalOffRankOffsets: ";
const bool verbose = verbose_;

std::unique_ptr<std::string> prefix;
if (verbose) {
prefix = this->createPrefix("CrsGraph", "getLocalOffRankOffsets");
std::ostringstream os;
os << *prefix << "offsets.extent(0)=" << offsets.extent(0)
<< endl;
std::cerr << os.str();
}

TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC
(! hasColMap (), std::runtime_error, "The graph must have a column Map.");
const LO lclNumRows = static_cast<LO> (this->getNodeNumRows ());
TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC
(static_cast<LO> (offsets.extent (0)) < lclNumRows,
std::invalid_argument, "offsets.extent(0) = " <<
offsets.extent (0) << " < getNodeNumRows() = " << lclNumRows << ".");

const map_type& rowMap = * (this->getRowMap ());
const map_type& colMap = * (this->getColMap ());
const map_type& domMap = * (this->getDomainMap ());

// We only use these in debug mode, but since debug mode is a
// run-time option, they need to exist here. That's why we create
// the vector with explicit size zero, to avoid overhead if debug
// mode is off.
bool allRowMapDiagEntriesInColMap = true;
bool allDiagEntriesFound = true;
bool allOffsetsCorrect = true;
bool noOtherWeirdness = true;
using wrong_offsets_type = std::vector<std::pair<LO, size_t> >;
wrong_offsets_type wrongOffsets(0);

// mfh 12 Mar 2016: LocalMap works on (CUDA) device. It has just
// the subset of Map functionality that we need below.
auto lclRowMap = rowMap.getLocalMap ();
auto lclColMap = colMap.getLocalMap ();
auto lclDomMap = domMap.getLocalMap ();

// FIXME (mfh 16 Dec 2015) It's easy to thread-parallelize this
// setup, at least on the host. For CUDA, we have to use LocalMap
// (that comes from each of the two Maps).

TEUCHOS_ASSERT(this->isSorted ());
if (isFillComplete ()) {
auto lclGraph = this->getLocalGraph ();
::Tpetra::Details::getGraphOffRankOffsets (offsets,
lclRowMap, lclColMap, lclDomMap,
lclGraph.row_map,
lclGraph.entries);
}
// else {
// // NOTE (mfh 22 Feb 2017): We have to run this code on host,
// // since the graph is not fill complete. The previous version
// // of this code assumed UVM; this version does not.
// auto offsets_h = Kokkos::create_mirror_view (offsets);

// for (LO lclRowInd = 0; lclRowInd < lclNumRows; ++lclRowInd) {
// // Find the diagonal entry. Since the row Map and column Map
// // may differ, we have to compare global row and column
// // indices, not local.
// const GO gblRowInd = lclRowMap.getGlobalElement (lclRowInd);
// const GO gblColInd = gblRowInd;
// const LO lclColInd = lclColMap.getLocalElement (gblColInd);

// if (lclColInd == Tpetra::Details::OrdinalTraits<LO>::invalid ()) {
// allRowMapDiagEntriesInColMap = false;
// offsets_h(lclRowInd) = Tpetra::Details::OrdinalTraits<size_t>::invalid ();
// }
// else {
// const RowInfo rowInfo = this->getRowInfo (lclRowInd);
// if (static_cast<LO> (rowInfo.localRow) == lclRowInd &&
// rowInfo.numEntries > 0) {

// auto colInds = this->getLocalKokkosRowView (rowInfo);
// const size_t hint = 0; // not needed for this algorithm
// const size_t offset =
// KokkosSparse::findRelOffset (colInds, rowInfo.numEntries,
// lclColInd, hint, sorted);
// offsets_h(lclRowInd) = offset;

// if (debug_) {
// // Now that we have what we think is an offset, make sure
// // that it really does point to the diagonal entry. Offsets
// // are _relative_ to each row, not absolute (for the whole
// // (local) graph).
// Teuchos::ArrayView<const LO> lclColInds;
// try {
// this->getLocalRowView (lclRowInd, lclColInds);
// }
// catch (...) {
// noOtherWeirdness = false;
// }
// // Don't continue with error checking if the above failed.
// if (noOtherWeirdness) {
// const size_t numEnt = lclColInds.size ();
// if (offset >= numEnt) {
// // Offsets are relative to each row, so this means that
// // the offset is out of bounds.
// allOffsetsCorrect = false;
// wrongOffsets.push_back (std::make_pair (lclRowInd, offset));
// } else {
// const LO actualLclColInd = lclColInds[offset];
// const GO actualGblColInd = lclColMap.getGlobalElement (actualLclColInd);
// if (actualGblColInd != gblColInd) {
// allOffsetsCorrect = false;
// wrongOffsets.push_back (std::make_pair (lclRowInd, offset));
// }
// }
// }
// } // debug_
// }
// else { // either row is empty, or something went wrong w/ getRowInfo()
// offsets_h(lclRowInd) = Tpetra::Details::OrdinalTraits<size_t>::invalid ();
// allDiagEntriesFound = false;
// }
// } // whether lclColInd is a valid local column index
// } // for each local row

// Kokkos::deep_copy (offsets, offsets_h);
// }
// whether the graph is fill complete

if (verbose && wrongOffsets.size () != 0) {
std::ostringstream os;
os << *prefix << "Wrong offsets: [";
for (size_t k = 0; k < wrongOffsets.size (); ++k) {
os << "(" << wrongOffsets[k].first << ","
<< wrongOffsets[k].second << ")";
if (k + 1 < wrongOffsets.size ()) {
os << ", ";
}
}
os << "]" << endl;
std::cerr << os.str();
}

if (debug_) {
using Teuchos::reduceAll;
using std::endl;
Teuchos::RCP<const Teuchos::Comm<int> > comm = this->getComm ();
const bool localSuccess =
allRowMapDiagEntriesInColMap && allDiagEntriesFound && allOffsetsCorrect;
const int numResults = 5;
int lclResults[5];
lclResults[0] = allRowMapDiagEntriesInColMap ? 1 : 0;
lclResults[1] = allDiagEntriesFound ? 1 : 0;
lclResults[2] = allOffsetsCorrect ? 1 : 0;
lclResults[3] = noOtherWeirdness ? 1 : 0;
// min-all-reduce will compute least rank of all the processes
// that didn't succeed.
lclResults[4] = ! localSuccess ? comm->getRank () : comm->getSize ();

int gblResults[5];
gblResults[0] = 0;
gblResults[1] = 0;
gblResults[2] = 0;
gblResults[3] = 0;
gblResults[4] = 0;
reduceAll<int, int> (*comm, Teuchos::REDUCE_MIN,
numResults, lclResults, gblResults);

if (gblResults[0] != 1 || gblResults[1] != 1 || gblResults[2] != 1
|| gblResults[3] != 1) {
std::ostringstream os; // build error message
os << "Issue(s) that we noticed (on Process " << gblResults[4] << ", "
"possibly among others): " << endl;
if (gblResults[0] == 0) {
os << " - The column Map does not contain at least one diagonal entry "
"of the graph." << endl;
}
if (gblResults[1] == 0) {
os << " - On one or more processes, some row does not contain a "
"diagonal entry." << endl;
}
if (gblResults[2] == 0) {
os << " - On one or more processes, some offsets are incorrect."
<< endl;
}
if (gblResults[3] == 0) {
os << " - One or more processes had some other error."
<< endl;
}
TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(true, std::runtime_error, os.str());
}
} // debug_
}

namespace { // (anonymous)

// mfh 21 Jan 2016: This is useful for getLocalDiagOffsets (see
Expand Down Expand Up @@ -7551,6 +7768,7 @@ namespace Tpetra {

std::swap(graph.rowPtrsUnpacked_dev_, this->rowPtrsUnpacked_dev_);
std::swap(graph.rowPtrsUnpacked_host_, this->rowPtrsUnpacked_host_);
std::swap(graph.k_rowPtrsOffRank_, this->k_rowPtrsOffRank_);

std::swap(graph.lclIndsUnpacked_wdv, this->lclIndsUnpacked_wdv);
std::swap(graph.gblInds_wdv, this->gblInds_wdv);
Expand All @@ -7566,6 +7784,7 @@ namespace Tpetra {
std::swap(graph.noRedundancies_, this->noRedundancies_);
std::swap(graph.haveLocalConstants_, this->haveLocalConstants_);
std::swap(graph.haveGlobalConstants_, this->haveGlobalConstants_);
std::swap(graph.haveLocalOffRankOffsets_, this->haveLocalOffRankOffsets_);

std::swap(graph.sortGhostsAssociatedWithEachProcessor_, this->sortGhostsAssociatedWithEachProcessor_);

Expand Down Expand Up @@ -7628,6 +7847,7 @@ namespace Tpetra {
output = this->noRedundancies_ == graph.noRedundancies_ ? output : false;
output = this->haveLocalConstants_ == graph.haveLocalConstants_ ? output : false;
output = this->haveGlobalConstants_ == graph.haveGlobalConstants_ ? output : false;
output = this->haveLocalOffRankOffsets_ == graph.haveLocalOffRankOffsets_ ? output : false;
output = this->sortGhostsAssociatedWithEachProcessor_ == this->sortGhostsAssociatedWithEachProcessor_ ? output : false;

// Compare nonlocals_ -- std::map<GlobalOrdinal, std::vector<GlobalOrdinal> >
Expand Down Expand Up @@ -7662,6 +7882,22 @@ namespace Tpetra {
output = rowPtrsThis(i) == rowPtrsGraph(i) ? output : false;
}

if (this->haveLocalOffRankOffsets() && graph.haveLocalOffRankOffsets()) {
// Compare this->k_rowPtrsOffRank_ isa Kokkos::View<LocalOrdinal*, ...>
output = this->k_rowPtrsOffRank_.extent(0) == graph.k_rowPtrsOffRank_.extent(0) ? output : false;
if(output && this->k_rowPtrsOffRank_.extent(0) > 0)
{
typename local_graph_type::row_map_type::const_type::HostMirror k_rowPtrsOffRank_host_this = Kokkos::create_mirror_view(this->k_rowPtrsOffRank_);
typename local_graph_type::row_map_type::const_type::HostMirror k_rowPtrsOffRank_host_graph= Kokkos::create_mirror_view(graph.k_rowPtrsOffRank_);
Kokkos::deep_copy(k_rowPtrsOffRank_host_this, this->k_rowPtrsOffRank_);
Kokkos::deep_copy(k_rowPtrsOffRank_host_graph, graph.k_rowPtrsOffRank_);
for(size_t i=0; output && i<k_rowPtrsOffRank_host_this.extent(0); i++)
output = k_rowPtrsOffRank_host_this(i) == k_rowPtrsOffRank_host_graph(i) ? output : false;
}
}
else
output = false;

// Compare lclIndsUnpacked_wdv isa Kokkos::View<LocalOrdinal*, ...>
output = this->lclIndsUnpacked_wdv.extent(0) == graph.lclIndsUnpacked_wdv.extent(0) ? output : false;
if(output && this->lclIndsUnpacked_wdv.extent(0) > 0)
Expand Down
67 changes: 67 additions & 0 deletions packages/tpetra/core/src/Tpetra_Details_getGraphOffRankOffsets.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
// @HEADER
// ***********************************************************************
//
// Tpetra: Templated Linear Algebra Services Package
// Copyright (2008) Sandia Corporation
//
// Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
// 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 SANDIA CORPORATION "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 SANDIA CORPORATION 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 Michael A. Heroux ([email protected])
//
// ************************************************************************
// @HEADER
*/

#include "TpetraCore_config.h"

#if defined(HAVE_TPETRA_EXPLICIT_INSTANTIATION)

// We protect the contents of this file with macros, to assist
// applications that circumvent Trilinos' build system. (We do NOT
// recommend this.) That way, they can still build this file, but as
// long as the macros have correct definitions, they won't build
// anything that's not enabled.

#include "KokkosCompat_ClassicNodeAPI_Wrapper.hpp"
#include "Tpetra_Details_getGraphOffRankOffsets_decl.hpp"
#include "Tpetra_Details_getGraphOffRankOffsets_def.hpp"
#include "TpetraCore_ETIHelperMacros.h"

namespace Tpetra {

TPETRA_ETI_MANGLING_TYPEDEFS()

TPETRA_INSTANTIATE_LGN( TPETRA_DETAILS_IMPL_GETGRAPHOFFRANKOFFSETS_INSTANT )

} // namespace Tpetra

#endif // Whether we should build this specialization
Loading

0 comments on commit 0c49c3d

Please sign in to comment.