/// <summary> /// Construct an initial simplex, given starting guesses for the constants, and /// initial step sizes for each dimension /// </summary> private static Vector <double>[] InitializeVertices(SimplexConstant[] simplexConstants) { int numDimensions = simplexConstants.Length; Vector <double>[] vertices = new Vector <double> [numDimensions + 1]; // define one point of the simplex as the given initial guesses Vector <double> p0 = new DenseVector(numDimensions); for (int i = 0; i < numDimensions; i++) { p0[i] = simplexConstants[i].Value; } // now fill in the vertices, creating the additional points as: // P(i) = P(0) + Scale(i) * UnitVector(i) vertices[0] = p0; for (int i = 0; i < numDimensions; i++) { double scale = simplexConstants[i].InitialPerturbation; Vector unitVector = new DenseVector(numDimensions); unitVector[i] = 1; vertices[i + 1] = p0.Add(unitVector.Multiply(scale)); } return(vertices); }
public static bool TryFindRootWithJacobianStep(Func <Real[], Real[]> f, Real[] initialGuess, Real accuracy, int maxIterations, Real jacobianStepSize, out Real[] root) { DenseVector denseVector1 = new DenseVector(initialGuess); Real[] numArray = f(initialGuess); DenseVector denseVector2 = new DenseVector(numArray); Real num1 = ToReal(denseVector2.L2Norm()); Matrix <Real> approximateJacobian = Broyden.CalculateApproximateJacobian(f, initialGuess, numArray, jacobianStepSize); for (int index = 0; index <= maxIterations; ++index) { DenseVector denseVector3 = (DenseVector)(-approximateJacobian.LU().Solve(denseVector2)); DenseVector denseVector4 = denseVector1 + denseVector3; DenseVector denseVector5 = new DenseVector(f(denseVector4.Values)); Real num2 = ToReal(denseVector5.L2Norm()); if (num2 > num1) { Real num3 = num1 * num1; Real num4 = num3 / (num3 + num2 * num2); if (num4 == 0.0) { num4 = ToReal(0.0001); } denseVector3 = num4 * denseVector3; denseVector4 = denseVector1 + denseVector3; denseVector5 = new DenseVector(f(denseVector4.Values)); num2 = ToReal(denseVector5.L2Norm()); } if (num2 < accuracy) { root = denseVector4.Values; return(true); } Matrix <Real> matrix = (denseVector5 - denseVector2 - approximateJacobian.Multiply(denseVector3)).ToColumnMatrix() * denseVector3.Multiply(ToReal(1.0) / Math.Pow(ToReal(denseVector3.L2Norm()), ToReal(2.0))) .ToRowMatrix(); approximateJacobian += matrix; denseVector1 = denseVector4; denseVector2 = denseVector5; num1 = num2; } root = null; return(false); }
/// <summary> /// Compute the centroid of all points except the worst /// </summary> /// <param name="vertices"></param> /// <param name="errorProfile"></param> /// <returns></returns> private static Vector ComputeCentroid(Vector <double>[] vertices, ErrorProfile errorProfile) { int numVertices = vertices.Length; // find the centroid of all points except the worst one Vector centroid = new DenseVector(numVertices - 1); for (int i = 0; i < numVertices; i++) { if (i != errorProfile.HighestIndex) { centroid = (Vector)centroid.Add(vertices[i]); } } return((Vector)centroid.Multiply(1.0d / (numVertices - 1))); }
public static double[] bar3gfBarnes(double[][] ec, double[] ep, double[] ed, double ten) { // Compute the internal force vector for a cable element according to: // FORM-FINDING AND ANALYSIS OF PRESTRESSED NETS AND MEMBRANES // by M. R. BARNES (1988) //ed = [a_1 a_2 ... a_6]; Element nodal displacements //ep = [E A]; element properties //ten = [ten]; Specified initial element tension //OUTPUT: //ef = F_int = [f_1 f_2 ... f_6]'; //ec format: [x,y,z][node1,node2...] //ed format: [node1x, node1y, node1z, node2x...] double l0 = getElementLength(ec); double[][] ecUpdated = updateEcWithDisp(ec, ed); double l = getElementLength(ecUpdated); //Current length //Current element force double tm = ten + (ep[0] * ep[1] / l0) * (l - l0); //Direction vector Vector v = new DenseVector(3); for (int i = 0; i < 3; i++) { v[i] = ecUpdated[i][0] - ecUpdated[i][1]; } double vl = v.Norm(2); Vector x = new DenseVector(6); for (int i = 0; i < 3; i++) { x[i] = v[i] / vl; } //Reverse direction vector for (int i = 0; i < 3; i++) { x[i + 3] = -v[i] / vl; } return(x.Multiply(tm).ToArray()); }
public bool Optimize(Vector <double> resultAndInitPos, IFunctionWithDerivative function, double minDerivCompMaxMagn, Action <int, Func <Vector <double> > > iterationCallback, CancellationToken ct) { Contract.Requires(resultAndInitPos.Count == function.DimensionsCount); Contract.Requires(minDerivCompMaxMagn > 0); var derivative = new DenseVector(function.DimensionsCount); var prevStep = new DenseVector(function.DimensionsCount); var position = new DenseVector(function.DimensionsCount); for (int iter = 0; iter < maxIters; ++iter) { if (ct.IsCancellationRequested) { break; } resultAndInitPos.CopyTo(position); double momentum = momentumStart + (momentumEnd - momentumStart) * ((double)iter / maxIters); // Compute derivative take step to point + momentum * prevStep and compute derivative there. prevStep.Multiply(momentum, prevStep); prevStep.Add(position, resultAndInitPos); function.Derivate(derivative, resultAndInitPos); if (derivative.AbsoluteMaximum() < minDerivCompMaxMagn) { return(true); } // Scale derivative and subtract from result to take "correction" step. derivative.Multiply(step, derivative); resultAndInitPos.Subtract(derivative, resultAndInitPos); // Conpute prevStep as (result - point). resultAndInitPos.Subtract(position, prevStep); if (iterationCallback != null) { iterationCallback(iter, resultAndInitPos.Clone); } } return(false); }
/// <summary> /// Estimate class weights for unbalanced datasets. /// </summary> /// <param name="classes">Sorted array of the classes occurring in the data.</param> /// <param name="yInd">Array of class indices per sample.</param> /// <returns>Array with ith element - the weight for i-th class (as determined by sorting).</returns> private static Vector ComputeClassWeightAuto(TLabel[] classes, int[] yInd) { // inversely proportional to the number of samples in the class var histogram = new Dictionary <TLabel, int>(); foreach (var ind in yInd) { int val; histogram.TryGetValue(classes[ind], out val); val++; histogram[classes[ind]] = val; } Vector weight = new DenseVector(classes.Count()); for (int i = 0; i < classes.Count(); i++) { weight[i] = 1.0 / (histogram.ContainsKey(classes[i]) ? histogram[classes[i]] : 1); } weight.Multiply(1.0 * classes.Length / weight.Sum(), weight); return(weight); }
public bool Optimize(Vector <double> resultAndInitPos, IFunctionWithDerivative function, double minDerivCompMaxMagn, Action <int, Func <Vector <double> > > iterationCallback, CancellationToken ct) { Contract.Requires(resultAndInitPos.Count == function.DimensionsCount); Contract.Requires(minDerivCompMaxMagn > 0); var derivative = new DenseVector(function.DimensionsCount); var prevStep = new DenseVector(function.DimensionsCount); for (int iter = 0; iter < maxIters; ++iter) { if (ct.IsCancellationRequested) { break; } function.Derivate(derivative, resultAndInitPos); if (derivative.AbsoluteMaximum() < minDerivCompMaxMagn) { return(true); } derivative.Multiply(step, derivative); double momentum = momentumStart + (momentumEnd - momentumStart) * ((double)iter / maxIters); prevStep.Multiply(momentum, prevStep); prevStep.Add(derivative, prevStep); resultAndInitPos.Subtract(prevStep, resultAndInitPos); if (iterationCallback != null) { iterationCallback(iter, resultAndInitPos.Clone); } } return(false); }
/// <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 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); // 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 Complex beta = 0; Complex 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.Real.AlmostEqual(0, 1) && cdot.Imaginary.AlmostEqual(0, 1)) { cdot = 1.0; } // 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); Complex 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.Real.AlmostEqual(0, 1) && ydot.Imaginary.AlmostEqual(0, 1)) { ydot = 1.0; } 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.Real.AlmostEqual(0, 1) || !sigma.Imaginary.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 <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 <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; // 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, 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); // 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 float currentRho = 0; float alpha = 0; float 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.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.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> /// Run example. /// </summary> /// <seealso cref="http://en.wikipedia.org/wiki/Euclidean_vector#Scalar_multiplication">Multiply vector by scalar</seealso> /// <seealso cref="http://en.wikipedia.org/wiki/Euclidean_vector#Dot_product">Multiply vector by vector (compute the dot product between two vectors)</seealso> /// <seealso cref="http://en.wikipedia.org/wiki/Euclidean_vector#Addition_and_subtraction">Vector addition and subtraction</seealso> /// <seealso cref="http://en.wikipedia.org/wiki/Outer_product">Outer Product of two vectors</seealso> public void Run() { // Initialize IFormatProvider to print matrix/vector data var formatProvider = (CultureInfo)CultureInfo.InvariantCulture.Clone(); formatProvider.TextInfo.ListSeparator = " "; // Create vector "X" var vectorX = new DenseVector(new[] { 1.0, 2.0, 3.0, 4.0, 5.0 }); Console.WriteLine(@"Vector X"); Console.WriteLine(vectorX.ToString("#0.00\t", formatProvider)); Console.WriteLine(); // Create vector "Y" var vectorY = new DenseVector(new[] { 5.0, 4.0, 3.0, 2.0, 1.0 }); Console.WriteLine(@"Vector Y"); Console.WriteLine(vectorY.ToString("#0.00\t", formatProvider)); Console.WriteLine(); // Multiply vector by scalar // 1. Using Multiply method and getting result into different vector instance var resultV = vectorX.Multiply(3.0); Console.WriteLine(@"Multiply vector by scalar using method Multiply. (result = X.Multiply(3.0))"); Console.WriteLine(resultV.ToString("#0.00\t", formatProvider)); Console.WriteLine(); // 2. Using operator "*" resultV = 3.0 * vectorX; Console.WriteLine(@"Multiply vector by scalar using operator *. (result = 3.0 * X)"); Console.WriteLine(resultV.ToString("#0.00\t", formatProvider)); Console.WriteLine(); // 3. Using Multiply method and updating vector itself vectorX.Multiply(3.0, vectorX); Console.WriteLine(@"Multiply vector by scalar using method Multiply. (X.Multiply(3.0, X))"); Console.WriteLine(vectorX.ToString("#0.00\t", formatProvider)); Console.WriteLine(); // Multiply vector by vector (compute the dot product between two vectors) // 1. Using operator "*" var dotProduct = vectorX * vectorY; Console.WriteLine(@"Dot product between two vectors using operator *. (result = X * Y)"); Console.WriteLine(dotProduct); Console.WriteLine(); // 2. Using DotProduct method and getting result into different vector instance dotProduct = vectorX.DotProduct(vectorY); Console.WriteLine(@"Dot product between two vectors using method DotProduct. (result = X.DotProduct(Y))"); Console.WriteLine(dotProduct.ToString("#0.00\t", formatProvider)); Console.WriteLine(); // Pointwise multiplie vector with another vector // 1. Using PointwiseMultiply method and getting result into different vector instance resultV = vectorX.PointwiseMultiply(vectorY); Console.WriteLine(@"Pointwise multiplie vector with another vector using method PointwiseMultiply. (result = X.PointwiseMultiply(Y))"); Console.WriteLine(resultV.ToString("#0.00\t", formatProvider)); Console.WriteLine(); // 2. Using PointwiseMultiply method and updating vector itself vectorX.PointwiseMultiply(vectorY, vectorX); Console.WriteLine(@"Pointwise multiplie vector with another vector using method PointwiseMultiply. (X.PointwiseMultiply(Y, X))"); Console.WriteLine(vectorX.ToString("#0.00\t", formatProvider)); Console.WriteLine(); // Pointwise divide vector with another vector // 1. Using PointwiseDivide method and getting result into different vector instance resultV = vectorX.PointwiseDivide(vectorY); Console.WriteLine(@"Pointwise divide vector with another vector using method PointwiseDivide. (result = X.PointwiseDivide(Y))"); Console.WriteLine(resultV.ToString("#0.00\t", formatProvider)); Console.WriteLine(); // 2. Using PointwiseDivide method and updating vector itself vectorX.PointwiseDivide(vectorY, vectorX); Console.WriteLine(@"Pointwise divide vector with another vector using method PointwiseDivide. (X.PointwiseDivide(Y, X))"); Console.WriteLine(vectorX.ToString("#0.00\t", formatProvider)); Console.WriteLine(); // Addition // 1. Using operator "+" resultV = vectorX + vectorY; Console.WriteLine(@"Add vectors using operator +. (result = X + Y)"); Console.WriteLine(resultV.ToString("#0.00\t", formatProvider)); Console.WriteLine(); // 2. Using Add method and getting result into different vector instance resultV = vectorX.Add(vectorY); Console.WriteLine(@"Add vectors using method Add. (result = X.Add(Y))"); Console.WriteLine(resultV.ToString("#0.00\t", formatProvider)); Console.WriteLine(); // 3. Using Add method and updating vector itself vectorX.Add(vectorY, vectorX); Console.WriteLine(@"Add vectors using method Add. (X.Add(Y, X))"); Console.WriteLine(vectorX.ToString("#0.00\t", formatProvider)); Console.WriteLine(); // Subtraction // 1. Using operator "-" resultV = vectorX - vectorY; Console.WriteLine(@"Subtract vectors using operator -. (result = X - Y)"); Console.WriteLine(resultV.ToString("#0.00\t", formatProvider)); Console.WriteLine(); // 2. Using Subtract method and getting result into different vector instance resultV = vectorX.Subtract(vectorY); Console.WriteLine(@"Subtract vectors using method Subtract. (result = X.Subtract(Y))"); Console.WriteLine(resultV.ToString("#0.00\t", formatProvider)); Console.WriteLine(); // 3. Using Subtract method and updating vector itself vectorX.Subtract(vectorY, vectorX); Console.WriteLine(@"Subtract vectors using method Subtract. (X.Subtract(Y, X))"); Console.WriteLine(vectorX.ToString("#0.00\t", formatProvider)); Console.WriteLine(); // Divide by scalar // 1. Using Divide method and getting result into different vector instance resultV = vectorX.Divide(3.0); Console.WriteLine(@"Divide vector by scalar using method Divide. (result = A.Divide(3.0))"); Console.WriteLine(resultV.ToString("#0.00\t", formatProvider)); Console.WriteLine(); // 2. Using Divide method and updating vector itself vectorX.Divide(3.0, vectorX); Console.WriteLine(@"Divide vector by scalar using method Divide. (X.Divide(3.0, X))"); Console.WriteLine(vectorX.ToString("#0.00\t", formatProvider)); Console.WriteLine(); // Outer Product of two vectors // 1. Using instanse method OuterProduct var resultM = vectorX.OuterProduct(vectorY); Console.WriteLine(@"Outer Product of two vectors using method OuterProduct. (X.OuterProduct(Y))"); Console.WriteLine(resultM.ToString("#0.00\t", formatProvider)); Console.WriteLine(); // 2. Using static method of the Vector class resultM = Vector.OuterProduct(vectorX, vectorY); Console.WriteLine(@"Outer Product of two vectors using method OuterProduct. (Vector.OuterProduct(X,Y))"); Console.WriteLine(resultM.ToString("#0.00\t", formatProvider)); Console.WriteLine(); }
/// <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 <Complex> matrix, Vector <Complex> input, Vector <Complex> result, Iterator <Complex> iterator, IPreconditioner <Complex> preconditioner) { if (matrix.RowCount != matrix.ColumnCount) { throw new ArgumentException(Resources.ArgumentMatrixSquare, nameof(matrix)); } if (input.Count != matrix.RowCount || result.Count != input.Count) { throw Matrix.DimensionsDontMatch <ArgumentException>(matrix, input, result); } if (iterator == null) { iterator = new Iterator <Complex>(); } if (preconditioner == null) { preconditioner = new UnitPreconditioner <Complex>(); } 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 Complex alpha = 0; Complex eta = 0; double theta = 0; // Initialize var tau = input.L2Norm(); 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 (iterator.DetermineStatus(iterationNumber, result, input, pseudoResiduals) == IterationStatus.Continue) { // First part of the step, the even bit if (IsEven(iterationNumber)) { // sigma = (v, r) var sigma = r.ConjugateDotProduct(v); if (sigma.Real.AlmostEqualNumbersBetween(0, 1) && sigma.Imaginary.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 = pseudoResiduals.L2Norm() / 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 (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.Real.AlmostEqualNumbersBetween(0, 1) && rho.Imaginary.AlmostEqualNumbersBetween(0, 1)) { // FAIL HERE iterator.Cancel(); break; } var rhoNew = r.ConjugateDotProduct(pseudoResiduals); 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> /// <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 <double> matrix, Vector <double> input, Vector <double> result, Iterator <double> iterator, IPreconditioner <double> 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 <double>(); } if (preconditioner == null) { preconditioner = new UnitPreconditioner <double>(); } 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 double 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.0; } // 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); double eta; double 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.0; } 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 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 (input.Count != matrix.RowCount || result.Count != input.Count) { throw Matrix.DimensionsDontMatch <ArgumentException>(matrix, 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); // 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> 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 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 <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 <Maths.Complex32> matrix, Vector <Maths.Complex32> input, Vector <Maths.Complex32> result, Iterator <Maths.Complex32> iterator, IPreconditioner <Maths.Complex32> preconditioner) { if (matrix.RowCount != matrix.ColumnCount) { throw new ArgumentException("Matrix must be square.", nameof(matrix)); } if (result.Count != input.Count) { throw new ArgumentException("All vectors must have the same dimensionality."); } if (input.Count != matrix.RowCount) { throw Matrix.DimensionsDontMatch <ArgumentException>(input, matrix); } if (iterator == null) { iterator = new Iterator <Maths.Complex32>(); } if (preconditioner == null) { preconditioner = new UnitPreconditioner <Maths.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 Maths.Complex32 currentRho = 0; Maths.Complex32 alpha = 0; Maths.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++; } }
/// <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> /// 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 <float> matrix) { if (matrix == null) { throw new ArgumentNullException("matrix"); } if (matrix.RowCount != matrix.ColumnCount) { throw new ArgumentException(Resources.ArgumentMatrixSquare, "matrix"); } SparseMatrix sparseMatrix = matrix as SparseMatrix ?? SparseMatrix.OfMatrix(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; } var workVector = new DenseVector(sparseMatrix.RowCount); var 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.InfinityNorm(); // 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 (Math.Abs(workVector[j]) < _dropTolerance) { workVector[j] = 0.0f; } // 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.0f; } 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 (Math.Abs(workVector[j]) <= _dropTolerance * vectorNorm) { workVector[j] = 0.0f; } } // 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 (Math.Abs(workVector[i]) < _pivotTolerance * Math.Abs(workVector[indexSorting[0]])) { // 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.0f; } }