/* sparse_linsolve.c * * Uses iterative methods in PETSc to solve a random * sparse linear system Ax = b. * * Input: * n: order of the linear system * diagonal: value of diagonal entries in matrix * prob: probability that an off-diagonal entry * is nonzero * initial_dist: the initial distribution of the * coefficient matrix: 0 indicates that * the entire matrix initially resides on * process 0, 1 indicates that it will be * distributed among the processes. * * Output: * Information from PETSc: solver, tolerances, * type of preconditioning, and storage * information for the coefficient matrix * error: 2-norm of error in solution * iterations: number of iterations before * solver terminated * * To compile: * See PETSc makefile. * * To run: * mpirun -np p linsolve [petsc options] * * Petsc Options: * Can specify solver and preconditioner. Options have * the form * -option_name option_value * Some valid option names and values follow. * 1. ksp_method: the Krylov Subspace method used * by the solver. Possible values: richardson, * chebychev, cg, gmres, tcqmr, bcgs, cgs, * tfqmr, cr, lsqr, preonly. * 2. ksp_rtol: decrease in residual norm relative * to size of initial residual for convergence. * Values are doubles. * 3. ksp_atol: absolute size of the residual norm * for convergence. Values are doubles. * 4. ksp_max_it: maximum number of iterations * before terminating solve. Values are ints. * 5. ksp_monitor: display residual norms. * (No value.) * 6. pc_method: the preconditioning method. * Some values are none, jacobi, bjacobi, sor, * icc, ilu, lu. (lu simply uses LU * factorization to solve the system. This is * only available when run with one process.) * 7. pc_sor_omega: the relaxation factor * for sor. Value is a double. * 8. pc_sor_its: the number of inner iterations * to use in the sor. Value is an int. * 9. pc_bjacobi_blocks: the number of blocks to * to use in block jacobi. Value is an int. * * Algorithm: * 1. Initialize MPI and PETSc. * 2. Build derived datatype for input data. * 3a. Process 0 read and broadcast input data. * 3b. Processes != 0 receive input data. * 4. if (initial_dist == 0) * process 0 initializes the matrix * else * each process initializes its rows * 5. All processes begin nonblocking distribution of * coefficient matrix using MatAssemblyBegin. * 6. Use VecCreate and VecDuplicate to create * vectors b, x, and exact. * 7. Set entries in exact to 1 using VecSet. * 8. Complete distribution of matrix with * MatAssemblyEnd. * 9. Use MatMult to set rhs b = A*exact. * 10. Create solver context using SLESCreate. * 11. Associate matrix and preconditioning matrix * with solver using SLESSetOperators. * 12. Get command line options specifying solver * options such as type of solver and type of * preconditioning. Use SLESSetFromOptions. * 13 Solve the system with SLESSolve. * 14. Print information on solver and matrix * using SLESView. * 15. Compute norm of error, ||x - exact||_2, using * VecAXPY and VecNorm. * 16. Print error norm and number of iterations * using MPIU_printf. * 17. Free storage used by PETSc. * 18. Shut down PETSc and MPI. * * Notes: * 1. Our PETSc matrices and vectors are distributed * by block panels. * 2. It is not necessary to initialize A on process 0: * any entries can be inserted on any process; * MatAssemblyBegin/End will correctly distribute * them among the processes. * 3. Solver context consists of such things as a * communicator for solver communication and data * structures needed for solver operations such * as matrix-vector multiply. * 4. PETSc provides an extensive error-handling and * traceback facility which we have not illustrated * in this example to make source more readable. * * See Chap 15, pp. 350 & ff, in PPMPI. */ #include #include /* Needed for drand48 and srand48 */ #include "sles.h" /* Includes headers needed by PETSc */ #include "mpi.h" /* Required in all PETSc programs */ static char help[] = "Solve a random sparse linear system"; void Get_input(int my_rank, int* n_ptr, double* diagonal_ptr, double* prob_ptr, int* initial_dist_ptr); void Build_input_datatype(MPI_Datatype* input_datatype_ptr, int* n_ptr, double* diagonal_ptr, double* prob_ptr, int* initial_dist_ptr); void Initialize_matrix(int my_rank, Mat A, int n, double diagonal, double prob, int initial_dist); void Allocate(int my_rank, char* name, void* list, int size, int datatype); /*======================================================*/ int main(int argc,char **argv) { Vec x; /* computed solution */ Vec b; /* right-hand side */ Vec exact; /* exact solution */ int p; int my_rank; int n; /* order of system */ double diagonal; /* diagonal matrix entries */ double prob; /* probability of an off- */ /* diagonal non-zero */ int initial_dist; /* =0 matrix initialized */ /* on process 0. =1 dist- */ /* tributed initialization */ Mat A; /* coefficient matrix */ double one = 1.0; double minus_one = -1.0; SLES sles; /* context for solver */ int iterations; /* number of iterations */ /* used by solver */ double error; /* 2-norm of error in sol- */ /* ution */ MPI_Init(&argc, &argv); PetscInitialize(&argc,&argv,0,0,help); MPI_Comm_rank(MPI_COMM_WORLD,&my_rank); MPI_Comm_size(MPI_COMM_WORLD,&p); Get_input(my_rank, &n, &diagonal, &prob, &initial_dist); MatCreate(MPI_COMM_WORLD, n, n, &A); Initialize_matrix(my_rank, A, n, diagonal, prob, initial_dist); MatAssemblyBegin(A, FINAL_ASSEMBLY); /* Create and set vectors */ VecCreate(MPI_COMM_WORLD,n,&x); VecDuplicate(x, &exact); VecDuplicate(x, &b); VecSet(&one, exact); MatAssemblyEnd(A, FINAL_ASSEMBLY); MatMult(A, exact, b); /* Set up solver context */ SLESCreate(MPI_COMM_WORLD, &sles); /* Identify coefficient matrix and preconditioning */ /* matrix */ SLESSetOperators(sles, A, A, 0); /* Use solver and preconditioner specified */ /* by command line options */ SLESSetFromOptions(sles); /* Now solve the system */ SLESSolve(sles, b, x, &iterations); #ifdef DEBUG VecView(x, STDOUT_VIEWER_WORLD); MatView(A, STDOUT_VIEWER_WORLD); #endif SLESView(sles, STDOUT_VIEWER_WORLD); /* Check solution */ VecAXPY(&minus_one, exact, x); VecNorm(x, NORM_2, &error); if (error >= 1.0e-12) MPIU_printf(MPI_COMM_WORLD, "Norm of error %g, Iterations %d\n", error, iterations); else MPIU_printf(MPI_COMM_WORLD, "Norm of error < 1.0e-12, Iterations %d\n", iterations); /* Pause before quitting */ { int anything; if (my_rank == 0) { printf("Enter a number to continue\n"); scanf("%d",&anything); } MPI_Bcast(&anything, 1, MPI_INT, 0, MPI_COMM_WORLD); printf("Process %d > anything = %d\n", my_rank, anything); } /* Free work space */ VecDestroy(x); VecDestroy(exact); VecDestroy(b); MatDestroy(A); SLESDestroy(sles); PetscFinalize(); MPI_Finalize(); } /* main */ /*======================================================== * * Process 0 read and broadcast input data */ void Get_input(int my_rank, int* n_ptr, double* diagonal_ptr, double* prob_ptr, int* initial_dist_ptr) { MPI_Datatype input_datatype; Build_input_datatype(&input_datatype, n_ptr, diagonal_ptr, prob_ptr, initial_dist_ptr); if (my_rank == 0) scanf("%d %lf %lf %d", n_ptr, diagonal_ptr, prob_ptr, initial_dist_ptr); MPI_Bcast(n_ptr, 1, input_datatype, 0, MPI_COMM_WORLD); } /* Get_input */ /*======================================================== * * Build derived datatype for distributing input data */ void Build_input_datatype(MPI_Datatype* input_datatype_ptr, int* n_ptr, double* diagonal_ptr, double* prob_ptr, int* initial_dist_ptr){ int array_of_block_lengths[4]; MPI_Aint array_of_displacements[4]; MPI_Datatype array_of_types[4]; MPI_Aint base_address; MPI_Aint temp_address; int i; for (i = 0; i < 4; i++) { array_of_block_lengths[i] = 1; } array_of_types[0] = MPI_INT; array_of_types[1] = MPI_DOUBLE; array_of_types[2] = MPI_DOUBLE; array_of_types[3] = MPI_INT; /* Compute displacements from n */ array_of_displacements[0] = 0; MPI_Address(n_ptr, &base_address); MPI_Address(diagonal_ptr, &temp_address); array_of_displacements[1] = temp_address - base_address; MPI_Address(prob_ptr, &temp_address); array_of_displacements[2] = temp_address - base_address; MPI_Address(initial_dist_ptr, &temp_address); array_of_displacements[3] = temp_address - base_address; MPI_Type_struct(4, array_of_block_lengths, array_of_displacements, array_of_types, input_datatype_ptr); MPI_Type_commit(input_datatype_ptr); } /* Build_input_datatype */ /*======================================================== * * Assign values to matrix entries: Diagonal entries * get value in diagonal. Off-diagonals get zero * or a random value in the range (-1,1). * initial_dist = 0: entire matrix initialized by * process 0. * initial_dist = 1: each processes initializes its * rows. */ void Initialize_matrix(int my_rank, Mat A, int n, double diagonal, double prob, int initial_dist) { int* columns; /* temporary storage for col */ double* temp_row; /* indices and row entries */ int nonzero_count; int my_min_row; int my_max_row; int i, j; if (initial_dist == 0) { if (my_rank == 0) { my_min_row = 0; my_max_row = n; } else { return; } } else { MatGetOwnershipRange(A, &my_min_row, &my_max_row); } printf("Process %d: my_min_row = %d, my_max_row = %d\n", my_rank, my_min_row, my_max_row); fflush(stdout); /* Allocate temporary storage */ Allocate(my_rank, "columns", &columns, n, 0); Allocate(my_rank, "temp_row", &temp_row, n, 1); /* Seed random number generator */ srand48((long) (my_rank*my_rank)); for (i = my_min_row; i < my_max_row; i++) { nonzero_count = 0; for (j = 0; j < n; j++) { if (i == j) { temp_row[nonzero_count] = diagonal; columns[nonzero_count] = j; nonzero_count++; } else if (drand48() <= prob) { temp_row[nonzero_count] = 2.0*drand48()-1.0; columns[nonzero_count] = j; nonzero_count++; } } /* Insert entries in a single row (row i) into */ /* the matrix */ MatSetValues(A, 1, &i, nonzero_count, columns, temp_row, INSERT_VALUES); } } /* Initialize_matrix */ /*======================================================== * * Allocate a list of ints or doubles. On error exit. * datatype = 0 => int, datatype = 1 => double. */ void Allocate(int my_rank, char* name, void* list, int size, int datatype) { int error = 0; if (datatype == 0) { *((int**)list) = (int*) malloc(size*sizeof(int)); if (*((int**)list) == (int*) NULL) error = 1; } else { *((double**)list) = (double*) malloc(size*sizeof(double)); if (*((double**)list) == (double*) NULL) error = 1; } if (error) { fprintf(stderr, "Process %d > Malloc failed for %s!\n", my_rank, name); fprintf(stderr, "Process %d > size = %d\n", my_rank, size); fprintf(stderr, "Process %d > Quitting.\n", my_rank); MPI_Abort(MPI_COMM_WORLD, -1); } } /* Allocate */