The Frobenius norm is matrix norm of a matrix defined as the square root of the sum of the absolute squares of its elements.
By definitions (see reference manual) of the skeletons provided in the library, a program written in Haskell which calculates the frobenius norm of a matrix is as follows:
fnorm = reduce ((+),(+)) . map square
Thus, we can get a parallel program by just translating it into C++ code with the Library.
C++ code for The Frobenius Norm is listed below:
double fnorm(const matrix<double> &mat){ dist_matrix<double> dmat(&mat); dist_matrix<double> *dmat2 = matrix_skeletons::map(Square<double>(), &dmat); double *ss = matrix_skeletons::reduce(Add<double>(), Add<double>(), dmat2); double ret = sqrt(*ss); delete ss; delete dmat2; return ret; }
This code is a part of a source file "fnorm.cpp" in samples directory.
An explanation for the code is as follows.
First, we distribute the matrix mat among processors by the constructor of dist_matrix class:
dist_matrix<double> dmat(&mat);
Then, applying the map skeleton to the distributed matrix dmat with a function object, we can get another distributed matrix *dmat2 as the intermediate data. An element of the distributed matrix *dmat2 is a square of corresponding element of the matrix dmat. The map skeleton is provided as a static member function of matrix_skeletons class. Square<double>() is a square function on double and provided in primitive_functions.h. In this case, the return value of the skeleton map is a pointer of a object of dist_matrix (an distributed matrix object).
dist_matrix<double> *dmat2 = matrix_skeletons::map(Square<double>(), &dmat);
NOTE: Function objects passed to the skeletons should inherit suitable base classes of function objects such as binary_function defined in "/library/util/functions.h"
Applying the reduce skeleton to the intermediate data *dmat2, we can get a sum of squares of all elements in the matrix mat. Add<double>() is a function object of an addition operator on double.
double *ss = matrix_skeletons::reduce(Add<double>(), Add<double>(), dmat2);
Finally, we free the pointers and return the square root of the sum.
double ret = sqrt(*ss); delete ss; delete dmat2; return ret;
A complete code with a sample main function is listed below("samples/matrix/fnorm.cpp"):
#include <cstdlib> #include <iostream> #include <cmath> #include "matrix_skeletons.h" #include "primitive_functions.h" using namespace primitive_functions; double fnorm(const matrix<double> &mat){ dist_matrix<double> dmat(&mat); dist_matrix<double> *dmat2 = matrix_skeletons::map(Square<double>(), &dmat); double *ss = matrix_skeletons::reduce(Add<double>(), Add<double>(), dmat2); double ret = sqrt(*ss); delete ss; delete dmat2; return ret; } int SketoMain( int argc, char **argv ) { int n = 1000; if(argc > 1){ n = atoi(argv[1]); } matrix<double> mat(n, n); mat.generate(GenSub<double>()); double norm = fnorm(mat); if(skeleton::rank==0){ std::cout << "#procs = "<< skeleton::procs << endl; std::cout << "matrix size = " << n << "x" << n << endl; std::cout << "The Frobenius Norm = " << norm << std::endl; std::cout << "the matrix = " << endl; std::cout<< mat<<endl; } return 0; }
This code includes "matrix_skeletons.h" for matrix skeletons and "primitive_functions.h" for function objects. The program will begin at the function "SketoMain" instead of the "main" function(the ordinary entry point). All the programs using our skeleton library must have "SketoMain" functions as their entry points.
Compilation is simply done by the following command:
(You should specify a suitable path to the skeleton library in path_to_skeleton_library and change the current directory to 'samples/matrix/')
> mpiCC -Wall -O2 -o fnorm fnorm.cpp
NOTE:The name of C++ compiler with MPI "mpiCC" may differ in some environments.
If you don't have MPI's C++ compiler "mpiCC" (or C++ bindings for MPI), you may compile the program by passing some extra arguments to an ordinary C++ compiler such as g++:
> g++ -Wall -O2 -Ipath_to_skeleton_library -Ipath_to_mpi_include_files -Lpath_to_mpi_libraries -o fnorm fnorm.cpp -lmpich
NOTE:You can also compile the source code by just using make (GNU make) command if you have successfully generated 'base makefile' during the installation.
> make -f ../../makefile.base fnorm
Other samples and tests in the directory 'samples/matrix/' can be compiled in the same way (or just type 'make' in the directory).
Then, we can execute the program by the following command:
> mpirun -np n fnorm
Here n is the number of processors involved in the execution.
NOTE:The executer of MPI programs "mpirun" may differ in some environments.