/// <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 <see cref="Matrix"/>, <c>A</c>.</param> /// <param name="input">The solution <see cref="Vector"/>, <c>b</c>.</param> /// <param name="result">The result <see cref="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; // Parameters 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, result); } // Initialize the solver fields // Set the convergence monitor if (_iterator == null) { _iterator = Iterator.CreateDefault(); } if (_preconditioner == null) { _preconditioner = new UnitPreconditioner(); } _preconditioner.Initialize(matrix); // Compute r_0 = b - Ax_0 for some initial guess x_0 // In this case we take x_0 = vector // This is basically a SAXPY so it could be made a lot faster Vector residuals = new DenseVector(matrix.RowCount); CalculateTrueResidual(matrix, residuals, result, input); // Choose r~ (for example, r~ = r_0) var tempResiduals = residuals.Clone(); // create seven temporary vectors needed to hold temporary // coefficients. All vectors are mangled in each iteration. // These are defined here to prevent stressing the garbage collector Vector vecP = new DenseVector(residuals.Count); Vector vecPdash = new DenseVector(residuals.Count); Vector nu = new DenseVector(residuals.Count); Vector vecS = new DenseVector(residuals.Count); Vector vecSdash = new DenseVector(residuals.Count); Vector temp = new DenseVector(residuals.Count); Vector temp2 = new DenseVector(residuals.Count); // create some temporary double variables that are needed // to hold values in between iterations Complex currentRho = 0; Complex alpha = 0; Complex omega = 0; var iterationNumber = 0; while (ShouldContinue(iterationNumber, result, input, residuals)) { // rho_(i-1) = r~^T r_(i-1) // dotproduct r~ and r_(i-1) var oldRho = currentRho; currentRho = tempResiduals.DotProduct(residuals); // if (rho_(i-1) == 0) // METHOD FAILS // If rho is only 1 ULP from zero then we fail. if (currentRho.Real.AlmostEqual(0, 1) && currentRho.Imaginary.AlmostEqual(0, 1)) { // Rho-type breakdown throw new Exception("Iterative solver experience a numerical break down"); } if (iterationNumber != 0) { // beta_(i-1) = (rho_(i-1)/rho_(i-2))(alpha_(i-1)/omega(i-1)) var beta = (currentRho / oldRho) * (alpha / omega); // p_i = r_(i-1) + beta_(i-1)(p_(i-1) - omega_(i-1) * nu_(i-1)) nu.Multiply(-omega, temp); vecP.Add(temp, temp2); temp2.CopyTo(vecP); vecP.Multiply(beta, vecP); vecP.Add(residuals, temp2); temp2.CopyTo(vecP); } else { // p_i = r_(i-1) residuals.CopyTo(vecP); } // SOLVE Mp~ = p_i // M = preconditioner _preconditioner.Approximate(vecP, vecPdash); // nu_i = Ap~ matrix.Multiply(vecPdash, nu); // alpha_i = rho_(i-1)/ (r~^T nu_i) = rho / dotproduct(r~ and nu_i) alpha = currentRho * 1 / tempResiduals.DotProduct(nu); // s = r_(i-1) - alpha_i nu_i nu.Multiply(-alpha, temp); residuals.Add(temp, vecS); // Check if we're converged. If so then stop. Otherwise continue; // Calculate the temporary result. // Be careful not to change any of the temp vectors, except for // temp. Others will be used in the calculation later on. // x_i = x_(i-1) + alpha_i * p^_i + s^_i vecPdash.Multiply(alpha, temp); temp.Add(vecSdash, temp2); temp2.CopyTo(temp); temp.Add(result, temp2); temp2.CopyTo(temp); // Check convergence and stop if we are converged. if (!ShouldContinue(iterationNumber, temp, input, vecS)) { temp.CopyTo(result); // Calculate the true residual CalculateTrueResidual(matrix, residuals, result, input); // Now recheck the convergence if (!ShouldContinue(iterationNumber, result, input, residuals)) { // We're all good now. return; } // Continue the calculation iterationNumber++; continue; } // SOLVE Ms~ = s _preconditioner.Approximate(vecS, vecSdash); // temp = As~ matrix.Multiply(vecSdash, temp); // omega_i = temp^T s / temp^T temp omega = temp.DotProduct(vecS) / temp.DotProduct(temp); // x_i = x_(i-1) + alpha_i p^ + omega_i s^ temp.Multiply(-omega, residuals); residuals.Add(vecS, temp2); temp2.CopyTo(residuals); vecSdash.Multiply(omega, temp); result.Add(temp, temp2); temp2.CopyTo(result); vecPdash.Multiply(alpha, temp); result.Add(temp, temp2); temp2.CopyTo(result); // for continuation it is necessary that omega_i != 0.0 // If omega is only 1 ULP from zero then we fail. if (omega.Real.AlmostEqual(0, 1) && omega.Imaginary.AlmostEqual(0, 1)) { // Omega-type breakdown throw new Exception("Iterative solver experience a numerical break down"); } if (!ShouldContinue(iterationNumber, result, input, residuals)) { // Recalculate the residuals and go round again. This is done to ensure that // we have the proper residuals. // The residual calculation based on omega_i * s can be off by a factor 10. So here // we calculate the real residual (which can be expensive) but we only do it if we're // sufficiently close to the finish. CalculateTrueResidual(matrix, residuals, result, input); } 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++; } }
/// <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); // x_0 is initial guess // Take x_0 = 0 Vector xtemp = new DenseVector(input.Count); // r_0 = b - Ax_0 // This is basically a SAXPY so it could be made a lot faster Vector residuals = new DenseVector(matrix.RowCount); CalculateTrueResidual(matrix, residuals, xtemp, input); // Define the temporary scalars float beta = 0; float sigma; // Define the temporary vectors // rDash_0 = r_0 Vector rdash = new DenseVector(residuals); // t_-1 = 0 Vector t = new DenseVector(residuals.Count); Vector t0 = new DenseVector(residuals.Count); // w_-1 = 0 Vector w = new DenseVector(residuals.Count); // Define the remaining temporary vectors Vector c = new DenseVector(residuals.Count); Vector p = new DenseVector(residuals.Count); Vector s = new DenseVector(residuals.Count); Vector u = new DenseVector(residuals.Count); Vector y = new DenseVector(residuals.Count); Vector z = new DenseVector(residuals.Count); Vector temp = new DenseVector(residuals.Count); Vector temp2 = new DenseVector(residuals.Count); Vector temp3 = new DenseVector(residuals.Count); // for (k = 0, 1, .... ) var iterationNumber = 0; while (ShouldContinue(iterationNumber, xtemp, input, residuals)) { // 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.AlmostEqual(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; 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.AlmostEqual(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.AlmostEqual(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 (!ShouldContinue(iterationNumber, result, input, residuals)) { // 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++; } }
/// <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 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 = (float)input.Norm(2); // Define the scalars float alpha = 0; float eta = 0; float 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 = (float)pseudoResiduals.Norm(2) / 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.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++; } }
/// <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); // Choose an initial guess x_0 // Take x_0 = 0 Vector 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 Vector residuals = new DenseVector(matrix.RowCount); CalculateTrueResidual(matrix, residuals, xtemp, input); // Define the temporary values var c = new Complex[k]; // Define the temporary vectors Vector gtemp = new DenseVector(residuals.Count); Vector u = new DenseVector(residuals.Count); Vector utemp = new DenseVector(residuals.Count); Vector temp = new DenseVector(residuals.Count); Vector temp1 = new DenseVector(residuals.Count); Vector temp2 = new DenseVector(residuals.Count); Vector zd = new DenseVector(residuals.Count); Vector zg = new DenseVector(residuals.Count); Vector 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].Real.AlmostEqual(0, 1) && c[k - 1].Imaginary.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.Real.AlmostEqual(0, 1) && rho.Imaginary.AlmostEqual(0, 1)) { rho = 1.0; } 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 Complex 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.Real.AlmostEqual(0, 1) && beta.Imaginary.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].Real.AlmostEqual(0, 1) && c[i].Imaginary.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); }
/// <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, "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++; } }
/// <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 <double> matrix, Vector <double> input, Vector <double> 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); // Choose an initial guess x_0 // Take x_0 = 0 Vector <double> 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 Vector <double> residuals = new DenseVector(matrix.RowCount); CalculateTrueResidual(matrix, residuals, xtemp, input); // Define the temporary scalars var c = new double[k]; // Define the temporary vectors Vector <double> gtemp = new DenseVector(residuals.Count); Vector <double> u = new DenseVector(residuals.Count); Vector <double> utemp = new DenseVector(residuals.Count); Vector <double> temp = new DenseVector(residuals.Count); Vector <double> temp1 = new DenseVector(residuals.Count); Vector <double> temp2 = new DenseVector(residuals.Count); Vector <double> zd = new DenseVector(residuals.Count); Vector <double> zg = new DenseVector(residuals.Count); Vector <double> 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.0; } 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 double 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); }
/// <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 <see cref="Matrix{T}"/>, <c>A</c>.</param> /// <param name="input">The solution <see cref="Vector{T}"/>, <c>b</c>.</param> /// <param name="result">The result <see cref="Vector{T}"/>, <c>x</c>.</param> public void Solve(Matrix <Complex> matrix, Vector <Complex> input, Vector <Complex> 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; // Parameters 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); // Compute r_0 = b - Ax_0 for some initial guess x_0 // In this case we take x_0 = vector // This is basically a SAXPY so it could be made a lot faster Vector <Complex> residuals = new DenseVector(matrix.RowCount); CalculateTrueResidual(matrix, residuals, result, input); // Choose r~ (for example, r~ = r_0) var tempResiduals = residuals.Clone(); // create seven temporary vectors needed to hold temporary // coefficients. All vectors are mangled in each iteration. // These are defined here to prevent stressing the garbage collector Vector <Complex> vecP = new DenseVector(residuals.Count); Vector <Complex> vecPdash = new DenseVector(residuals.Count); Vector <Complex> nu = new DenseVector(residuals.Count); Vector <Complex> vecS = new DenseVector(residuals.Count); Vector <Complex> vecSdash = new DenseVector(residuals.Count); Vector <Complex> temp = new DenseVector(residuals.Count); Vector <Complex> temp2 = new DenseVector(residuals.Count); // create some temporary double variables that are needed // to hold values in between iterations Complex currentRho = 0; Complex alpha = 0; Complex omega = 0; var iterationNumber = 0; while (ShouldContinue(iterationNumber, result, input, residuals)) { // rho_(i-1) = r~^T r_(i-1) // dotproduct r~ and r_(i-1) var oldRho = currentRho; currentRho = tempResiduals.DotProduct(residuals); // if (rho_(i-1) == 0) // METHOD FAILS // If rho is only 1 ULP from zero then we fail. if (currentRho.Real.AlmostEqual(0, 1) && currentRho.Imaginary.AlmostEqual(0, 1)) { // Rho-type breakdown throw new Exception("Iterative solver experience a numerical break down"); } if (iterationNumber != 0) { // beta_(i-1) = (rho_(i-1)/rho_(i-2))(alpha_(i-1)/omega(i-1)) var beta = (currentRho / oldRho) * (alpha / omega); // p_i = r_(i-1) + beta_(i-1)(p_(i-1) - omega_(i-1) * nu_(i-1)) nu.Multiply(-omega, temp); vecP.Add(temp, temp2); temp2.CopyTo(vecP); vecP.Multiply(beta, vecP); vecP.Add(residuals, temp2); temp2.CopyTo(vecP); } else { // p_i = r_(i-1) residuals.CopyTo(vecP); } // SOLVE Mp~ = p_i // M = preconditioner _preconditioner.Approximate(vecP, vecPdash); // nu_i = Ap~ matrix.Multiply(vecPdash, nu); // alpha_i = rho_(i-1)/ (r~^T nu_i) = rho / dotproduct(r~ and nu_i) alpha = currentRho * 1 / tempResiduals.DotProduct(nu); // s = r_(i-1) - alpha_i nu_i nu.Multiply(-alpha, temp); residuals.Add(temp, vecS); // Check if we're converged. If so then stop. Otherwise continue; // Calculate the temporary result. // Be careful not to change any of the temp vectors, except for // temp. Others will be used in the calculation later on. // x_i = x_(i-1) + alpha_i * p^_i + s^_i vecPdash.Multiply(alpha, temp); temp.Add(vecSdash, temp2); temp2.CopyTo(temp); temp.Add(result, temp2); temp2.CopyTo(temp); // Check convergence and stop if we are converged. if (!ShouldContinue(iterationNumber, temp, input, vecS)) { temp.CopyTo(result); // Calculate the true residual CalculateTrueResidual(matrix, residuals, result, input); // Now recheck the convergence if (!ShouldContinue(iterationNumber, result, input, residuals)) { // We're all good now. return; } // Continue the calculation iterationNumber++; continue; } // SOLVE Ms~ = s _preconditioner.Approximate(vecS, vecSdash); // temp = As~ matrix.Multiply(vecSdash, temp); // omega_i = temp^T s / temp^T temp omega = temp.DotProduct(vecS) / temp.DotProduct(temp); // x_i = x_(i-1) + alpha_i p^ + omega_i s^ temp.Multiply(-omega, residuals); residuals.Add(vecS, temp2); temp2.CopyTo(residuals); vecSdash.Multiply(omega, temp); result.Add(temp, temp2); temp2.CopyTo(result); vecPdash.Multiply(alpha, temp); result.Add(temp, temp2); temp2.CopyTo(result); // for continuation it is necessary that omega_i != 0.0 // If omega is only 1 ULP from zero then we fail. if (omega.Real.AlmostEqual(0, 1) && omega.Imaginary.AlmostEqual(0, 1)) { // Omega-type breakdown throw new Exception("Iterative solver experience a numerical break down"); } if (!ShouldContinue(iterationNumber, result, input, residuals)) { // Recalculate the residuals and go round again. This is done to ensure that // we have the proper residuals. // The residual calculation based on omega_i * s can be off by a factor 10. So here // we calculate the real residual (which can be expensive) but we only do it if we're // sufficiently close to the finish. CalculateTrueResidual(matrix, residuals, result, input); } 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> /// <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); var d = new DenseVector(input.Count); var r = DenseVector.OfVector(input); var uodd = new DenseVector(input.Count); var ueven = new DenseVector(input.Count); var v = new DenseVector(input.Count); var pseudoResiduals = DenseVector.OfVector(input); var x = new DenseVector(input.Count); var yodd = new DenseVector(input.Count); var yeven = DenseVector.OfVector(input); // Temp vectors var temp = new DenseVector(input.Count); var temp1 = new DenseVector(input.Count); var temp2 = new DenseVector(input.Count); // Define the scalars float alpha = 0; float eta = 0; float theta = 0; // Initialize var tau = (float)input.L2Norm(); 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 (iterator.DetermineStatus(iterationNumber, result, input, pseudoResiduals) == IterationStatus.Continue) { // First part of the step, the even bit if (IsEven(iterationNumber)) { // sigma = (v, r) var sigma = v.DotProduct(r); if (sigma.AlmostEqualNumbersBetween(0, 1)) { // FAIL HERE iterator.Cancel(); 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 = (float)pseudoResiduals.L2Norm() / 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 (iterator.DetermineStatus(iterationNumber, result, input, pseudoResiduals) != IterationStatus.Continue) { // 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 (iterator.DetermineStatus(iterationNumber, result, input, temp) != IterationStatus.Continue) { // We're all good now. return; } } // The odd step if (!IsEven(iterationNumber)) { if (rho.AlmostEqualNumbersBetween(0, 1)) { // FAIL HERE iterator.Cancel(); 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++; } }
/// <summary> /// Fitting a power function like, y = a * x ^ b. /// </summary> /// <param name="guess">A guess for a and b.</param> /// <param name="pointCount">Number of data points.</param> /// <param name="dataX">X-coordinates of the data points.</param> /// <param name="dataY">Y-coordinates of the data points.</param> /// <returns>The power value for this fitting.</returns> public double Fit( double[] guess, int pointCount, Vector <double> dataX, Vector <double> dataY) { int n = guess.Length; int iterationsCount = 0; Vector <double> parametersCurrent = new DenseVector(guess); Vector <double> parametersNew = new DenseVector(n); double valueCurrent; double valueNew; GetObjectiveValue( pointCount, dataX, dataY, parametersCurrent, out valueCurrent); while (true) { Matrix <double> jacobian = new DenseMatrix(pointCount, n); Vector <double> residual = new DenseVector(pointCount); GetObjectiveJacobian( pointCount, dataX, dataY, parametersCurrent, ref jacobian); GetResidualVector( pointCount, dataX, dataY, parametersCurrent, ref residual); Vector <double> step = jacobian.Transpose().Multiply(jacobian).Cholesky().Solve(jacobian.Transpose().Multiply(residual)); parametersCurrent.Subtract(step, parametersNew); GetObjectiveValue( pointCount, dataX, dataY, parametersNew, out valueNew); iterationsCount++; if (ShouldTerminate( valueCurrent, valueNew, iterationsCount, parametersCurrent, parametersNew)) { break; } parametersNew.CopyTo(parametersCurrent); valueCurrent = valueNew; } return(parametersNew[1]); }
/// <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 <see cref="Matrix"/>, <c>A</c>.</param> /// <param name="input">The solution <see cref="Vector"/>, <c>b</c>.</param> /// <param name="result">The result <see cref="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 <Numerics.Complex32> matrix, Vector <Numerics.Complex32> input, Vector <Numerics.Complex32> result, Iterator <Numerics.Complex32> iterator, IPreconditioner <Numerics.Complex32> 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 <Numerics.Complex32>(); } if (preconditioner == null) { preconditioner = new UnitPreconditioner <Numerics.Complex32>(); } preconditioner.Initialize(matrix); // Compute r_0 = b - Ax_0 for some initial guess x_0 // In this case we take x_0 = vector // This is basically a SAXPY so it could be made a lot faster var residuals = new DenseVector(matrix.RowCount); CalculateTrueResidual(matrix, residuals, result, input); // Choose r~ (for example, r~ = r_0) var tempResiduals = residuals.Clone(); // create seven temporary vectors needed to hold temporary // coefficients. All vectors are mangled in each iteration. // These are defined here to prevent stressing the garbage collector var vecP = new DenseVector(residuals.Count); var vecPdash = new DenseVector(residuals.Count); var nu = new DenseVector(residuals.Count); var vecS = new DenseVector(residuals.Count); var vecSdash = new DenseVector(residuals.Count); var temp = new DenseVector(residuals.Count); var temp2 = new DenseVector(residuals.Count); // create some temporary float variables that are needed // to hold values in between iterations Numerics.Complex32 currentRho = 0; Numerics.Complex32 alpha = 0; Numerics.Complex32 omega = 0; var iterationNumber = 0; while (iterator.DetermineStatus(iterationNumber, result, input, residuals) == IterationStatus.Continue) { // rho_(i-1) = r~^T r_(i-1) // dotproduct r~ and r_(i-1) var oldRho = currentRho; currentRho = tempResiduals.ConjugateDotProduct(residuals); // if (rho_(i-1) == 0) // METHOD FAILS // If rho is only 1 ULP from zero then we fail. if (currentRho.Real.AlmostEqualNumbersBetween(0, 1) && currentRho.Imaginary.AlmostEqualNumbersBetween(0, 1)) { // Rho-type breakdown throw new NumericalBreakdownException(); } if (iterationNumber != 0) { // beta_(i-1) = (rho_(i-1)/rho_(i-2))(alpha_(i-1)/omega(i-1)) var beta = (currentRho / oldRho) * (alpha / omega); // p_i = r_(i-1) + beta_(i-1)(p_(i-1) - omega_(i-1) * nu_(i-1)) nu.Multiply(-omega, temp); vecP.Add(temp, temp2); temp2.CopyTo(vecP); vecP.Multiply(beta, vecP); vecP.Add(residuals, temp2); temp2.CopyTo(vecP); } else { // p_i = r_(i-1) residuals.CopyTo(vecP); } // SOLVE Mp~ = p_i // M = preconditioner preconditioner.Approximate(vecP, vecPdash); // nu_i = Ap~ matrix.Multiply(vecPdash, nu); // alpha_i = rho_(i-1)/ (r~^T nu_i) = rho / dotproduct(r~ and nu_i) alpha = (currentRho * 1) / tempResiduals.ConjugateDotProduct(nu); // s = r_(i-1) - alpha_i nu_i nu.Multiply(-alpha, temp); residuals.Add(temp, vecS); // Check if we're converged. If so then stop. Otherwise continue; // Calculate the temporary result. // Be careful not to change any of the temp vectors, except for // temp. Others will be used in the calculation later on. // x_i = x_(i-1) + alpha_i * p^_i + s^_i vecPdash.Multiply(alpha, temp); temp.Add(vecSdash, temp2); temp2.CopyTo(temp); temp.Add(result, temp2); temp2.CopyTo(temp); // Check convergence and stop if we are converged. if (iterator.DetermineStatus(iterationNumber, temp, input, vecS) != IterationStatus.Continue) { temp.CopyTo(result); // Calculate the true residual CalculateTrueResidual(matrix, residuals, result, input); // Now recheck the convergence if (iterator.DetermineStatus(iterationNumber, result, input, residuals) != IterationStatus.Continue) { // We're all good now. return; } // Continue the calculation iterationNumber++; continue; } // SOLVE Ms~ = s preconditioner.Approximate(vecS, vecSdash); // temp = As~ matrix.Multiply(vecSdash, temp); // omega_i = temp^T s / temp^T temp omega = temp.ConjugateDotProduct(vecS) / temp.ConjugateDotProduct(temp); // x_i = x_(i-1) + alpha_i p^ + omega_i s^ temp.Multiply(-omega, residuals); residuals.Add(vecS, temp2); temp2.CopyTo(residuals); vecSdash.Multiply(omega, temp); result.Add(temp, temp2); temp2.CopyTo(result); vecPdash.Multiply(alpha, temp); result.Add(temp, temp2); temp2.CopyTo(result); // for continuation it is necessary that omega_i != 0.0f // If omega is only 1 ULP from zero then we fail. if (omega.Real.AlmostEqualNumbersBetween(0, 1) && omega.Imaginary.AlmostEqualNumbersBetween(0, 1)) { // Omega-type breakdown throw new NumericalBreakdownException(); } 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. // The residual calculation based on omega_i * s can be off by a factor 10. So here // we calculate the real residual (which can be expensive) but we only do it if we're // sufficiently close to the finish. CalculateTrueResidual(matrix, residuals, result, input); } iterationNumber++; } }
public void Fit(IList <Point> points, ref double alpha, ref double beta, ref double gamma) { var model = new Model(); var minimumDeltaValue = 1; var minimumDeltaParameters = 1; var maximumIterations = 10; var pointCount = points.Count; Vector <double> dataX = new DenseVector(points.Select(p => p.X).ToArray()); Vector <double> dataY = new DenseVector(points.Select(p => p.Y).ToArray()); var iterations = new List <Vector <double> >(); var guess = new[] { alpha, beta, gamma }; Vector <double> parametersCurrent = new DenseVector(guess); Vector <double> parametersNew = new DenseVector(parametersCurrent.Count); var valueCurrent = GetObjectiveValue(model, pointCount, dataX, dataY, parametersCurrent[0], parametersCurrent[1], parametersCurrent[2]); while (true) { var jacobian = GetObjectiveJacobian(model, pointCount, dataX, parametersCurrent[0], parametersCurrent[1], parametersCurrent[2]); var residual = model.GetResidualVector(pointCount, dataX, dataY, parametersCurrent[0], parametersCurrent[1], parametersCurrent[2]); try { var jacobianT = jacobian.Transpose(); var res = jacobianT.Multiply(residual); var jjT = jacobian.Transpose().Multiply(jacobian); var cholesky = jjT.Cholesky(); var step = cholesky.Solve(res); parametersCurrent.Subtract(step, parametersNew); } catch (Exception) { alpha = 0; beta = 0; gamma = 0; return; } var valueNew = GetObjectiveValue(model, pointCount, dataX, dataY, parametersCurrent[0], parametersCurrent[1], parametersCurrent[2]); iterations.Add(parametersNew.Clone()); if (ShouldTerminate(valueCurrent, valueNew, iterations.Count, parametersCurrent, parametersNew, minimumDeltaValue, minimumDeltaParameters, maximumIterations)) { break; } parametersNew.CopyTo(parametersCurrent); valueCurrent = valueNew; } alpha = parametersCurrent[0]; beta = parametersCurrent[1]; gamma = parametersCurrent[2]; }