source: CIVL/examples/mpi/gaussJordan_elimination.c@ d5abab8

1.23 2.0 acw/focus-triggers main test-branch
Last change on this file since d5abab8 was 61f3044, checked in by Ziqing Luo <ziqing@…>, 11 years ago

gaussian elimination example

git-svn-id: svn://vsl.cis.udel.edu/civl/trunk@1862 fb995dde-84ed-4084-dfe6-e5aef3e2452c

  • Property mode set to 100644
File size: 12.8 KB
Line 
1/* FILE: gaussJordan_elimination.c A gaussian-jordan elimination
2 * solver that converts a given matrix to a reduce row echelon form
3 * matrix
4 * RUN : mpicc gaussJordan_elimination.c; mpiexec -n 4 ./a.out numRow numCol m[0][0], m[0][1] ...
5 * VERIFY : civl verify gaussianJordan_elimination.c
6 */
7#include <assert.h>
8#include <mpi.h>
9#include <stdio.h>
10#include <stdlib.h>
11#include <string.h>
12
13/* Message tag */
14#define PRINT 0
15/* Global parameters */
16#ifdef _CIVL
17$input int _NPROCS_UPPER_BOUND=3;
18$input int _NPROCS_LOWER_BOUND=1;
19$input int ROWB = 4; // upper bound of numRow
20$input int numRow; // number of rows in the matrix
21$assume 0 < numRow && numRow <= ROWB;
22$input int COLB = 2; // upper bound of numCol
23$input int numCol; // number of columns in the matrix
24$assume 0 < numCol && numCol <= COLB;
25$input long double data[numRow][numCol]; // input matrix
26long double oracle[numRow][numCol]; // results of sequential run
27#else
28int numRow; // number of rows in the matrix
29int numCol; // number of columns in the matrix, the right-most
30 // column is vector B
31#endif
32int localRow; // number of rows owned by the process
33int rank, nprocs;
34int first; // the global index of the first row in original
35 // matrix
36/* a Global Row Index -> Current Row Location table maps original
37 indices of rows to their current location in current matrix */
38int *loc;
39/* a Current Row Location -> Global Row Index table maps current
40 locations of current matrix to their original row indices */
41int *idx;
42
43/* Book keeping functions */
44/* Return the owner of the row given by the global index of it in
45 original matrix */
46#define OWNER(index) ((nprocs*(index+1)-1)/numRow)
47
48/* Returns the global index of the first row owned
49 * by the process with given rank */
50int firstForProc(int rank) {
51 return (rank*numRow)/nprocs;
52}
53
54/* Returns the number of rows the given process owns */
55int countForProc(int rank) {
56 int a = firstForProc(rank);
57 int b = firstForProc(rank + 1);
58
59 return b - a;
60}
61
62/* Locally print a row */
63void printRow(long double * row) {
64 for(int k=0; k < numCol; k++)
65 printf("%2.6Lf ", row[k]);
66 printf("\n");
67}
68
69/* Print the given matrix. Since each process needs to send their data
70 * to root process 0, this function is collective.
71 * Parameters:
72 * a: the (part of) matrix will be printed.
73 */
74void printSystem(long double * a) {
75 long double recvbuf[numCol];
76
77 // Every process follows the order of locations of rows to send their
78 // rows to process with rank 0
79 for(int i=0; i<numRow; i++)
80 if(OWNER(idx[i]) == rank && rank != 0)
81 MPI_Send(&a[(idx[i]-first)*numCol], numCol, MPI_LONG_DOUBLE, 0, i, MPI_COMM_WORLD);
82
83 if(rank == 0) {
84 for(int i=0; i<numRow; i++) {
85 if(OWNER(idx[i]) != 0) {
86 MPI_Recv(recvbuf, numCol, MPI_LONG_DOUBLE, MPI_ANY_SOURCE, i,
87 MPI_COMM_WORLD, MPI_STATUS_IGNORE);
88 printRow(recvbuf);
89#ifdef _CIVL
90 for(int j=0; j < numCol; j++) {
91 $assert(recvbuf[j] == oracle[i][j]) :
92 "Get %Lf while expecting %Lf at position [%d][%d]\n", recvbuf[j], oracle[i][j], i, j;
93 }
94#endif
95 }
96 else {
97 printRow(&a[(idx[i]-first)*numCol]);
98#ifdef _CIVL
99 for(int j=0; j < numCol; j++) {
100 $assert(a[(idx[i]-first)*numCol + j] == oracle[i][j]):
101 "Get %Lf while expecting %Lf at position [%d][%d]\n",
102 a[(idx[i]-first)*numCol + j], oracle[i][j], i, j;
103 }
104#endif
105 }
106 }
107 }
108}
109
110void specElimination(long double *a, int * rowLoc) {
111 long double denom; // a temporary variable will be used to
112 //divide other variables
113
114 for(int i=0; i < numRow; i++) {
115 int leadCol = numCol; // the column where leading 1 be in
116 int rowOfLeadCol = i; // the row where leadCol be in
117
118 /* step 1: Find out the leftmost nonzero column, interchange it with
119 the current iterated row. */
120 for(int j=i; j < numCol; j++) {
121 for(int k=i; k < numRow; k++) {
122 if(a[rowLoc[k]*numCol + j] != 0.0) {
123 leadCol = j;
124 rowOfLeadCol = k;
125 break;
126 }
127 }
128 if(leadCol < numCol)
129 break;
130 }
131 /* If there is no leading 1 in all unprocessed rows, elimination
132 terminates. */
133 if(leadCol == numCol)
134 return;
135 /* step 2: Reducing the leading number to one */
136 denom = a[rowLoc[rowOfLeadCol]*numCol + leadCol];
137 /* If the denominator is zero (or extremely nearing zero), do
138 * nothing. The reason is the denominator is the left-most
139 * nonzero element in all unprocessed rows, if it's zero, all
140 * numbers at that column in all unprocessed rows are zeros. For
141 * such a case, it's no need to do anything in this iteration.
142 */
143 if(denom != 0.0) {
144 for(int j=leadCol; j < numCol; j++) {
145 long double tmp = a[rowLoc[rowOfLeadCol]*numCol + j] / denom;
146
147 a[rowLoc[rowOfLeadCol]*numCol + j] = tmp;
148 }
149 }
150 if(rowOfLeadCol != i) {
151 int tmp;
152
153 tmp = rowLoc[i];
154 rowLoc[i] = rowLoc[rowOfLeadCol];
155 rowLoc[rowOfLeadCol] = tmp;
156 }
157 /* step 3: Add a suitable value to each row below row i so that they have zero at column i */
158 for(int j=i+1; j < numRow; j++) {
159 long double factor = -a[rowLoc[j]*numCol + leadCol];
160
161 for(int k=leadCol; k < numCol; k++)
162 a[rowLoc[j]*numCol + k] += factor * a[rowLoc[i]*numCol + k];
163 }
164 }
165}
166
167/* Working upward to make each leading one the only nonzero number in
168 * which column it be .
169 * Parameters:
170 * a: the matrix in a row echelon form.
171 * rowLoc: a look-up table for rows' locations
172 */
173void specReduce(long double * a, int * rowLoc) {
174 int leadCol; // the column of the leading one in a row
175 int i;
176
177 i = (numRow > (numCol - 1))?(numCol-2):(numRow-1);
178 for(; i>=0; i--) {
179 //Find the leading 1, but if it's an all-zero row, skip it.
180 leadCol = -1;
181 for(int j=i; j<numCol; j++) {
182 if(a[rowLoc[i]*numCol + j] != 0.0) {
183 leadCol = j;
184 break;
185 }
186 }
187 // if it's not an all-zero row, reducing all other numbers in all
188 // rows above at the column at where the leading 1 be.
189 if(leadCol > -1) {
190 for(int j=i-1; j >=0; j--) {
191 long double factor = -a[rowLoc[j]*numCol + leadCol];
192
193 for(int k=leadCol; k < numCol; k++)
194 a[rowLoc[j]*numCol + k] += a[rowLoc[i]*numCol + k] * factor;
195 }
196 }
197 }
198}
199
200/* Initializing parameters and assigning input values to the original
201 * matrix.
202 * Parameter:
203 * argc: the first argument of main function
204 * argv: the second argument of main function
205 * a: the matrix owned by the process
206 * loc: the Global Row Index -> Current Row Location table
207 * idx: the Current Row Location -> Global Row Index table
208 */
209void initialization(int argc, char * argv[],
210 long double * a, int * loc, int * idx) {
211#ifdef _CIVL
212 long double spec[numRow*numCol];
213 int rowLoc[numRow];
214
215 for(int i=first; i<first+localRow; i++)
216 for(int j=0; j<numCol; j++) {
217 a[(i-first)*numCol + j] = data[i][j];
218 }
219 //sequential run
220 if(rank == 0) {
221 for(int i=0; i<numRow; i++){
222 rowLoc[i] = i;
223 memcpy(&spec[i*numCol], &data[i][0], numCol * sizeof(long double));
224 }
225 specElimination(spec, rowLoc);
226 specReduce(spec, rowLoc);
227 for(int i=0; i<numRow; i++){
228 for(int j=0; j<numCol; j++)
229 oracle[i][j] = spec[rowLoc[i]*numCol + j];
230 }
231 printf("oracle is :\n");
232 for(int i=0; i<numRow; i++)
233 printRow(&oracle[i][0]);
234 }
235#else
236 if((argc - 3) != numRow * numCol)
237 printf("Too few arguments.\n"
238 "Usage: mpiexec -n nprocs ./a.out n m A[0,0] A[0,1] ... A[n-1,m-1]\n"
239 " n : number of rows in matrix\n"
240 " m : number of columns in matrix\n"
241 " A[0,0] .. A[n-1,m-1] : entries of matrix (doubles)\n");
242 first = firstForProc(rank);
243 localRow = countForProc(rank);
244 //initializing matrix
245 for(int i=0; i<localRow; i++)
246 for(int j=0; j<numCol; j++)
247 sscanf(argv[(first+i)*numCol + j + 3], "%Lf", &a[i*numCol + j]);
248#endif
249 for(int i=0; i<numRow; i++){
250 loc[i] = i;
251 idx[i] = i;
252 }
253}
254
255/* Set row to location loca */
256void setLoc(int row, int loca){
257 int tmpLoc, tmpIdx;
258
259 tmpLoc = loc[row];
260 tmpIdx = idx[loca];
261 //swap locations(update index -> location table)
262 loc[row] = loca;
263 loc[tmpIdx] = tmpLoc;
264 //update location -> index table
265 idx[loca] = row;
266 idx[tmpLoc] = tmpIdx;
267}
268
269/* Performs a gaussian elimination on the given matrix, the output
270 * matrix will finally be in row echelon form .
271 */
272void gaussianElimination(long double *a) {
273 /* Buffer for the current toppest unprocessed row. */
274 long double top[numCol];
275
276 /* For each row of the matrix, it will be processed once. */
277 for(int i=0; i < numRow; i++) {
278 /* owner of the current unprocessed top row */
279 int owner = OWNER(idx[i]);
280 /* the column of the next leading 1, initial value is numCol
281 * because later it will pick up a minimum number.
282 */
283 int leadCol = numCol;
284 /* the global index of the row the next leading 1 will be in */
285 int rowOfLeadCol = -1;
286 int rowOfLeadColOwner; // the owner of rowOfLeadCol
287 /* message buffer: [0]:leadCol ;[1]:rowOfLeadCol */
288 int sendbuf[2];
289 /* receive buffer: it will contain lead 1 column candidates from
290 all processes */
291 int recvbuf[2*nprocs];
292 int tmp;
293
294 //step 1: find out the local leftmost nonzero column
295 for(int j=i; j < numCol; j++) {
296 int k;
297
298 for(k = first; k < first + localRow; k++) {
299 // only look at unprocessed rows
300 if(loc[k] >= i) {
301 if(a[(k-first)*numCol+j] != 0.0) {
302 leadCol = j;
303 rowOfLeadCol = k;
304 break;
305 }
306 }
307 }
308 if(leadCol < numCol)
309 break;
310 }
311 sendbuf[0] = leadCol;
312 sendbuf[1] = rowOfLeadCol;
313 /* All reduce the smallest column(left-most) of leading 1 to every
314 process */
315 MPI_Allreduce(sendbuf, recvbuf, 1, MPI_2INT, MPI_MINLOC, MPI_COMM_WORLD);
316 leadCol = recvbuf[0];
317 rowOfLeadCol = recvbuf[1];
318 /* Now the row containing next leading 1 is decided, findout the
319 owner of it. */
320 rowOfLeadColOwner = OWNER(rowOfLeadCol);
321 /* if leadCol is still initial value, it means there is no avaliable
322 column suitable for next leading 1. */
323 if(leadCol == numCol)
324 return;
325 // step 2: reducing the leading number to 1
326 if(rank == rowOfLeadColOwner) {
327 long double denom = a[(rowOfLeadCol - first)*numCol + leadCol];
328
329 if(denom != 0.0)
330 for(int j=leadCol; j < numCol; j++)
331 a[(rowOfLeadCol - first)*numCol + j] = a[(rowOfLeadCol - first)*numCol + j] / denom;
332 memcpy(top, &a[(rowOfLeadCol - first)*numCol
333 ], numCol*sizeof(long double));
334 }
335 MPI_Bcast(top, numCol, MPI_LONG_DOUBLE, rowOfLeadColOwner, MPI_COMM_WORLD);
336 /* swap the row containing next leading 1 to the top location of
337 current submatrix */
338 if(loc[rowOfLeadCol] != i)
339 setLoc(rowOfLeadCol, i);
340 /* step 3: add a suitable value to all unprocessed rows to make
341 all numbers at the same column as leading 1 zeros. */
342 for(int j=0; j < localRow; j++) {
343 if(loc[j+first] > i){
344 long double factor = -a[j*numCol + leadCol];
345
346 for(int k=leadCol; k < numCol; k++) {
347 a[j*numCol + k] += factor * top[k];
348 }
349 }
350 }
351 }
352}
353
354/* Perform a backward reduction on the given matrix which transforms a
355 row echelon form to a reduced row echelon form */
356void backwardReduce(long double *a) {
357 int leadCol;
358 int owner;
359 int i;
360 long double top[numCol];
361
362 i = (numRow > (numCol - 1))?(numCol-2):numRow-1;
363 for(; i>=1; i--) {
364 leadCol = -1;
365 owner = OWNER(idx[i]);
366 if(rank == owner)
367 memcpy(top, &a[(idx[i] - first)*numCol + i], (numCol-i)*sizeof(long double));
368 MPI_Bcast(top, (numCol-i), MPI_LONG_DOUBLE, owner, MPI_COMM_WORLD);
369 //find out the leading 1 column
370 for(int j=0; j<(numCol-i); j++){
371 if(top[j] != 0.0){
372 leadCol = j+i;
373 break;
374 }
375 }
376 if(leadCol == -1)
377 continue;
378 else {
379 for(int j=first; j<first+localRow; j++){
380 if(loc[j] < i){
381 long double factor = -a[(j-first)*numCol + leadCol];
382
383 for(int k=leadCol; k<numCol; k++)
384 a[(j-first)*numCol + k] += factor*top[k-i];
385 }
386 }
387 }
388 }
389}
390
391int main(int argc, char *argv[]) {
392 long double *a;
393
394#ifndef _CIVL
395 if(argc < 3)
396 printf("Expecting the arguments: numberOfRows numberOfColumns\n");
397 numRow = atoi(argv[1]);
398 numCol = atoi(argv[2]);
399#else
400 elaborate(numRow);
401 elaborate(numCol);
402#endif
403 MPI_Init(&argc, &argv);
404 MPI_Comm_rank(MPI_COMM_WORLD, &rank);
405 MPI_Comm_size(MPI_COMM_WORLD, &nprocs);
406 first = firstForProc(rank);
407 localRow = countForProc(rank);
408 a = (long double*)malloc(numCol*localRow*sizeof(long double));
409 loc = (int*)malloc(numRow*sizeof(int));
410 idx = (int*)malloc(numRow*sizeof(int));
411 initialization(argc, argv, a, loc, idx);
412 gaussianElimination(a);
413 backwardReduce(a);
414 if(!rank)printf("After backward reduction, the matrix in reduced row echelon form is:\n");
415 printSystem(a);
416 MPI_Finalize();
417 free(loc);
418 free(idx);
419 free(a);
420 return 0;
421}
422
Note: See TracBrowser for help on using the repository browser.