public void DetermineStatus() { var criteria = new List<IIterationStopCriterium<float>> { new FailureStopCriterium(), new DivergenceStopCriterium(), new IterationCountStopCriterium<float>(1) }; var iterator = new Iterator<float>(criteria); // First step, nothing should happen. iterator.DetermineStatus( 0, DenseVector.Create(3, i => 4), DenseVector.Create(3, i => 4), DenseVector.Create(3, i => 4)); Assert.AreEqual(IterationStatus.Continue, iterator.Status, "Incorrect status"); // Second step, should run out of iterations. iterator.DetermineStatus( 1, DenseVector.Create(3, i => 4), DenseVector.Create(3, i => 4), DenseVector.Create(3, i => 4)); Assert.AreEqual(IterationStatus.StoppedWithoutConvergence, iterator.Status, "Incorrect status"); }
/// <summary> /// Creates a default iterator with all the <see cref="IIterationStopCriterium"/> objects. /// </summary> /// <returns>A new <see cref="IIterator"/> object.</returns> public static IIterator CreateDefault() { var iterator = new Iterator(); iterator.Add(new FailureStopCriterium()); iterator.Add(new DivergenceStopCriterium()); iterator.Add(new IterationCountStopCriterium()); iterator.Add(new ResidualStopCriterium()); return iterator; }
public void CanSolveForRandomMatrix(int order) { // Due to datatype "float" it can happen that solution will not converge for specific random matrix // That's why we will do 4 tries and downgrade stop criterium each time for (var iteration = 6; iteration > 3; iteration--) { var matrixA = MatrixLoader.GenerateRandomDenseMatrix(order, order); var matrixB = MatrixLoader.GenerateRandomDenseMatrix(order, order); var monitor = new Iterator<float>(new IIterationStopCriterium<float>[] { new IterationCountStopCriterium<float>(MaximumIterations), new ResidualStopCriterium((float) Math.Pow(1.0/10.0, iteration)) }); var solver = new TFQMR(monitor); var matrixX = solver.Solve(matrixA, matrixB); if (!monitor.HasConverged) { // Solution was not found, try again downgrading convergence boundary continue; } // The solution X row dimension is equal to the column dimension of A Assert.AreEqual(matrixA.ColumnCount, matrixX.RowCount); // The solution X has the same number of columns as B Assert.AreEqual(matrixB.ColumnCount, matrixX.ColumnCount); var matrixBReconstruct = matrixA*matrixX; // Check the reconstruction. for (var i = 0; i < matrixB.RowCount; i++) { for (var j = 0; j < matrixB.ColumnCount; j++) { Assert.AreEqual(matrixB[i, j], matrixBReconstruct[i, j], (float) Math.Pow(1.0/10.0, iteration - 4)); } } return; } }
public void CanSolveForRandomVector(int order) { // Due to datatype "float" it can happen that solution will not converge for specific random matrix // That's why we will do 3 tries and downgrade stop criterium each time for (var iteration = 6; iteration > 3; iteration--) { var matrixA = MatrixLoader.GenerateRandomDenseMatrix(order, order); var vectorb = MatrixLoader.GenerateRandomDenseVector(order); var monitor = new Iterator<float>( new IterationCountStopCriterium<float>(MaximumIterations), new ResidualStopCriterium((float) Math.Pow(1.0/10.0, iteration))); var solver = new TFQMR(); var resultx = matrixA.SolveIterative(vectorb, solver, monitor); if (monitor.Status != IterationStatus.Converged) { // Solution was not found, try again downgrading convergence boundary continue; } Assert.AreEqual(matrixA.ColumnCount, resultx.Count); var matrixBReconstruct = matrixA*resultx; // Check the reconstruction. for (var i = 0; i < order; i++) { Assert.AreEqual(vectorb[i], matrixBReconstruct[i], (float) Math.Pow(1.0/10.0, iteration - 3)); } return; } }
public void SolvePoissonMatrixAndBackMultiply() { // Create the matrix var matrix = new SparseMatrix(25); // Assemble the matrix. We assume we're solving the Poisson equation // on a rectangular 5 x 5 grid const int GridSize = 5; // The pattern is: // 0 .... 0 -1 0 0 0 0 0 0 0 0 -1 4 -1 0 0 0 0 0 0 0 0 -1 0 0 ... 0 for (var i = 0; i < matrix.RowCount; i++) { // Insert the first set of -1's if (i > (GridSize - 1)) { matrix[i, i - GridSize] = -1; } // Insert the second set of -1's if (i > 0) { matrix[i, i - 1] = -1; } // Insert the centerline values matrix[i, i] = 4; // Insert the first trailing set of -1's if (i < matrix.RowCount - 1) { matrix[i, i + 1] = -1; } // Insert the second trailing set of -1's if (i < matrix.RowCount - GridSize) { matrix[i, i + GridSize] = -1; } } // Create the y vector var y = DenseVector.Create(matrix.RowCount, i => 1); // Due to datatype "float" it can happen that solution will not converge for specific random starting vectors // That's why we will do 3 tries for (var iteration = 0; iteration <= 3; iteration++) { // Create an iteration monitor which will keep track of iterative convergence var monitor = new Iterator<float>( new IterationCountStopCriterium<float>(MaximumIterations), new ResidualStopCriterium<float>(ConvergenceBoundary), new DivergenceStopCriterium<float>(), new FailureStopCriterium<float>()); var solver = new MlkBiCgStab(); // Solve equation Ax = y Vector<float> x; try { x = matrix.SolveIterative(y, solver, monitor); } catch (Exception) { continue; } if (monitor.Status != IterationStatus.Converged) { continue; } // Now compare the results Assert.IsNotNull(x, "#02"); Assert.AreEqual(y.Count, x.Count, "#03"); // Back multiply the vector var z = matrix.Multiply(x); // Now compare the vectors for (var i = 0; i < y.Count; i++) { Assert.GreaterOrEqual(ConvergenceBoundary, Math.Abs(y[i] - z[i]), "#05-" + i); } return; } }
public void SolveScaledUnitMatrixAndBackMultiply() { // Create the identity matrix var matrix = SparseMatrix.CreateIdentity(100); // Scale it with a funny number matrix.Multiply((float)Math.PI, matrix); // Create the y vector var y = Vector<float>.Build.Dense(matrix.RowCount, 1); // Create an iteration monitor which will keep track of iterative convergence var monitor = new Iterator<float>( new IterationCountStopCriterion<float>(MaximumIterations), new ResidualStopCriterion<float>(ConvergenceBoundary), new DivergenceStopCriterion<float>(), new FailureStopCriterion<float>()); var solver = new MlkBiCgStab(); // Solve equation Ax = y var x = matrix.SolveIterative(y, solver, monitor); // Now compare the results Assert.IsNotNull(x, "#02"); Assert.AreEqual(y.Count, x.Count, "#03"); // Back multiply the vector var z = matrix.Multiply(x); // Check that the solution converged Assert.IsTrue(monitor.Status == IterationStatus.Converged, "#04"); // Now compare the vectors for (var i = 0; i < y.Count; i++) { Assert.GreaterOrEqual(ConvergenceBoundary, Math.Abs(y[i] - z[i]), "#05-" + i); } }
public void SolveUnitMatrixAndBackMultiply() { // Create the identity matrix var matrix = SparseMatrix.CreateIdentity(100); // Create the y vector var y = Vector<float>.Build.Dense(matrix.RowCount, 1); // Create an iteration monitor which will keep track of iterative convergence var monitor = new Iterator<float>( new IterationCountStopCriterium<float>(MaximumIterations), new ResidualStopCriterium<float>(ConvergenceBoundary), new DivergenceStopCriterium<float>(), new FailureStopCriterium<float>()); var solver = new TFQMR(); // Solve equation Ax = y var x = matrix.SolveIterative(y, solver, monitor); // Now compare the results Assert.IsNotNull(x, "#02"); Assert.AreEqual(y.Count, x.Count, "#03"); // Back multiply the vector var z = matrix.Multiply(x); // Check that the solution converged Assert.IsTrue(monitor.Status == IterationStatus.Converged, "#04"); // Now compare the vectors Assert.LessOrEqual(Distance.Chebyshev(y, z), 2*ConvergenceBoundary); }
/// <summary> /// Solves the matrix equation Ax = b, where A is the coefficient matrix, b is the /// solution vector and x is the unknown vector. /// </summary> /// <param name="matrix">The coefficient matrix, <c>A</c>.</param> /// <param name="input">The solution vector, <c>b</c></param> /// <param name="result">The result vector, <c>x</c></param> /// <param name="iterator">The iterator to use to control when to stop iterating.</param> /// <param name="preconditioner">The preconditioner to use for approximations.</param> public void Solve(Matrix <float> matrix, Vector <float> input, Vector <float> result, Iterator <float> iterator, IPreconditioner <float> preconditioner) { if (matrix.RowCount != matrix.ColumnCount) { throw new ArgumentException(Resources.ArgumentMatrixSquare, nameof(matrix)); } if (result.Count != input.Count) { throw new ArgumentException(Resources.ArgumentVectorsSameLength); } if (iterator == null) { iterator = new Iterator <float>(); } if (preconditioner == null) { preconditioner = new UnitPreconditioner <float>(); } // Create a copy of the solution and result vectors so we can use them // later on var internalInput = input.Clone(); var internalResult = result.Clone(); foreach (var solver in _solvers) { // Store a reference to the solver so we can stop it. IterationStatus status; try { // Reset the iterator and pass it to the solver iterator.Reset(); // Start the solver solver.Item1.Solve(matrix, internalInput, internalResult, iterator, solver.Item2 ?? preconditioner); status = iterator.Status; } catch (Exception) { // The solver broke down. // Log a message about this // Switch to the next preconditioner. // Reset the solution vector to the previous solution input.CopyTo(internalInput); continue; } // There was no fatal breakdown so check the status if (status == IterationStatus.Converged) { // We're done internalResult.CopyTo(result); break; } // We're not done // Either: // - calculation finished without convergence if (status == IterationStatus.StoppedWithoutConvergence) { // Copy the internal result to the result vector and // continue with the calculation. internalResult.CopyTo(result); } else { // - calculation failed --> restart with the original vector // - calculation diverged --> restart with the original vector // - Some unknown status occurred --> To be safe restart. input.CopyTo(internalInput); } } }
public void ResetToPrecalculationState() { var criteria = new List<IIterationStopCriterium<float>> { new FailureStopCriterium(), new DivergenceStopCriterium(), new IterationCountStopCriterium<float>(1) }; var iterator = new Iterator<float>(criteria); // First step, nothing should happen. iterator.DetermineStatus( 0, DenseVector.Create(3, i => 4), DenseVector.Create(3, i => 4), DenseVector.Create(3, i => 4)); Assert.AreEqual(IterationStatus.Continue, iterator.Status, "Incorrect status"); iterator.Reset(); Assert.AreEqual(IterationStatus.Continue, iterator.Status, "Incorrect status"); Assert.AreEqual(IterationStatus.Continue, criteria[0].Status, "Incorrect status"); Assert.AreEqual(IterationStatus.Continue, criteria[1].Status, "Incorrect status"); Assert.AreEqual(IterationStatus.Continue, criteria[2].Status, "Incorrect status"); }
/// <summary> /// Solves the matrix equation Ax = b, where A is the coefficient matrix, b is the /// solution vector and x is the unknown vector. /// </summary> /// <param name="matrix">The coefficient matrix, <c>A</c>.</param> /// <param name="input">The solution vector, <c>b</c></param> /// <param name="result">The result vector, <c>x</c></param> public void Solve(Matrix <float> matrix, Vector <float> input, Vector <float> result) { // If we were stopped before, we are no longer // We're doing this at the start of the method to ensure // that we can use these fields immediately. _hasBeenStopped = false; // Error checks if (matrix == null) { throw new ArgumentNullException("matrix"); } if (matrix.RowCount != matrix.ColumnCount) { throw new ArgumentException(Resources.ArgumentMatrixSquare, "matrix"); } if (input == null) { throw new ArgumentNullException("input"); } if (result == null) { throw new ArgumentNullException("result"); } if (result.Count != input.Count) { throw new ArgumentException(Resources.ArgumentVectorsSameLength); } if (input.Count != matrix.RowCount) { throw Matrix.DimensionsDontMatch <ArgumentException>(input, matrix); } // Initialize the solver fields // Set the convergence monitor if (_iterator == null) { _iterator = Iterator.CreateDefault(); } if (_preconditioner == null) { _preconditioner = new UnitPreconditioner <float>(); } _preconditioner.Initialize(matrix); // Choose an initial guess x_0 // Take x_0 = 0 var xtemp = new DenseVector(input.Count); // Choose k vectors q_1, q_2, ..., q_k // Build a new set if: // a) the stored set doesn't exist (i.e. == null) // b) Is of an incorrect length (i.e. too long) // c) The vectors are of an incorrect length (i.e. too long or too short) var useOld = false; if (_startingVectors != null) { // We don't accept collections with zero starting vectors so ... if (_startingVectors.Count <= NumberOfStartingVectorsToCreate(_numberOfStartingVectors, input.Count)) { // Only check the first vector for sizing. If that matches we assume the // other vectors match too. If they don't the process will crash if (_startingVectors[0].Count == input.Count) { useOld = true; } } } _startingVectors = useOld ? _startingVectors : CreateStartingVectors(_numberOfStartingVectors, input.Count); // Store the number of starting vectors. Not really necessary but easier to type :) var k = _startingVectors.Count; // r_0 = b - Ax_0 // This is basically a SAXPY so it could be made a lot faster var residuals = new DenseVector(matrix.RowCount); CalculateTrueResidual(matrix, residuals, xtemp, input); // Define the temporary scalars var c = new float[k]; // Define the temporary vectors var gtemp = new DenseVector(residuals.Count); var u = new DenseVector(residuals.Count); var utemp = new DenseVector(residuals.Count); var temp = new DenseVector(residuals.Count); var temp1 = new DenseVector(residuals.Count); var temp2 = new DenseVector(residuals.Count); var zd = new DenseVector(residuals.Count); var zg = new DenseVector(residuals.Count); var zw = new DenseVector(residuals.Count); var d = CreateVectorArray(_startingVectors.Count, residuals.Count); // g_0 = r_0 var g = CreateVectorArray(_startingVectors.Count, residuals.Count); residuals.CopyTo(g[k - 1]); var w = CreateVectorArray(_startingVectors.Count, residuals.Count); // FOR (j = 0, 1, 2 ....) var iterationNumber = 0; while (ShouldContinue(iterationNumber, xtemp, input, residuals)) { // SOLVE M g~_((j-1)k+k) = g_((j-1)k+k) _preconditioner.Approximate(g[k - 1], gtemp); // w_((j-1)k+k) = A g~_((j-1)k+k) matrix.Multiply(gtemp, w[k - 1]); // c_((j-1)k+k) = q^T_1 w_((j-1)k+k) c[k - 1] = _startingVectors[0].DotProduct(w[k - 1]); if (c[k - 1].AlmostEqual(0, 1)) { throw new Exception("Iterative solver experience a numerical break down"); } // alpha_(jk+1) = q^T_1 r_((j-1)k+k) / c_((j-1)k+k) var alpha = _startingVectors[0].DotProduct(residuals) / c[k - 1]; // u_(jk+1) = r_((j-1)k+k) - alpha_(jk+1) w_((j-1)k+k) w[k - 1].Multiply(-alpha, temp); residuals.Add(temp, u); // SOLVE M u~_(jk+1) = u_(jk+1) _preconditioner.Approximate(u, temp1); temp1.CopyTo(utemp); // rho_(j+1) = -u^t_(jk+1) A u~_(jk+1) / ||A u~_(jk+1)||^2 matrix.Multiply(temp1, temp); var rho = temp.DotProduct(temp); // If rho is zero then temp is a zero vector and we're probably // about to have zero residuals (i.e. an exact solution). // So set rho to 1.0 because in the next step it will turn to zero. if (rho.AlmostEqual(0, 1)) { rho = 1.0f; } rho = -u.DotProduct(temp) / rho; // r_(jk+1) = rho_(j+1) A u~_(jk+1) + u_(jk+1) u.CopyTo(residuals); // Reuse temp temp.Multiply(rho, temp); residuals.Add(temp, temp2); temp2.CopyTo(residuals); // x_(jk+1) = x_((j-1)k_k) - rho_(j+1) u~_(jk+1) + alpha_(jk+1) g~_((j-1)k+k) utemp.Multiply(-rho, temp); xtemp.Add(temp, temp2); temp2.CopyTo(xtemp); gtemp.Multiply(alpha, gtemp); xtemp.Add(gtemp, temp2); temp2.CopyTo(xtemp); // Check convergence and stop if we are converged. if (!ShouldContinue(iterationNumber, xtemp, input, residuals)) { // Calculate the true residual CalculateTrueResidual(matrix, residuals, xtemp, input); // Now recheck the convergence if (!ShouldContinue(iterationNumber, xtemp, input, residuals)) { // We're all good now. // Exit from the while loop. break; } } // FOR (i = 1,2, ...., k) for (var i = 0; i < k; i++) { // z_d = u_(jk+1) u.CopyTo(zd); // z_g = r_(jk+i) residuals.CopyTo(zg); // z_w = 0 zw.Clear(); // FOR (s = i, ...., k-1) AND j >= 1 float beta; if (iterationNumber >= 1) { for (var s = i; s < k - 1; s++) { // beta^(jk+i)_((j-1)k+s) = -q^t_(s+1) z_d / c_((j-1)k+s) beta = -_startingVectors[s + 1].DotProduct(zd) / c[s]; // z_d = z_d + beta^(jk+i)_((j-1)k+s) d_((j-1)k+s) d[s].Multiply(beta, temp); zd.Add(temp, temp2); temp2.CopyTo(zd); // z_g = z_g + beta^(jk+i)_((j-1)k+s) g_((j-1)k+s) g[s].Multiply(beta, temp); zg.Add(temp, temp2); temp2.CopyTo(zg); // z_w = z_w + beta^(jk+i)_((j-1)k+s) w_((j-1)k+s) w[s].Multiply(beta, temp); zw.Add(temp, temp2); temp2.CopyTo(zw); } } beta = rho * c[k - 1]; if (beta.AlmostEqual(0, 1)) { throw new Exception("Iterative solver experience a numerical break down"); } // beta^(jk+i)_((j-1)k+k) = -(q^T_1 (r_(jk+1) + rho_(j+1) z_w)) / (rho_(j+1) c_((j-1)k+k)) zw.Multiply(rho, temp2); residuals.Add(temp2, temp); beta = -_startingVectors[0].DotProduct(temp) / beta; // z_g = z_g + beta^(jk+i)_((j-1)k+k) g_((j-1)k+k) g[k - 1].Multiply(beta, temp); zg.Add(temp, temp2); temp2.CopyTo(zg); // z_w = rho_(j+1) (z_w + beta^(jk+i)_((j-1)k+k) w_((j-1)k+k)) w[k - 1].Multiply(beta, temp); zw.Add(temp, temp2); temp2.CopyTo(zw); zw.Multiply(rho, zw); // z_d = r_(jk+i) + z_w residuals.Add(zw, zd); // FOR (s = 1, ... i - 1) for (var s = 0; s < i - 1; s++) { // beta^(jk+i)_(jk+s) = -q^T_s+1 z_d / c_(jk+s) beta = -_startingVectors[s + 1].DotProduct(zd) / c[s]; // z_d = z_d + beta^(jk+i)_(jk+s) * d_(jk+s) d[s].Multiply(beta, temp); zd.Add(temp, temp2); temp2.CopyTo(zd); // z_g = z_g + beta^(jk+i)_(jk+s) * g_(jk+s) g[s].Multiply(beta, temp); zg.Add(temp, temp2); temp2.CopyTo(zg); } // d_(jk+i) = z_d - u_(jk+i) zd.Subtract(u, d[i]); // g_(jk+i) = z_g + z_w zg.Add(zw, g[i]); // IF (i < k - 1) if (i < k - 1) { // c_(jk+1) = q^T_i+1 d_(jk+i) c[i] = _startingVectors[i + 1].DotProduct(d[i]); if (c[i].AlmostEqual(0, 1)) { throw new Exception("Iterative solver experience a numerical break down"); } // alpha_(jk+i+1) = q^T_(i+1) u_(jk+i) / c_(jk+i) alpha = _startingVectors[i + 1].DotProduct(u) / c[i]; // u_(jk+i+1) = u_(jk+i) - alpha_(jk+i+1) d_(jk+i) d[i].Multiply(-alpha, temp); u.Add(temp, temp2); temp2.CopyTo(u); // SOLVE M g~_(jk+i) = g_(jk+i) _preconditioner.Approximate(g[i], gtemp); // x_(jk+i+1) = x_(jk+i) + rho_(j+1) alpha_(jk+i+1) g~_(jk+i) gtemp.Multiply(rho * alpha, temp); xtemp.Add(temp, temp2); temp2.CopyTo(xtemp); // w_(jk+i) = A g~_(jk+i) matrix.Multiply(gtemp, w[i]); // r_(jk+i+1) = r_(jk+i) - rho_(j+1) alpha_(jk+i+1) w_(jk+i) w[i].Multiply(-rho * alpha, temp); residuals.Add(temp, temp2); temp2.CopyTo(residuals); // We can check the residuals here if they're close if (!ShouldContinue(iterationNumber, xtemp, input, residuals)) { // Recalculate the residuals and go round again. This is done to ensure that // we have the proper residuals. CalculateTrueResidual(matrix, residuals, xtemp, input); } } } // END ITERATION OVER i iterationNumber++; } // copy the temporary result to the real result vector xtemp.CopyTo(result); }
public void DetermineStatusWithoutStopCriteriaDoesNotThrow() { var iterator = new Iterator<float>(); Assert.DoesNotThrow(() => iterator.DetermineStatus( 0, DenseVector.Create(3, i => 4), DenseVector.Create(3, i => 5), DenseVector.Create(3, i => 6))); }
/// <summary> /// Sets the <see cref="Iterator{T}"/> that will be used to track the iterative process. /// </summary> /// <param name="iterator">The iterator.</param> public void SetIterator(Iterator <float> iterator) { _iterator = iterator; }
/// <summary> /// Initializes a new instance of the <see cref="MlkBiCgStab"/> class. /// </summary> /// <remarks> /// <para> /// The main advantages of using a user defined <see cref="Iterator{T}"/> are: /// <list type="number"> /// <item>It is possible to set the desired convergence limits.</item> /// <item> /// It is possible to check the reason for which the solver finished /// the iterative procedure by calling the <see cref="Iterator{T}.Status"/> property. /// </item> /// </list> /// </para> /// </remarks> /// <param name="preconditioner">The <see cref="IPreConditioner{T}"/> that will be used to precondition the matrix equation.</param> /// <param name="iterator">The <see cref="Iterator{T}"/> that will be used to monitor the iterative process.</param> public MlkBiCgStab(IPreConditioner <float> preconditioner, Iterator <float> iterator) { _iterator = iterator; _preconditioner = preconditioner; }
/// <summary> /// Initializes a new instance of the <see cref="MlkBiCgStab"/> class. /// </summary> /// <remarks> /// <para> /// When using this constructor the solver will use a default preconditioner. /// </para> /// <para> /// The main advantages of using a user defined <see cref="Iterator{T}"/> are: /// <list type="number"> /// <item>It is possible to set the desired convergence limits.</item> /// <item> /// It is possible to check the reason for which the solver finished /// the iterative procedure by calling the <see cref="Iterator{T}.Status"/> property. /// </item> /// </list> /// </para> /// </remarks> /// <param name="iterator">The <see cref="Iterator{T}"/> that will be used to monitor the iterative process.</param> public MlkBiCgStab(Iterator <float> iterator) : this(null, iterator) { }
public void SolvePoissonMatrixAndBackMultiply() { // Create the matrix var matrix = new SparseMatrix(25); // Assemble the matrix. We assume we're solving the Poisson equation // on a rectangular 5 x 5 grid const int GridSize = 5; // The pattern is: // 0 .... 0 -1 0 0 0 0 0 0 0 0 -1 4 -1 0 0 0 0 0 0 0 0 -1 0 0 ... 0 for (var i = 0; i < matrix.RowCount; i++) { // Insert the first set of -1's if (i > (GridSize - 1)) { matrix[i, i - GridSize] = -1; } // Insert the second set of -1's if (i > 0) { matrix[i, i - 1] = -1; } // Insert the centerline values matrix[i, i] = 4; // Insert the first trailing set of -1's if (i < matrix.RowCount - 1) { matrix[i, i + 1] = -1; } // Insert the second trailing set of -1's if (i < matrix.RowCount - GridSize) { matrix[i, i + GridSize] = -1; } } // Create the y vector var y = DenseVector.Create(matrix.RowCount, i => 1); // Create an iteration monitor which will keep track of iterative convergence var monitor = new Iterator<float>( new IterationCountStopCriterium<float>(MaximumIterations), new ResidualStopCriterium(ConvergenceBoundary), new DivergenceStopCriterium(), new FailureStopCriterium()); var solver = new TFQMR(); // Solve equation Ax = y var x = matrix.SolveIterative(y, solver, monitor); // Now compare the results Assert.IsNotNull(x, "#02"); Assert.AreEqual(y.Count, x.Count, "#03"); // Back multiply the vector var z = matrix.Multiply(x); // Check that the solution converged Assert.IsTrue(monitor.Status == IterationStatus.Converged, "#04"); // Now compare the vectors for (var i = 0; i < y.Count; i++) { Assert.IsTrue(Math.Abs(y[i] - z[i]).IsSmaller(ConvergenceBoundary, 1), "#05-" + i); } }
public void DetermineStatusWithNegativeIterationNumberThrowsArgumentOutOfRangeException() { var criteria = new List<IIterationStopCriterium<float>> { new FailureStopCriterium(), new DivergenceStopCriterium(), new IterationCountStopCriterium<float>(), new ResidualStopCriterium() }; var iterator = new Iterator<float>(criteria); Assert.Throws<ArgumentOutOfRangeException>(() => iterator.DetermineStatus( -1, DenseVector.Create(3, i => 4), DenseVector.Create(3, i => 5), DenseVector.Create(3, i => 6))); }
public void SolveUnitMatrixAndBackMultiply() { // Create the identity matrix var matrix = SparseMatrix.Identity(100); // Create the y vector var y = DenseVector.Create(matrix.RowCount, i => 1); // Create an iteration monitor which will keep track of iterative convergence var monitor = new Iterator<float>( new IterationCountStopCriterium<float>(MaximumIterations), new ResidualStopCriterium(ConvergenceBoundary), new DivergenceStopCriterium(), new FailureStopCriterium()); var solver = new TFQMR(); // Solve equation Ax = y var x = matrix.SolveIterative(y, solver, monitor); // Now compare the results Assert.IsNotNull(x, "#02"); Assert.AreEqual(y.Count, x.Count, "#03"); // Back multiply the vector var z = matrix.Multiply(x); // Check that the solution converged Assert.IsTrue(monitor.Status == IterationStatus.Converged, "#04"); // Now compare the vectors for (var i = 0; i < y.Count; i++) { Assert.IsTrue((y[i] - z[i]).IsSmaller(ConvergenceBoundary, 1), "#05-" + i); } }
/// <summary> /// Solves the matrix equation Ax = b, where A is the coefficient matrix, b is the /// solution vector and x is the unknown vector. /// </summary> /// <param name="matrix">The coefficient matrix, <c>A</c>.</param> /// <param name="input">The solution vector, <c>b</c></param> /// <param name="result">The result vector, <c>x</c></param> /// <param name="iterator">The iterator to use to control when to stop iterating.</param> /// <param name="preconditioner">The preconditioner to use for approximations.</param> public void Solve(Matrix <float> matrix, Vector <float> input, Vector <float> result, Iterator <float> iterator, IPreconditioner <float> preconditioner) { if (matrix.RowCount != matrix.ColumnCount) { throw new ArgumentException(Resources.ArgumentMatrixSquare, nameof(matrix)); } if (result.Count != input.Count) { throw new ArgumentException(Resources.ArgumentVectorsSameLength); } if (input.Count != matrix.RowCount) { throw Matrix.DimensionsDontMatch <ArgumentException>(input, matrix); } if (iterator == null) { iterator = new Iterator <float>(); } if (preconditioner == null) { preconditioner = new UnitPreconditioner <float>(); } preconditioner.Initialize(matrix); // x_0 is initial guess // Take x_0 = 0 var xtemp = new DenseVector(input.Count); // r_0 = b - Ax_0 // This is basically a SAXPY so it could be made a lot faster var residuals = new DenseVector(matrix.RowCount); CalculateTrueResidual(matrix, residuals, xtemp, input); // Define the temporary scalars float beta = 0; // Define the temporary vectors // rDash_0 = r_0 var rdash = DenseVector.OfVector(residuals); // t_-1 = 0 var t = new DenseVector(residuals.Count); var t0 = new DenseVector(residuals.Count); // w_-1 = 0 var w = new DenseVector(residuals.Count); // Define the remaining temporary vectors var c = new DenseVector(residuals.Count); var p = new DenseVector(residuals.Count); var s = new DenseVector(residuals.Count); var u = new DenseVector(residuals.Count); var y = new DenseVector(residuals.Count); var z = new DenseVector(residuals.Count); var temp = new DenseVector(residuals.Count); var temp2 = new DenseVector(residuals.Count); var temp3 = new DenseVector(residuals.Count); // for (k = 0, 1, .... ) var iterationNumber = 0; while (iterator.DetermineStatus(iterationNumber, xtemp, input, residuals) == IterationStatus.Continue) { // p_k = r_k + beta_(k-1) * (p_(k-1) - u_(k-1)) p.Subtract(u, temp); temp.Multiply(beta, temp2); residuals.Add(temp2, p); // Solve M b_k = p_k preconditioner.Approximate(p, temp); // s_k = A b_k matrix.Multiply(temp, s); // alpha_k = (r*_0 * r_k) / (r*_0 * s_k) var alpha = rdash.DotProduct(residuals) / rdash.DotProduct(s); // y_k = t_(k-1) - r_k - alpha_k * w_(k-1) + alpha_k s_k s.Subtract(w, temp); t.Subtract(residuals, y); temp.Multiply(alpha, temp2); y.Add(temp2, temp3); temp3.CopyTo(y); // Store the old value of t in t0 t.CopyTo(t0); // t_k = r_k - alpha_k s_k s.Multiply(-alpha, temp2); residuals.Add(temp2, t); // Solve M d_k = t_k preconditioner.Approximate(t, temp); // c_k = A d_k matrix.Multiply(temp, c); var cdot = c.DotProduct(c); // cDot can only be zero if c is a zero vector // We'll set cDot to 1 if it is zero to prevent NaN's // Note that the calculation should continue fine because // c.DotProduct(t) will be zero and so will c.DotProduct(y) if (cdot.AlmostEqualNumbersBetween(0, 1)) { cdot = 1.0f; } // Even if we don't want to do any BiCGStab steps we'll still have // to do at least one at the start to initialize the // system, but we'll only have to take special measures // if we don't do any so ... var ctdot = c.DotProduct(t); float eta; float sigma; if (((_numberOfBiCgStabSteps == 0) && (iterationNumber == 0)) || ShouldRunBiCgStabSteps(iterationNumber)) { // sigma_k = (c_k * t_k) / (c_k * c_k) sigma = ctdot / cdot; // eta_k = 0 eta = 0; } else { var ydot = y.DotProduct(y); // yDot can only be zero if y is a zero vector // We'll set yDot to 1 if it is zero to prevent NaN's // Note that the calculation should continue fine because // y.DotProduct(t) will be zero and so will c.DotProduct(y) if (ydot.AlmostEqualNumbersBetween(0, 1)) { ydot = 1.0f; } var ytdot = y.DotProduct(t); var cydot = c.DotProduct(y); var denom = (cdot * ydot) - (cydot * cydot); // sigma_k = ((y_k * y_k)(c_k * t_k) - (y_k * t_k)(c_k * y_k)) / ((c_k * c_k)(y_k * y_k) - (y_k * c_k)(c_k * y_k)) sigma = ((ydot * ctdot) - (ytdot * cydot)) / denom; // eta_k = ((c_k * c_k)(y_k * t_k) - (y_k * c_k)(c_k * t_k)) / ((c_k * c_k)(y_k * y_k) - (y_k * c_k)(c_k * y_k)) eta = ((cdot * ytdot) - (cydot * ctdot)) / denom; } // u_k = sigma_k s_k + eta_k (t_(k-1) - r_k + beta_(k-1) u_(k-1)) u.Multiply(beta, temp2); t0.Add(temp2, temp); temp.Subtract(residuals, temp3); temp3.CopyTo(temp); temp.Multiply(eta, temp); s.Multiply(sigma, temp2); temp.Add(temp2, u); // z_k = sigma_k r_k +_ eta_k z_(k-1) - alpha_k u_k z.Multiply(eta, z); u.Multiply(-alpha, temp2); z.Add(temp2, temp3); temp3.CopyTo(z); residuals.Multiply(sigma, temp2); z.Add(temp2, temp3); temp3.CopyTo(z); // x_(k+1) = x_k + alpha_k p_k + z_k p.Multiply(alpha, temp2); xtemp.Add(temp2, temp3); temp3.CopyTo(xtemp); xtemp.Add(z, temp3); temp3.CopyTo(xtemp); // r_(k+1) = t_k - eta_k y_k - sigma_k c_k // Copy the old residuals to a temp vector because we'll // need those in the next step residuals.CopyTo(t0); y.Multiply(-eta, temp2); t.Add(temp2, residuals); c.Multiply(-sigma, temp2); residuals.Add(temp2, temp3); temp3.CopyTo(residuals); // beta_k = alpha_k / sigma_k * (r*_0 * r_(k+1)) / (r*_0 * r_k) // But first we check if there is a possible NaN. If so just reset beta to zero. beta = (!sigma.AlmostEqualNumbersBetween(0, 1)) ? ((alpha / sigma) * rdash.DotProduct(residuals)) / rdash.DotProduct(t0) : 0; // w_k = c_k + beta_k s_k s.Multiply(beta, temp2); c.Add(temp2, w); // Get the real value preconditioner.Approximate(xtemp, result); // Now check for convergence if (iterator.DetermineStatus(iterationNumber, result, input, residuals) != IterationStatus.Continue) { // Recalculate the residuals and go round again. This is done to ensure that // we have the proper residuals. CalculateTrueResidual(matrix, residuals, result, input); } // Next iteration. iterationNumber++; } }