// return true if it converges. Output: solution matrix, errors, loops it took public static Boolean solve(Matrix A, Matrix b, out Matrix x, out Matrix err, out int loops, Intracommunicator comm) { // check sanity. rank 0 only if (comm.Rank == 0 && (!A.isSquare || !b.isColumn || (A.Height != b.Height))) { Exception e = new Exception("Matrix A must be square! Matrix b must be a column matrix with the same height as matrix A!"); throw e; } // follow samples in Wikipedia step by step https://en.wikipedia.org/wiki/Gauss%E2%80%93Seidel_method benchmark bm = new benchmark(), bm2 = new benchmark(), bm3 = new benchmark(); double sequential = 0, parallel = 0, communication = 0; bm.start(); bm2.start(); // decompose A into the sum of a lower triangular component L* and a strict upper triangular component U int size = 0; Matrix L = null, U = null, L_1; if (comm.Rank == 0) { size = A.Height; Matrix.Decompose(A, out L, out U); } bm2.pause(); sequential += bm2.getElapsedSeconds(); bm2.start(); comm.Broadcast(ref size, 0); comm.Broadcast(ref U, 0); comm.Broadcast(ref b, 0); bm2.pause(); communication += bm2.getElapsedSeconds(); // Inverse matrix L* comm.Barrier(); L_1 = MatrixParallel.Inverse(L, comm, ref sequential, ref parallel, ref communication); // Main iteration: x (at step k+1) = T * x (at step k) + C // where T = - (inverse of L*) * U, and C = (inverse of L*) * b // split T & C into groups of rows, each for one slave, according to the nature of this algorithm // each slave will have one piece of T & one piece of C stored locally. the rest of T & C is not needed // there might be cases where jobs > slaves, so some might get no job at all // Changes: only split L_1. Slaves will calculate T & C (pieces) themselves bm2.start(); Matrix jobDistro = Utils.splitJob(size, comm.Size); int startRow = 0, endRow = 0, myJobSize = (int)jobDistro[0, comm.Rank]; for (int p = 0; p < comm.Size; p++) { if (p != comm.Rank) { startRow += (int)jobDistro[0, p]; } else { endRow = startRow + (int)jobDistro[0, p] - 1; break; } } Matrix[] L_1Ps = new Matrix[comm.Size]; if (comm.Rank == 0) { int slaveStart = 0; for (int p = 0; p < comm.Size; p++) { L_1Ps[p] = Matrix.extractRows(L_1, slaveStart, slaveStart + (int)jobDistro[0, p] - 1); slaveStart += (int)jobDistro[0, p]; } } bm2.pause(); sequential += bm2.getElapsedSeconds(); bm2.start(); Matrix L_1P = comm.Scatter(L_1Ps, 0); bm2.pause(); communication += bm2.getElapsedSeconds(); bm2.start(); Matrix T = -L_1P * U; Matrix C = L_1P * b; bm2.pause(); parallel += bm2.getElapsedSeconds(); // the actual iteration // if it still doesn't converge after this many loops, assume it won't converge and give up Boolean converge = false; int loopLimit = 100; x = Matrix.zeroLike(b); // at step k for (loops = 0; loops < loopLimit; loops++) { bm3.start(); // (re-)distributing x vector. Must be done every single loop // this loop needs x from the previous loop comm.Broadcast(ref x, 0); bm3.pause(); communication += bm3.getElapsedSeconds(); // calculation step bm3.start(); comm.Barrier(); Matrix new_x = T * x + C; // check convergence converge = Matrix.SomeClose(new_x, x, 1e-15, startRow); // collect result x comm.Barrier(); x = comm.Reduce(new_x, Matrix.Concatenate, 0); // collect convergence. consider converged if ALL slaves claim so converge = comm.Reduce(converge, bothTrue, 0); comm.Broadcast(ref converge, 0); // make sure EVERYONE breaks/coninues bm3.pause(); parallel += bm3.getElapsedSeconds(); if (converge) { loops++; break; } } bm2.start(); // round the result slightly err = null; if (comm.Rank == 0) { x.Round(1e-14); err = A * x - b; err.Round(1e-14); } bm2.pause(); sequential += bm2.getElapsedSeconds(); bm.pause(); if (showBenchmark) { Console.WriteLine("Sequential part took " + sequential + " secs."); Console.WriteLine("Parallel part took " + parallel + " secs."); Console.WriteLine("Communication took " + communication + " secs."); Console.WriteLine("Total: " + bm.getResult() + " (" + bm.getElapsedSeconds() + " secs). Seq + Parallel: " + (sequential + parallel)); } return(converge); }
static void Main(string[] _args) { using (new MPI.Environment(ref _args)) { Intracommunicator comm = Communicator.world; if (comm.Rank == 0) { // program for rank 0 string programName = "Gauss-Seidel Linear System of Equations Solver"; string programVer = "1.0 (parallel)"; string programAuthor = "Quy N.H."; Console.WriteLine(programName + " v" + programVer + " by " + programAuthor + "\n"); bool testing = false; string[] args = _args; if (testing) { args = "-o output.txt -b 200 -t 10".Split(new char[] { ' ' }); } // parse args string inputFile = "", outputFile = ""; bool benchmarkMode = false, showEquation = false, generateInput = false, showBenchmark = false; int benchmarkSize = 3; int benchmarkTime = 1; int i = 0; while (i < args.Length) { string arg = args[i]; if (arg.StartsWith("--")) { arg = arg.Substring(2); switch (arg) { case "input": if (i + 1 < args.Length) { inputFile = args[i + 1]; } break; case "output": if (i + 1 < args.Length) { outputFile = args[i + 1]; } break; case "show-equation": showEquation = true; break; case "show-benchmark": showBenchmark = true; break; case "generate-input": generateInput = true; break; case "benchmark": if (i + 1 < args.Length && int.TryParse(args[i + 1], out benchmarkSize)) { benchmarkMode = true; i++; } ; break; case "times": if (i + 1 < args.Length && int.TryParse(args[i + 1], out benchmarkTime)) { benchmarkMode = true; i++; } ; break; } } else if (arg.StartsWith("-")) { arg = arg.Substring(1); switch (arg) { case "i": if (i + 1 < args.Length) { inputFile = args[i + 1]; } break; case "o": if (i + 1 < args.Length) { outputFile = args[i + 1]; } break; case "e": showEquation = true; break; case "m": showBenchmark = true; break; case "g": generateInput = true; break; case "b": if (i + 1 < args.Length && int.TryParse(args[i + 1], out benchmarkSize)) { benchmarkMode = true; i++; } ; break; case "t": if (i + 1 < args.Length && int.TryParse(args[i + 1], out benchmarkTime)) { benchmarkMode = true; i++; } ; break; } } i++; } // get input(s) List <Matrix> As = new List <Matrix>(), bs = new List <Matrix>(), sols = new List <Matrix>(), xs = new List <Matrix>(); if (benchmarkMode) { // generate input for (int j = 0; j < benchmarkTime; j++) { As.Add(Matrix.generateDiagonallyDominantMatrix(benchmarkSize, true, -100, 100)); bs.Add(Matrix.random(benchmarkSize, 1, -100, 100, true)); } Console.WriteLine("Generated " + benchmarkTime.ToString() + " random system(s) to solve."); } else if (inputFile.Length > 0 && File.Exists(inputFile)) { // parse input string inputArray = File.ReadAllText(inputFile); Utils.parseInput(inputArray, out As, out bs, out sols); Console.WriteLine("Got " + As.Count.ToString() + " system(s) from input file."); } else { // yell at user Console.WriteLine("Give me some inputs!"); Console.WriteLine("Exiting..."); MPI.Environment.Abort(1); } // do the calculation List <bool> converges = new List <bool>(); List <int> loopses = new List <int>(); List <Matrix> errs = new List <Matrix>(); int equCounts = As.Count; benchmark bm = new benchmark(); string bmResult = ""; Console.WriteLine("Now working with " + (comm.Size).ToString() + " process(es)...\n"); Gauss_Seidel_Parallel.showBenchmark = showBenchmark; bm.start(); for (int j = 0; j < equCounts; j++) { Console.Write("Solving system #" + (j + 1).ToString() + "... "); Matrix x, err; int loops = 0; for (int r = 1; r < comm.Size; r++) { comm.Send("start", r, 0); } bool converge = Gauss_Seidel_Parallel.solve(As[j], bs[j], out x, out err, out loops, comm); xs.Add(x); loopses.Add(loops); converges.Add(converge); errs.Add(err); Console.WriteLine("Done."); } bmResult = bm.getResult(); // write output if (!generateInput) { // show the result as usual if (outputFile.Length > 0) { writeOutput(outputFile, "\n"); } for (int j = 0; j < equCounts; j++) { Matrix x = xs[j], err = errs[j]; int loops = loopses[j]; bool converge = converges[j]; string strResult = ""; if (showEquation) { strResult += "\nEquation:\n" + Utils.writeEquation(As[j], bs[j]); } strResult += "\nNo. equations: " + x.Height.ToString(); strResult += "\nSolution: " + Matrix.Transpose(x).ToString(1e-14); strResult += "\nErrors: " + Matrix.Transpose(err).ToString(1e-14); strResult += "\nMean absolute error: " + string.Format("{0:0.##############}", Matrix.Abs(err).avgValue); strResult += "\nConverged: " + converge.ToString(); strResult += "\nLoops: " + loops.ToString(); writeOutput(outputFile, strResult); } writeOutput(outputFile, "\nElapsed time: " + bmResult + " (" + string.Format("{0:0.###}", bm.getElapsedSeconds() / equCounts) + " sec / equation)."); writeOutput(outputFile, ""); } else { // create a valid input file for (int j = 0; j < equCounts; j++) { Matrix x = xs[j], err = errs[j]; int loops = loopses[j]; bool converge = converges[j]; string strResult = "\n-----------\n"; strResult += x.Height.ToString(); strResult += "\n"; strResult += As[j].ToString(); strResult += "\n"; strResult += Matrix.Transpose(bs[j]).ToString(); strResult += "\n"; strResult += Matrix.Transpose(x).ToString(1e-14); strResult += "\n"; writeOutput(outputFile, strResult); } writeOutput("", "\nElapsed time: " + bmResult + " (" + string.Format("{0:0.###}", bm.getElapsedSeconds() / equCounts) + " sec / equation)."); writeOutput("", ""); } Console.WriteLine("Done. Exiting..."); // tell other ranks to exit for (int r = 1; r < comm.Size; r++) { comm.Send("exit", r, 0); } } else { // program for all other ranks // wait for command (start (solveSub), exit) string command = null; do { command = comm.Receive <string>(0, 0); // receive command from rank 0 if (command == "start") { Matrix A = null, b = null, x, err; int loops; Gauss_Seidel_Parallel.solve(A, b, out x, out err, out loops, comm); } } while (command != "exit"); } } }
static void Main(string[] _args) { using (new MPI.Environment(ref _args)) { Intracommunicator comm = Communicator.world; if (comm.Rank == 0) { // program for rank 0 string programName = "Gauss-Seidel Linear System of Equations Solver"; string programVer = "1.0 (test)"; string programAuthor = "Quy N.H."; Console.WriteLine(programName + " v" + programVer + " by " + programAuthor + "\n"); // check number of processes in this communicator if (comm.Size < 2) { Console.WriteLine("Please run at least 2 processes of me."); Console.WriteLine("Exiting..."); MPI.Environment.Abort(1); } string inputFile = "", outputFile = ""; bool benchmarkMode = true, showEquation = false, generateInput = false; int benchmarkSize = 100; int benchmarkTime = 100; // get input(s) List <Matrix> As = new List <Matrix>(), bs = new List <Matrix>(), sols = new List <Matrix>(), xs_p = new List <Matrix>(), xs_s = new List <Matrix>(); if (benchmarkMode) { // generate input for (int j = 0; j < benchmarkTime; j++) { As.Add(Matrix.generateDiagonallyDominantMatrix(benchmarkSize, true, -100, 100)); bs.Add(Matrix.random(benchmarkSize, 1, -100, 100, true)); } } else if (inputFile.Length > 0) { // parse input string inputArray = File.ReadAllText(inputFile); Utils.parseInput(inputArray, out As, out bs, out sols); } else { // yell at user Console.WriteLine("Give me some inputs!"); Console.WriteLine("Exiting..."); MPI.Environment.Abort(1); } // do the calculation List <bool> converges_p = new List <bool>(), converges_s = new List <bool>(); List <int> loopses_p = new List <int>(), loopses_s = new List <int>(); List <Matrix> errs_p = new List <Matrix>(), errs_s = new List <Matrix>(); int equCounts = As.Count; benchmark bm = new benchmark(); string bmResult = ""; bm.start(); for (int j = 0; j < equCounts; j++) { Console.Write("Solving system #" + (j + 1).ToString() + "... "); Matrix x, err; int loops = 0; for (int r = 1; r < comm.Size; r++) { comm.Send("start", r, 0); } Console.Write("Parallel... "); bool converge = Gauss_Seidel_Parallel.solve(As[j], bs[j], out x, out err, out loops, comm); xs_p.Add(x); loopses_p.Add(loops); converges_p.Add(converge); errs_p.Add(err); Console.Write("Serial... "); converge = Gauss_Seidel.solve(As[j], bs[j], out x, out err, out loops); xs_s.Add(x); loopses_s.Add(loops); converges_s.Add(converge); errs_s.Add(err); Console.WriteLine("Done."); } bmResult = bm.getResult(); // write output Console.WriteLine("\nVerifying results:\n"); int total = 0, passed = 0, failed = 0; for (int j = 0; j < equCounts; j++) { Matrix x_p = xs_p[j], err_p = errs_p[j], x_s = xs_s[j], err_s = errs_s[j]; int loops_p = loopses_p[j], loops_s = loopses_s[j]; bool converge_p = converges_p[j], converge_s = converges_s[j]; bool c = false, l = false, s = false; Console.Write("System #" + (j + 1).ToString() + ": "); if (s = x_p.ToString() == x_s.ToString()) { Console.Write("solutions match, "); } else { Console.Write("solutions DON'T match, "); } if (l = loops_p == loops_s) { Console.Write("loop count matches, "); } else { Console.Write("loop count DOESN'T match, "); } if (c = converge_p == converge_s) { Console.Write("convergence matches. "); } else { Console.Write("convergence DOESN'T match. "); } if (s && c && l) { Console.WriteLine("Passed!"); passed += 1; } else { Console.WriteLine("Failed!"); failed += 1; } total += 1; } Console.WriteLine("\nTotal: " + total.ToString() + ". Passed: " + passed.ToString() + ". Failed: " + failed.ToString()); Console.WriteLine("\nElapsed time: " + bmResult + " (" + string.Format("{0:0.###}", bm.getElapsedSeconds() / equCounts) + " sec / equation)."); Console.WriteLine("Done. Exiting..."); // tell other ranks to exit for (int r = 1; r < comm.Size; r++) { comm.Send("exit", r, 0); } } else { // program for all other ranks // wait for command (start (solveSub), exit) string command = null; do { command = comm.Receive <string>(0, 0); // receive command from rank 0 if (command == "start") { Matrix A = null, b = null, x, err; int loops; Gauss_Seidel_Parallel.solve(A, b, out x, out err, out loops, comm); } } while (command != "exit"); } } }