Sample solution

Content

#include <cassert>
#include <mpi.h>
#include <printf.hpp>
#include <hpc/matvec/gematrix.hpp>
#include <hpc/matvec/iterators.hpp>
#include <hpc/matvec/print.hpp>
#include <hpc/mpi/matrix.hpp>
#include <hpc/mpi/vector.hpp>

int main(int argc, char** argv) {
   MPI_Init(&argc, &argv);

   int nof_processes; MPI_Comm_size(MPI_COMM_WORLD, &nof_processes);
   int rank; MPI_Comm_rank(MPI_COMM_WORLD, &rank);
   assert(nof_processes == 2);

   using namespace hpc::matvec;
   using namespace hpc::mpi;
   using namespace hpc::aux;

   GeMatrix<double> A(3, 7, Order::ColMajor);
   GeMatrix<double> B(3, 7, Order::RowMajor);
   MPI_Datatype datatype_A = get_type(A);
   MPI_Datatype datatype_B = get_type(B);
   
   if (rank == 0) {
      MPI_Aint true_extent; MPI_Aint true_lb;
      MPI_Type_get_true_extent(datatype_A, &true_lb, &true_extent);
      MPI_Aint extent; MPI_Aint lb;
      MPI_Type_get_extent(datatype_A, &lb, &extent);
      auto size = sizeof(double) * A.numRows() * A.numCols();
      fmt::printf("true extent of A = %d\n", true_extent);
      fmt::printf("extent of A = %d\n", true_extent);
      fmt::printf("size = %zd\n", size);
      assert(true_extent == size);
   }

   if (rank == 0) {
      for (auto [i, j, Aij]: A) {
	 Aij = i * 100 + j;
      }
      MPI_Send(&A(0, 0), 1, datatype_A, 1, 0, MPI_COMM_WORLD);
      MPI_Status status;
      MPI_Recv(&B(0, 0), 1, datatype_B, 1, 0, MPI_COMM_WORLD, &status);
      for (auto [i, j, Aij]: A) {
	 if (Aij != B(i, j)) {
	    fmt::printf("verification failed for (%d,%d): %lg vs %lg\n",
	       i, j, Aij, B(i, j));
	 }
      }
   } else {
      MPI_Status status;
      MPI_Recv(&A(0, 0), 1, datatype_A, 0, 0, MPI_COMM_WORLD, &status);
      MPI_Send(&A(0, 0), 1, datatype_A, 0, 0, MPI_COMM_WORLD);
   }

   MPI_Finalize();
}
#ifndef HPC_MPI_MATRIX_HPP
#define HPC_MPI_MATRIX_HPP 1

#include <cassert>
#include <memory>
#include <type_traits>
#include <vector>
#include <mpi.h>
#include <hpc/aux/slices.hpp>
#include <hpc/matvec/gematrix.hpp>
#include <hpc/mpi/fundamental.hpp>

namespace hpc { namespace mpi {

/* construct MPI data type for a row of Matrix A
   where the extent is adapted such that consecutive
   rows can be combined even if they overlap */
template<typename T, template<typename> class Matrix,
   Require<Ge<Matrix<T>>> = true>
MPI_Datatype get_row_type(const Matrix<T>& A) {
   MPI_Datatype rowtype;
   MPI_Type_vector(
      /* count = */ A.numCols(),
      /* blocklength = */ 1,
      /* stride = */ A.incCol(),
      /* element type = */ get_type(A(0, 0)),
      /* newly created type = */ &rowtype);

   /* in case of row-major we are finished */
   if (A.incRow() == A.numCols()) {
      MPI_Type_commit(&rowtype);
      return rowtype;
   }

   /* the extent of the MPI data type does not match
      the offset of subsequent rows -- this is a problem
      whenever we want to handle more than one row;
      to fix this we need to use the resize function
      which allows us to adapt the extent to A.incRow() */
   MPI_Datatype resized_rowtype;
   MPI_Type_create_resized(rowtype, 0, /* lb remains unchanged */
      A.incRow() * sizeof(T), &resized_rowtype);
   MPI_Type_commit(&resized_rowtype);
   MPI_Type_free(&rowtype);
   return resized_rowtype;
}

/* create MPI data type for matrix A */
template<typename T, template<typename> class Matrix,
   Require<Ge<Matrix<T>>> = true>
MPI_Datatype get_type(const Matrix<T>& A) {
   MPI_Datatype datatype;
   if (A.incCol() == 1) {
      MPI_Type_vector(
	 /* count = */ A.numRows(),
	 /* blocklength = */ A.numCols(),
	 /* stride = */ A.incRow(),
	 /* element type = */ get_type(A(0, 0)),
	 /* newly created type = */ &datatype);
   } else {
      /* vector of row vectors */
      MPI_Datatype rowtype = get_row_type(A);
      MPI_Type_contiguous(A.numRows(), rowtype, &datatype);
      MPI_Type_free(&rowtype);
   }
   MPI_Type_commit(&datatype);
   return datatype;
}

} } // namespaces mpi, hpc

#endif // HPC_MPI_MATRIX_H
theon$ mpic++ -g -std=c++17 -Istep02 -I/home/numerik/pub/pp/ss19/lib -o transfer_matrices transfer_matrices.cpp
theon$ mpirun -np 2 transfer_matrices
true extent of A = 168
extent of A = 168
size = 168
theon$ 
heim$ OMPI_CXX=g++-8.3 mpic++ -g -std=c++17 -Istep02 -I/home/numerik/pub/pp/ss19/lib -o transfer_matrices transfer_matrices.cpp -Wno-literal-suffix
/usr/local/libexec/gcc/x86_64-pc-linux-gnu/8.3.0/cc1plus: error while loading shared libraries: libmpfr.so.4: cannot open shared object file: No such file or directory
heim$ mpirun -np 2 transfer_matrices
--------------------------------------------------------------------------
Open MPI tried to fork a new process via the "execve" system call but
failed.  Open MPI checks many things before attempting to launch a
child process, but nothing is perfect. This error may be indicative
of another problem on the target host, or even something as silly as
having specified a directory for your application. Your job will now
abort.

  Local host:        heim
  Working dir:       /home/numerik/pp/ss19/sessions/session06
  Application name:  /home/borchert/pp/ss19/sessions/session06/transfer_matrices
  Error:             No such file or directory
--------------------------------------------------------------------------
--------------------------------------------------------------------------
mpirun was unable to start the specified application as it encountered an
error:

Error code: -125
Error name: The specified application failed to start
Node: heim

when attempting to start process rank 0.
--------------------------------------------------------------------------
[heim:02353] 1 more process has sent help message help-orte-odls-default.txt / execve error
[heim:02353] Set MCA parameter "orte_base_help_aggregate" to 0 to see all help / error messages
2 total processes failed to start
heim$