From ae2243232a25c08529c81f4126fa2623d5787793 Mon Sep 17 00:00:00 2001 From: Pratik Nayak Date: Mon, 25 Oct 2021 17:27:01 +0200 Subject: [PATCH] Add an example as an integration test --- examples/CMakeLists.txt | 1 + examples/mpi-simple-solver/CMakeLists.txt | 17 ++ examples/mpi-simple-solver/build.sh | 16 ++ examples/mpi-simple-solver/data/A.mtx | 114 +++++++++++ examples/mpi-simple-solver/data/b.mtx | 21 ++ examples/mpi-simple-solver/data/x0.mtx | 21 ++ examples/mpi-simple-solver/doc/builds-on | 1 + examples/mpi-simple-solver/doc/intro.dox | 19 ++ examples/mpi-simple-solver/doc/kind | 1 + examples/mpi-simple-solver/doc/results.dox | 35 ++++ examples/mpi-simple-solver/doc/short-intro | 1 + examples/mpi-simple-solver/doc/tooltip | 1 + .../mpi-simple-solver/mpi-simple-solver.cpp | 179 ++++++++++++++++++ 13 files changed, 427 insertions(+) create mode 100644 examples/mpi-simple-solver/CMakeLists.txt create mode 100644 examples/mpi-simple-solver/build.sh create mode 100644 examples/mpi-simple-solver/data/A.mtx create mode 100644 examples/mpi-simple-solver/data/b.mtx create mode 100644 examples/mpi-simple-solver/data/x0.mtx create mode 100644 examples/mpi-simple-solver/doc/builds-on create mode 100644 examples/mpi-simple-solver/doc/intro.dox create mode 100644 examples/mpi-simple-solver/doc/kind create mode 100644 examples/mpi-simple-solver/doc/results.dox create mode 100644 examples/mpi-simple-solver/doc/short-intro create mode 100644 examples/mpi-simple-solver/doc/tooltip create mode 100644 examples/mpi-simple-solver/mpi-simple-solver.cpp diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 151430846e1..fc5562aaac5 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -23,6 +23,7 @@ set(EXAMPLES_LIST ginkgo-overhead minimal-cuda-solver mixed-spmv + mpi-simple-solver mixed-multigrid-solver multigrid-preconditioned-solver par-ilu-convergence diff --git a/examples/mpi-simple-solver/CMakeLists.txt b/examples/mpi-simple-solver/CMakeLists.txt new file mode 100644 index 00000000000..7f146a1972c --- /dev/null +++ b/examples/mpi-simple-solver/CMakeLists.txt @@ -0,0 +1,17 @@ +cmake_minimum_required(VERSION 3.9) +project(mpi-simple-solver) + +# We only need to find Ginkgo if we build this example stand-alone +if (NOT GINKGO_BUILD_EXAMPLES) + find_package(Ginkgo 1.5.0 REQUIRED) + find_package(MPI REQUIRED) +endif() + +add_executable(mpi-simple-solver mpi-simple-solver.cpp) +target_link_libraries(mpi-simple-solver Ginkgo::ginkgo) +target_link_libraries(mpi-simple-solver MPI::MPI_CXX) + +# Copy the data files to the execution directory +configure_file(data/A.mtx data/A.mtx COPYONLY) +configure_file(data/b.mtx data/b.mtx COPYONLY) +configure_file(data/x0.mtx data/x0.mtx COPYONLY) diff --git a/examples/mpi-simple-solver/build.sh b/examples/mpi-simple-solver/build.sh new file mode 100644 index 00000000000..8b025d647b5 --- /dev/null +++ b/examples/mpi-simple-solver/build.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +# set up script +if [ $# -ne 1 ]; then + echo -e "Usage: $0 GINKGO_BUILD_DIRECTORY" + exit 1 +fi +BUILD_DIR=$1 +THIS_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" &>/dev/null && pwd ) + +source ${THIS_DIR}/../build-setup.sh + +# build +${CXX} -std=c++14 -o ${THIS_DIR}/mpi-simple-solver ${THIS_DIR}/mpi-simple-solver.cpp \ + -I${THIS_DIR}/../../include -I${BUILD_DIR}/include \ + -L${THIS_DIR} ${LINK_FLAGS} diff --git a/examples/mpi-simple-solver/data/A.mtx b/examples/mpi-simple-solver/data/A.mtx new file mode 100644 index 00000000000..c67437da567 --- /dev/null +++ b/examples/mpi-simple-solver/data/A.mtx @@ -0,0 +1,114 @@ +%%MatrixMarket matrix coordinate integer symmetric +%------------------------------------------------------------------------------- +% UF Sparse Matrix Collection, Tim Davis +% http://www.cise.ufl.edu/research/sparse/matrices/JGD_Trefethen/Trefethen_20b +% name: JGD_Trefethen/Trefethen_20b +% [Diagonal matrices with primes, Nick Trefethen, Oxford Univ.] +% id: 2203 +% date: 2008 +% author: N. Trefethen +% ed: J.-G. Dumas +% fields: name title A id date author ed kind notes +% kind: combinatorial problem +%------------------------------------------------------------------------------- +% notes: +% Diagonal matrices with primes, Nick Trefethen, Oxford Univ. +% From Jean-Guillaume Dumas' Sparse Integer Matrix Collection, +% http://ljk.imag.fr/membres/Jean-Guillaume.Dumas/simc.html +% +% Problem 7 of the Hundred-dollar, Hundred-digit Challenge Problems, +% SIAM News, vol 35, no. 1. +% +% 7. Let A be the 20,000 x 20,000 matrix whose entries are zero +% everywhere except for the primes 2, 3, 5, 7, . . . , 224737 along the +% main diagonal and the number 1 in all the positions A(i,j) with +% |i-j| = 1,2,4,8, . . . ,16384. What is the (1,1) entry of inv(A)? +% +% http://www.siam.org/news/news.php?id=388 +% +% Filename in JGD collection: Trefethen/trefethen_20__19_minor.sms +%------------------------------------------------------------------------------- +19 19 83 +1 1 3 +2 1 1 +3 1 1 +5 1 1 +9 1 1 +17 1 1 +2 2 5 +3 2 1 +4 2 1 +6 2 1 +10 2 1 +18 2 1 +3 3 7 +4 3 1 +5 3 1 +7 3 1 +11 3 1 +19 3 1 +4 4 11 +5 4 1 +6 4 1 +8 4 1 +12 4 1 +5 5 13 +6 5 1 +7 5 1 +9 5 1 +13 5 1 +6 6 17 +7 6 1 +8 6 1 +10 6 1 +14 6 1 +7 7 19 +8 7 1 +9 7 1 +11 7 1 +15 7 1 +8 8 23 +9 8 1 +10 8 1 +12 8 1 +16 8 1 +9 9 29 +10 9 1 +11 9 1 +13 9 1 +17 9 1 +10 10 31 +11 10 1 +12 10 1 +14 10 1 +18 10 1 +11 11 37 +12 11 1 +13 11 1 +15 11 1 +19 11 1 +12 12 41 +13 12 1 +14 12 1 +16 12 1 +13 13 43 +14 13 1 +15 13 1 +17 13 1 +14 14 47 +15 14 1 +16 14 1 +18 14 1 +15 15 53 +16 15 1 +17 15 1 +19 15 1 +16 16 59 +17 16 1 +18 16 1 +17 17 61 +18 17 1 +19 17 1 +18 18 67 +19 18 1 +19 19 71 diff --git a/examples/mpi-simple-solver/data/b.mtx b/examples/mpi-simple-solver/data/b.mtx new file mode 100644 index 00000000000..05d92ecc6f7 --- /dev/null +++ b/examples/mpi-simple-solver/data/b.mtx @@ -0,0 +1,21 @@ +%%MatrixMarket matrix array real general +19 1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 diff --git a/examples/mpi-simple-solver/data/x0.mtx b/examples/mpi-simple-solver/data/x0.mtx new file mode 100644 index 00000000000..91d470cdbcd --- /dev/null +++ b/examples/mpi-simple-solver/data/x0.mtx @@ -0,0 +1,21 @@ +%%MatrixMarket matrix array real general +19 1 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 diff --git a/examples/mpi-simple-solver/doc/builds-on b/examples/mpi-simple-solver/doc/builds-on new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/examples/mpi-simple-solver/doc/builds-on @@ -0,0 +1 @@ + diff --git a/examples/mpi-simple-solver/doc/intro.dox b/examples/mpi-simple-solver/doc/intro.dox new file mode 100644 index 00000000000..70bc1ce3cc7 --- /dev/null +++ b/examples/mpi-simple-solver/doc/intro.dox @@ -0,0 +1,19 @@ + +

Introduction

+This simple solver example should help you get started with Ginkgo. This example is meant for you to understand +how Ginkgo works and how you can solve a simple linear system with Ginkgo. We encourage you to play with the code, +change the parameters and see what is best suited for your purposes. + +

About the example

+Each example has the following sections: +
    +
  1. Introduction:This gives an overview of the example and mentions + any interesting aspects in the example that might help the reader. +
  2. The commented program: This section is intended for you to + understand the details of the example so that you can play with it and understand + Ginkgo and its features better. +
  3. Results: This section shows the results of the code when run. Though the + results may not be completely the same, you can expect the behaviour to be similar. +
  4. The plain program: This is the complete code without any comments to have + an complete overview of the code. +
diff --git a/examples/mpi-simple-solver/doc/kind b/examples/mpi-simple-solver/doc/kind new file mode 100644 index 00000000000..15a13db4511 --- /dev/null +++ b/examples/mpi-simple-solver/doc/kind @@ -0,0 +1 @@ +basic diff --git a/examples/mpi-simple-solver/doc/results.dox b/examples/mpi-simple-solver/doc/results.dox new file mode 100644 index 00000000000..69a4ef8211c --- /dev/null +++ b/examples/mpi-simple-solver/doc/results.dox @@ -0,0 +1,35 @@ +

Results

+The following is the expected result: + +@code{.cpp} + +Solution (x): +%%MatrixMarket matrix array real general +19 1 +0.252218 +0.108645 +0.0662811 +0.0630433 +0.0384088 +0.0396536 +0.0402648 +0.0338935 +0.0193098 +0.0234653 +0.0211499 +0.0196413 +0.0199151 +0.0181674 +0.0162722 +0.0150714 +0.0107016 +0.0121141 +0.0123025 +Residual norm sqrt(r^T r): +%%MatrixMarket matrix array real general +1 1 +2.10788e-15 + +@endcode + +

Comments about programming and debugging

diff --git a/examples/mpi-simple-solver/doc/short-intro b/examples/mpi-simple-solver/doc/short-intro new file mode 100644 index 00000000000..578df81366b --- /dev/null +++ b/examples/mpi-simple-solver/doc/short-intro @@ -0,0 +1 @@ +The simple solver example. diff --git a/examples/mpi-simple-solver/doc/tooltip b/examples/mpi-simple-solver/doc/tooltip new file mode 100644 index 00000000000..52c4f6d5f44 --- /dev/null +++ b/examples/mpi-simple-solver/doc/tooltip @@ -0,0 +1 @@ +Solve a simple linear system with CG. Read a matrix and right hand side from a file. diff --git a/examples/mpi-simple-solver/mpi-simple-solver.cpp b/examples/mpi-simple-solver/mpi-simple-solver.cpp new file mode 100644 index 00000000000..f150c37a96c --- /dev/null +++ b/examples/mpi-simple-solver/mpi-simple-solver.cpp @@ -0,0 +1,179 @@ +/************************************************************* +Copyright (c) 2017-2021, the Ginkgo authors +All rights reserved. + +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 copyright holder nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT +HOLDER OR 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. +*************************************************************/ + +// @sect3{Include files} + +// This is the main ginkgo header file. +#include + +// Add the fstream header to read from data from files. +#include +// Add the C++ iostream header to output information to the console. +#include +// Add the STL map header for the executor selection +#include +// Add the string manipulation header to handle strings. +#include + + +int main(int argc, char* argv[]) +{ + auto mpi_init_fin = gko::mpi::init_finalize(argc, argv); + { + auto mpi_init_fin2 = gko::mpi::init_finalize(argc, argv); + + // Use some shortcuts. In Ginkgo, vectors are seen as a + // gko::matrix::Dense with one column/one row. The advantage of this + // concept is that using multiple vectors is a now a natural extension + // of adding columns/rows are necessary. + using ValueType = double; + using RealValueType = gko::remove_complex; + using IndexType = int; + using vec = gko::matrix::Dense; + using real_vec = gko::matrix::Dense; + // The gko::matrix::Csr class is used here, but any other matrix class + // such as gko::matrix::Coo, gko::matrix::Hybrid, gko::matrix::Ell or + // gko::matrix::Sellp could also be used. + using mtx = gko::matrix::Csr; + // The gko::solver::Cg is used here, but any other solver class can also + // be used. + using cg = gko::solver::Cg; + + // Print the ginkgo version information. + std::cout << gko::version_info::get() << std::endl; + + if (argc == 2 && (std::string(argv[1]) == "--help")) { + std::cerr << "Usage: " << argv[0] << " [executor] " << std::endl; + std::exit(-1); + } + + // @sect3{Where do you want to run your solver ?} + // The gko::Executor class is one of the cornerstones of Ginkgo. + // Currently, we have support for an gko::OmpExecutor, which uses OpenMP + // multi-threading in most of its kernels, a gko::ReferenceExecutor, a + // single threaded specialization of the OpenMP executor and a + // gko::CudaExecutor which runs the code on a NVIDIA GPU if available. + // @note With the help of C++, you see that you only ever need to change + // the executor and all the other functions/ routines within Ginkgo + // should automatically work and run on the executor with any other + // changes. + const auto executor_string = argc >= 2 ? argv[1] : "reference"; + std::map()>> + exec_map{ + {"omp", [] { return gko::OmpExecutor::create(); }}, + {"cuda", + [] { + return gko::CudaExecutor::create( + 0, gko::OmpExecutor::create(), true); + }}, + {"hip", + [] { + return gko::HipExecutor::create( + 0, gko::OmpExecutor::create(), true); + }}, + {"dpcpp", + [] { + return gko::DpcppExecutor::create( + 0, gko::OmpExecutor::create()); + }}, + {"reference", [] { return gko::ReferenceExecutor::create(); }}}; + + // executor where Ginkgo will perform the computation + const auto exec = + exec_map.at(executor_string)(); // throws if not valid + + // @sect3{Reading your data and transfer to the proper device.} + // Read the matrix, right hand side and the initial solution using the + // @ref read function. + // @note Ginkgo uses C++ smart pointers to automatically manage memory. + // To this end, we use our own object ownership transfer functions that + // under the hood call the required smart pointer functions to manage + // object ownership. The gko::share , gko::give and gko::lend are the + // functions that you would need to use. + auto A = share(gko::read(std::ifstream("data/A.mtx"), exec)); + auto b = gko::read(std::ifstream("data/b.mtx"), exec); + auto x = gko::read(std::ifstream("data/x0.mtx"), exec); + + // @sect3{Creating the solver} + // Generate the gko::solver factory. Ginkgo uses the concept of + // Factories to build solvers with certain properties. Observe the + // Fluent interface used here. Here a cg solver is generated with a + // stopping criteria of maximum iterations of 20 and a residual norm + // reduction of 1e-7. You also observe that the stopping + // criteria(gko::stop) are also generated from factories using their + // build methods. You need to specify the executors which each of the + // object needs to be built on. + const RealValueType reduction_factor{1e-7}; + auto solver_gen = + cg::build() + .with_criteria( + gko::stop::Iteration::build().with_max_iters(20u).on(exec), + gko::stop::ResidualNorm::build() + .with_reduction_factor(reduction_factor) + .on(exec)) + .on(exec); + // Generate the solver from the matrix. The solver factory built in the + // previous step takes a "matrix"(a gko::LinOp to be more general) as an + // input. In this case we provide it with a full matrix that we + // previously read, but as the solver only effectively uses the apply() + // method within the provided "matrix" object, you can effectively + // create a gko::LinOp class with your own apply implementation to + // accomplish more tasks. We will see an example of how this can be done + // in the custom-matrix-format example + auto solver = solver_gen->generate(A); + + // Finally, solve the system. The solver, being a gko::LinOp, can be + // applied to a right hand side, b to obtain the solution, x. + solver->apply(lend(b), lend(x)); + + // Print the solution to the command line. + std::cout << "Solution (x):\n"; + write(std::cout, lend(x)); + + // To measure if your solution has actually converged, you can measure + // the error of the solution. one, neg_one are objects that represent + // the numbers which allow for a uniform interface when computing on any + // device. To compute the residual, all you need to do is call the apply + // method, which in this case is an spmv and equivalent to the LAPACK + // z_spmv routine. Finally, you compute the euclidean 2-norm with the + // compute_norm2 function. + auto one = gko::initialize({1.0}, exec); + auto neg_one = gko::initialize({-1.0}, exec); + auto res = gko::initialize({0.0}, exec); + A->apply(lend(one), lend(x), lend(neg_one), lend(b)); + b->compute_norm2(lend(res)); + + std::cout << "Residual norm sqrt(r^T r):\n"; + write(std::cout, lend(res)); + } + gko::mpi::synchronize(); +}