/// <summary> /// Initializes the preconditioner and loads the internal data structures. /// </summary> /// <param name="matrix"> /// The <see cref="Matrix"/> upon which this preconditioner is based. Note that the /// method takes a general matrix type. However internally the data is stored /// as a sparse matrix. Therefore it is not recommended to pass a dense matrix. /// </param> /// <exception cref="ArgumentNullException"> If <paramref name="matrix"/> is <see langword="null" />.</exception> /// <exception cref="ArgumentException">If <paramref name="matrix"/> is not a square matrix.</exception> public void Initialize(Matrix matrix) { if (matrix == null) { throw new ArgumentNullException("matrix"); } if (matrix.RowCount != matrix.ColumnCount) { throw new ArgumentException(Resources.ArgumentMatrixSquare, "matrix"); } var sparseMatrix = (matrix is SparseMatrix) ? matrix as SparseMatrix : new SparseMatrix(matrix); // The creation of the preconditioner follows the following algorithm. // spaceLeft = lfilNnz * nnz(A) // for i = 1, .. , n // { // w = a(i,*) // for j = 1, .. , i - 1 // { // if (w(j) != 0) // { // w(j) = w(j) / a(j,j) // if (w(j) < dropTol) // { // w(j) = 0; // } // if (w(j) != 0) // { // w = w - w(j) * U(j,*) // } // } // } // // for j = i, .. ,n // { // if w(j) <= dropTol * ||A(i,*)|| // { // w(j) = 0 // } // } // // spaceRow = spaceLeft / (n - i + 1) // Determine the space for this row // lfil = spaceRow / 2 // space for this row of L // l(i,j) = w(j) for j = 1, .. , i -1 // only the largest lfil elements // // lfil = spaceRow - nnz(L(i,:)) // space for this row of U // u(i,j) = w(j) for j = i, .. , n // only the largest lfil - 1 elements // w = 0 // // if max(U(i,i + 1: n)) > U(i,i) / pivTol then // pivot if necessary // { // pivot by swapping the max and the diagonal entries // Update L, U // Update P // } // spaceLeft = spaceLeft - nnz(L(i,:)) - nnz(U(i,:)) // } // Create the lower triangular matrix _lower = new SparseMatrix(sparseMatrix.RowCount); // Create the upper triangular matrix and copy the values _upper = new SparseMatrix(sparseMatrix.RowCount); // Create the pivot array _pivots = new int[sparseMatrix.RowCount]; for (var i = 0; i < _pivots.Length; i++) { _pivots[i] = i; } Vector workVector = new DenseVector(sparseMatrix.RowCount); Vector rowVector = new DenseVector(sparseMatrix.ColumnCount); var indexSorting = new int[sparseMatrix.RowCount]; // spaceLeft = lfilNnz * nnz(A) var spaceLeft = (int)_fillLevel * sparseMatrix.NonZerosCount; // for i = 1, .. , n for (var i = 0; i < sparseMatrix.RowCount; i++) { // w = a(i,*) sparseMatrix.Row(i, workVector); // pivot the row PivotRow(workVector); var vectorNorm = workVector.Norm(Double.PositiveInfinity); // for j = 1, .. , i - 1) for (var j = 0; j < i; j++) { // if (w(j) != 0) // { // w(j) = w(j) / a(j,j) // if (w(j) < dropTol) // { // w(j) = 0; // } // if (w(j) != 0) // { // w = w - w(j) * U(j,*) // } if (workVector[j] != 0.0) { // Calculate the multiplication factors that go into the L matrix workVector[j] = workVector[j] / _upper[j, j]; if (workVector[j].Magnitude < _dropTolerance) { workVector[j] = 0.0; } // Calculate the addition factor if (workVector[j] != 0.0) { // vector update all in one go _upper.Row(j, rowVector); // zero out columnVector[k] because we don't need that // one anymore for k = 0 to k = j for (var k = 0; k <= j; k++) { rowVector[k] = 0.0; } rowVector.Multiply(workVector[j], rowVector); workVector.Subtract(rowVector, workVector); } } } // for j = i, .. ,n for (var j = i; j < sparseMatrix.RowCount; j++) { // if w(j) <= dropTol * ||A(i,*)|| // { // w(j) = 0 // } if (workVector[j].Magnitude <= _dropTolerance * vectorNorm.Real) { workVector[j] = 0.0; } } // spaceRow = spaceLeft / (n - i + 1) // Determine the space for this row var spaceRow = spaceLeft / (sparseMatrix.RowCount - i + 1); // lfil = spaceRow / 2 // space for this row of L var fillLevel = spaceRow / 2; FindLargestItems(0, i - 1, indexSorting, workVector); // l(i,j) = w(j) for j = 1, .. , i -1 // only the largest lfil elements var lowerNonZeroCount = 0; var count = 0; for (var j = 0; j < i; j++) { if ((count > fillLevel) || (indexSorting[j] == -1)) { break; } _lower[i, indexSorting[j]] = workVector[indexSorting[j]]; count += 1; lowerNonZeroCount += 1; } FindLargestItems(i + 1, sparseMatrix.RowCount - 1, indexSorting, workVector); // lfil = spaceRow - nnz(L(i,:)) // space for this row of U fillLevel = spaceRow - lowerNonZeroCount; // u(i,j) = w(j) for j = i + 1, .. , n // only the largest lfil - 1 elements var upperNonZeroCount = 0; count = 0; for (var j = 0; j < sparseMatrix.RowCount - i; j++) { if ((count > fillLevel - 1) || (indexSorting[j] == -1)) { break; } _upper[i, indexSorting[j]] = workVector[indexSorting[j]]; count += 1; upperNonZeroCount += 1; } // Simply copy the diagonal element. Next step is to see if we pivot _upper[i, i] = workVector[i]; // if max(U(i,i + 1: n)) > U(i,i) / pivTol then // pivot if necessary // { // pivot by swapping the max and the diagonal entries // Update L, U // Update P // } // Check if we really need to pivot. If (i+1) >=(mCoefficientMatrix.Rows -1) then // we are working on the last row. That means that there is only one number // And pivoting is useless. Also the indexSorting array will only contain // -1 values. if ((i + 1) < (sparseMatrix.RowCount - 1)) { if (workVector[i].Magnitude < _pivotTolerance * workVector[indexSorting[0]].Magnitude) { // swap columns of u (which holds the values of A in the // sections that haven't been partitioned yet. SwapColumns(_upper, i, indexSorting[0]); // Update P var temp = _pivots[i]; _pivots[i] = _pivots[indexSorting[0]]; _pivots[indexSorting[0]] = temp; } } // spaceLeft = spaceLeft - nnz(L(i,:)) - nnz(U(i,:)) spaceLeft -= lowerNonZeroCount + upperNonZeroCount; } for (var i = 0; i < _lower.RowCount; i++) { _lower[i, i] = 1.0; } }
/// <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 matrix, Vector input, Vector 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 new ArgumentException(Resources.ArgumentMatrixDimensions); } // Initialize the solver fields // Set the convergence monitor if (_iterator == null) { _iterator = Iterator.CreateDefault(); } if (_preconditioner == null) { _preconditioner = new UnitPreconditioner(); } _preconditioner.Initialize(matrix); var d = new DenseVector(input.Count); var r = new DenseVector(input); var uodd = new DenseVector(input.Count); var ueven = new DenseVector(input.Count); var v = new DenseVector(input.Count); var pseudoResiduals = new DenseVector(input); var x = new DenseVector(input.Count); var yodd = new DenseVector(input.Count); var yeven = new DenseVector(input); // Temp vectors var temp = new DenseVector(input.Count); var temp1 = new DenseVector(input.Count); var temp2 = new DenseVector(input.Count); // Initialize var startNorm = input.Norm(2); // Define the scalars Complex alpha = 0; Complex eta = 0; double theta = 0; var tau = startNorm.Real; Complex rho = tau * tau; // Calculate the initial values for v // M temp = yEven _preconditioner.Approximate(yeven, temp); // v = A temp matrix.Multiply(temp, v); // Set uOdd v.CopyTo(ueven); // Start the iteration var iterationNumber = 0; while (ShouldContinue(iterationNumber, result, input, pseudoResiduals)) { // First part of the step, the even bit if (IsEven(iterationNumber)) { // sigma = (v, r) var sigma = v.DotProduct(r.Conjugate()); if (sigma.Real.AlmostEqual(0, 1) && sigma.Imaginary.AlmostEqual(0, 1)) { // FAIL HERE _iterator.IterationCancelled(); break; } // alpha = rho / sigma alpha = rho / sigma; // yOdd = yEven - alpha * v v.Multiply(-alpha, temp1); yeven.Add(temp1, yodd); // Solve M temp = yOdd _preconditioner.Approximate(yodd, temp); // uOdd = A temp matrix.Multiply(temp, uodd); } // The intermediate step which is equal for both even and // odd iteration steps. // Select the correct vector var uinternal = IsEven(iterationNumber) ? ueven : uodd; var yinternal = IsEven(iterationNumber) ? yeven : yodd; // pseudoResiduals = pseudoResiduals - alpha * uOdd uinternal.Multiply(-alpha, temp1); pseudoResiduals.Add(temp1, temp2); temp2.CopyTo(pseudoResiduals); // d = yOdd + theta * theta * eta / alpha * d d.Multiply(theta * theta * eta / alpha, temp); yinternal.Add(temp, d); // theta = ||pseudoResiduals||_2 / tau theta = pseudoResiduals.Norm(2).Real / tau; var c = 1 / Math.Sqrt(1 + (theta * theta)); // tau = tau * theta * c tau *= theta * c; // eta = c^2 * alpha eta = c * c * alpha; // x = x + eta * d d.Multiply(eta, temp1); x.Add(temp1, temp2); temp2.CopyTo(x); // Check convergence and see if we can bail if (!ShouldContinue(iterationNumber, result, input, pseudoResiduals)) { // Calculate the real values _preconditioner.Approximate(x, result); // Calculate the true residual. Use the temp vector for that // so that we don't pollute the pseudoResidual vector for no // good reason. CalculateTrueResidual(matrix, temp, result, input); // Now recheck the convergence if (!ShouldContinue(iterationNumber, result, input, temp)) { // We're all good now. return; } } // The odd step if (!IsEven(iterationNumber)) { if (rho.Real.AlmostEqual(0, 1) && rho.Imaginary.AlmostEqual(0, 1)) { // FAIL HERE _iterator.IterationCancelled(); break; } var rhoNew = pseudoResiduals.DotProduct(r.Conjugate()); var beta = rhoNew / rho; // Update rho for the next loop rho = rhoNew; // yOdd = pseudoResiduals + beta * yOdd yodd.Multiply(beta, temp1); pseudoResiduals.Add(temp1, yeven); // Solve M temp = yOdd _preconditioner.Approximate(yeven, temp); // uOdd = A temp matrix.Multiply(temp, ueven); // v = uEven + beta * (uOdd + beta * v) v.Multiply(beta, temp1); uodd.Add(temp1, temp); temp.Multiply(beta, temp1); ueven.Add(temp1, v); } // Calculate the real values _preconditioner.Approximate(x, result); iterationNumber++; } }
/// <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 matrix, Vector input, Vector 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(); } _preconditioner.Initialize(matrix); var d = new DenseVector(input.Count); var r = new DenseVector(input); var uodd = new DenseVector(input.Count); var ueven = new DenseVector(input.Count); var v = new DenseVector(input.Count); var pseudoResiduals = new DenseVector(input); var x = new DenseVector(input.Count); var yodd = new DenseVector(input.Count); var yeven = new DenseVector(input); // Temp vectors var temp = new DenseVector(input.Count); var temp1 = new DenseVector(input.Count); var temp2 = new DenseVector(input.Count); // Initialize var startNorm = input.Norm(2); // Define the scalars double alpha = 0; double eta = 0; double theta = 0; var tau = startNorm; var rho = tau * tau; // Calculate the initial values for v // M temp = yEven _preconditioner.Approximate(yeven, temp); // v = A temp matrix.Multiply(temp, v); // Set uOdd v.CopyTo(ueven); // Start the iteration var iterationNumber = 0; while (ShouldContinue(iterationNumber, result, input, pseudoResiduals)) { // First part of the step, the even bit if (IsEven(iterationNumber)) { // sigma = (v, r) var sigma = v.DotProduct(r); if (sigma.AlmostEqual(0, 1)) { // FAIL HERE _iterator.IterationCancelled(); break; } // alpha = rho / sigma alpha = rho / sigma; // yOdd = yEven - alpha * v v.Multiply(-alpha, temp1); yeven.Add(temp1, yodd); // Solve M temp = yOdd _preconditioner.Approximate(yodd, temp); // uOdd = A temp matrix.Multiply(temp, uodd); } // The intermediate step which is equal for both even and // odd iteration steps. // Select the correct vector var uinternal = IsEven(iterationNumber) ? ueven : uodd; var yinternal = IsEven(iterationNumber) ? yeven : yodd; // pseudoResiduals = pseudoResiduals - alpha * uOdd uinternal.Multiply(-alpha, temp1); pseudoResiduals.Add(temp1, temp2); temp2.CopyTo(pseudoResiduals); // d = yOdd + theta * theta * eta / alpha * d d.Multiply(theta * theta * eta / alpha, temp); yinternal.Add(temp, d); // theta = ||pseudoResiduals||_2 / tau theta = pseudoResiduals.Norm(2) / tau; var c = 1 / Math.Sqrt(1 + (theta * theta)); // tau = tau * theta * c tau *= theta * c; // eta = c^2 * alpha eta = c * c * alpha; // x = x + eta * d d.Multiply(eta, temp1); x.Add(temp1, temp2); temp2.CopyTo(x); // Check convergence and see if we can bail if (!ShouldContinue(iterationNumber, result, input, pseudoResiduals)) { // Calculate the real values _preconditioner.Approximate(x, result); // Calculate the true residual. Use the temp vector for that // so that we don't pollute the pseudoResidual vector for no // good reason. CalculateTrueResidual(matrix, temp, result, input); // Now recheck the convergence if (!ShouldContinue(iterationNumber, result, input, temp)) { // We're all good now. return; } } // The odd step if (!IsEven(iterationNumber)) { if (rho.AlmostEqual(0, 1)) { // FAIL HERE _iterator.IterationCancelled(); break; } var rhoNew = pseudoResiduals.DotProduct(r); var beta = rhoNew / rho; // Update rho for the next loop rho = rhoNew; // yOdd = pseudoResiduals + beta * yOdd yodd.Multiply(beta, temp1); pseudoResiduals.Add(temp1, yeven); // Solve M temp = yOdd _preconditioner.Approximate(yeven, temp); // uOdd = A temp matrix.Multiply(temp, ueven); // v = uEven + beta * (uOdd + beta * v) v.Multiply(beta, temp1); uodd.Add(temp1, temp); temp.Multiply(beta, temp1); ueven.Add(temp1, v); } // Calculate the real values _preconditioner.Approximate(x, result); iterationNumber++; } }