public void DetermineStatus() { var criteria = new List<IIterationStopCriterium<Complex>> { new FailureStopCriterium(), new DivergenceStopCriterium(), new IterationCountStopCriterium<Complex>(1) }; var iterator = new Iterator<Complex>(criteria); // First step, nothing should happen. iterator.DetermineStatus( 0, DenseVector.Create(3, i => 4), DenseVector.Create(3, i => 4), DenseVector.Create(3, i => 4)); Assert.AreEqual(IterationStatus.Continue, iterator.Status, "Incorrect status"); // Second step, should run out of iterations. iterator.DetermineStatus( 1, DenseVector.Create(3, i => 4), DenseVector.Create(3, i => 4), DenseVector.Create(3, i => 4)); Assert.AreEqual(IterationStatus.StoppedWithoutConvergence, iterator.Status, "Incorrect status"); }
/// <summary> /// Determine if calculation should continue /// </summary> /// <param name="iterationNumber">Number of iterations passed</param> /// <param name="result">Result <see cref="Vector"/>.</param> /// <param name="source">Source <see cref="Vector"/>.</param> /// <param name="residuals">Residual <see cref="Vector"/>.</param> /// <returns><c>true</c> if continue, otherwise <c>false</c></returns> bool ShouldContinue(int iterationNumber, Vector <Complex> result, Vector <Complex> source, Vector <Complex> residuals) { // We stop if either: // - the user has stopped the calculation // - the calculation needs to be stopped from a numerical point of view (divergence, convergence etc.) if (_hasBeenStopped) { _iterator.Cancel(); return(true); } var status = _iterator.DetermineStatus(iterationNumber, result, source, residuals); return(status == IterationStatus.Running || status == IterationStatus.Indetermined); }
public void DetermineStatusWithNegativeIterationNumberThrowsArgumentOutOfRangeException() { var criteria = new List<IIterationStopCriterium<Complex>> { new FailureStopCriterium(), new DivergenceStopCriterium(), new IterationCountStopCriterium<Complex>(), new ResidualStopCriterium() }; var iterator = new Iterator<Complex>(criteria); Assert.Throws<ArgumentOutOfRangeException>(() => iterator.DetermineStatus( -1, DenseVector.Create(3, i => 4), DenseVector.Create(3, i => 5), DenseVector.Create(3, i => 6))); }
public void ResetToPrecalculationState() { var criteria = new List<IIterationStopCriterium<Complex>> { new FailureStopCriterium(), new DivergenceStopCriterium(), new IterationCountStopCriterium<Complex>(1) }; var iterator = new Iterator<Complex>(criteria); // First step, nothing should happen. iterator.DetermineStatus( 0, DenseVector.Create(3, i => 4), DenseVector.Create(3, i => 4), DenseVector.Create(3, i => 4)); Assert.AreEqual(IterationStatus.Continue, iterator.Status, "Incorrect status"); iterator.Reset(); Assert.AreEqual(IterationStatus.Continue, iterator.Status, "Incorrect status"); Assert.AreEqual(IterationStatus.Continue, criteria[0].Status, "Incorrect status"); Assert.AreEqual(IterationStatus.Continue, criteria[1].Status, "Incorrect status"); Assert.AreEqual(IterationStatus.Continue, criteria[2].Status, "Incorrect status"); }
public void DetermineStatusWithoutStopCriteriaDoesNotThrow() { var iterator = new Iterator<Complex>(); Assert.DoesNotThrow(() => iterator.DetermineStatus( 0, DenseVector.Create(3, i => 4), DenseVector.Create(3, i => 5), DenseVector.Create(3, i => 6))); }
/// <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 <Complex> matrix, Vector <Complex> input, Vector <Complex> result, Iterator <Complex> iterator, IPreconditioner <Complex> preconditioner) { var A = (SparseMatrix)matrix; var M = preconditioner; var b = (DenseVector)input; var x = (DenseVector)result; double atolf = 0.0; double rtol_1 = 0.0; int recompute_residual_p = 0; var p = new DenseVector(b.Count); var s = new DenseVector(b.Count); var r = new DenseVector(b.Count); Complex alpha, beta; Complex gamma, gamma_old; Complex bi_prod; Complex sdotp; bool recompute_true_residual = false; int i = 0; M.Initialize(A); // Start pcg solve // bi_prod = <C*b,b> //VectorHelper.Clear(p); M.Approximate(input, p); bi_prod = VectorHelper.DotProduct(p, b); if (bi_prod.Real > 0.0) { if (atolf > 0) // mixed relative and absolute tolerance { bi_prod += atolf; } } else // bi_prod==0.0: the rhs vector b is zero { // Set x equal to zero and return VectorHelper.Copy(b, x); return; } // r = b - Ax VectorHelper.Copy(b, r); A.Multiply(-1.0, result, 1.0, r); // p = C*r //VectorHelper.Clear(p); M.Approximate(r, p); // gamma = <r,p> gamma = VectorHelper.DotProduct(r, p); while (iterator.DetermineStatus(i, x, b, r) == IterationStatus.Continue) { // the core CG calculations... i++; // At user request, periodically recompute the residual from the formula // r = b - A x (instead of using the recursive definition). Note that this // is potentially expensive and can lead to degraded convergence (since it // essentially a "restarted CG"). recompute_true_residual = (recompute_residual_p > 0) && !((i % recompute_residual_p) == 0); // s = A*p A.Multiply(1.0, p, 0.0, s); // alpha = gamma / <s,p> sdotp = VectorHelper.DotProduct(s, p); if (sdotp == 0.0) { throw new NumericalBreakdownException(); } alpha = gamma / sdotp; gamma_old = gamma; // x = x + alpha*p VectorHelper.Add(alpha, p, x, x); // r = r - alpha*s if (recompute_true_residual) { //Recomputing the residual... VectorHelper.Copy(b, r); A.Multiply(-1.0, result, 1.0, r); } else { VectorHelper.Add(-alpha, s, r, r); } // s = C*r VectorHelper.Clear(s); M.Approximate(r, s); // gamma = <r,s> gamma = VectorHelper.DotProduct(r, s); // residual-based stopping criteria: ||r_new-r_old||_C < rtol ||b||_C if (rtol_1 > 0) { // use that ||r_new-r_old||_C^2 = (r_new ,C r_new) + (r_old, C r_old) if (Complex.Abs((gamma + gamma_old) / bi_prod) < rtol_1 * rtol_1) { break; } } // ... gamma should be >=0. IEEE subnormal numbers are < 2**(-1022)=2.2e-308 // (and >= 2**(-1074)=4.9e-324). So a gamma this small means we're getting // dangerously close to subnormal or zero numbers (usually if gamma is small, // so will be other variables). Thus further calculations risk a crash. // Such small gamma generally means no hope of progress anyway. if (Complex.Abs(gamma) < TINY) { throw new NumericalBreakdownException(); } // beta = gamma / gamma_old beta = gamma / gamma_old; // p = s + beta p if (recompute_true_residual) { VectorHelper.Copy(s, p); } else { p.Scale(beta); VectorHelper.Add(1.0, s, p, p); } } }