/// <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 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 Complex32 alpha = 0; Complex32 eta = 0; float theta = 0; var tau = startNorm.Real; Complex32 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 / (float)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++; } }