csharpfftfsharpintegrationinterpolationlinear-algebramathdifferentiationmatrixnumericsrandomregressionstatisticsmathnet
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
541 lines
21 KiB
541 lines
21 KiB
// <copyright file="BiCgStab.cs" company="Math.NET">
|
|
// Math.NET Numerics, part of the Math.NET Project
|
|
// http://numerics.mathdotnet.com
|
|
// http://github.com/mathnet/mathnet-numerics
|
|
// http://mathnetnumerics.codeplex.com
|
|
//
|
|
// Copyright (c) 2009-2010 Math.NET
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person
|
|
// obtaining a copy of this software and associated documentation
|
|
// files (the "Software"), to deal in the Software without
|
|
// restriction, including without limitation the rights to use,
|
|
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
// copies of the Software, and to permit persons to whom the
|
|
// Software is furnished to do so, subject to the following
|
|
// conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be
|
|
// included in all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
// OTHER DEALINGS IN THE SOFTWARE.
|
|
// </copyright>
|
|
|
|
using MathNet.Numerics.LinearAlgebra.Complex.Solvers.Preconditioners;
|
|
using MathNet.Numerics.LinearAlgebra.Solvers.Status;
|
|
using System;
|
|
using MathNet.Numerics.Properties;
|
|
|
|
namespace MathNet.Numerics.LinearAlgebra.Complex.Solvers.Iterative
|
|
{
|
|
|
|
#if NOSYSNUMERICS
|
|
using Complex = Numerics.Complex;
|
|
#else
|
|
using Complex = System.Numerics.Complex;
|
|
#endif
|
|
|
|
/// <summary>
|
|
/// A Bi-Conjugate Gradient stabilized iterative matrix solver.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// <para>
|
|
/// The Bi-Conjugate Gradient Stabilized (BiCGStab) solver is an 'improvement'
|
|
/// of the standard Conjugate Gradient (CG) solver. Unlike the CG solver the
|
|
/// BiCGStab can be used on non-symmetric matrices. <br/>
|
|
/// Note that much of the success of the solver depends on the selection of the
|
|
/// proper preconditioner.
|
|
/// </para>
|
|
/// <para>
|
|
/// The Bi-CGSTAB algorithm was taken from: <br/>
|
|
/// Templates for the solution of linear systems: Building blocks
|
|
/// for iterative methods
|
|
/// <br/>
|
|
/// Richard Barrett, Michael Berry, Tony F. Chan, James Demmel,
|
|
/// June M. Donato, Jack Dongarra, Victor Eijkhout, Roldan Pozo,
|
|
/// Charles Romine and Henk van der Vorst
|
|
/// <br/>
|
|
/// Url: <a href="http://www.netlib.org/templates/Templates.html">http://www.netlib.org/templates/Templates.html</a>
|
|
/// <br/>
|
|
/// Algorithm is described in Chapter 2, section 2.3.8, page 27
|
|
/// </para>
|
|
/// <para>
|
|
/// The example code below provides an indication of the possible use of the
|
|
/// solver.
|
|
/// </para>
|
|
/// </remarks>
|
|
public sealed class BiCgStab : IIterativeSolver
|
|
{
|
|
/// <summary>
|
|
/// The status used if there is no status, i.e. the solver hasn't run yet and there is no
|
|
/// iterator.
|
|
/// </summary>
|
|
private static readonly ICalculationStatus DefaultStatus = new CalculationIndetermined();
|
|
|
|
/// <summary>
|
|
/// The preconditioner that will be used. Can be set to <see langword="null" />, in which case the default
|
|
/// pre-conditioner will be used.
|
|
/// </summary>
|
|
private IPreConditioner _preconditioner;
|
|
|
|
/// <summary>
|
|
/// The iterative process controller.
|
|
/// </summary>
|
|
private IIterator _iterator;
|
|
|
|
/// <summary>
|
|
/// Indicates if the user has stopped the solver.
|
|
/// </summary>
|
|
private bool _hasBeenStopped;
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="BiCgStab"/> class.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// When using this constructor the solver will use the <see cref="IIterator"/> with
|
|
/// the standard settings and a default preconditioner.
|
|
/// </remarks>
|
|
public BiCgStab() : this(null, null)
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="BiCgStab"/> class.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// <para>
|
|
/// When using this constructor the solver will use a default preconditioner.
|
|
/// </para>
|
|
/// <para>
|
|
/// The main advantages of using a user defined <see cref="IIterator"/> are:
|
|
/// <list type="number">
|
|
/// <item>It is possible to set the desired convergence limits.</item>
|
|
/// <item>
|
|
/// It is possible to check the reason for which the solver finished
|
|
/// the iterative procedure by calling the <see cref="IIterator.Status"/> property.
|
|
/// </item>
|
|
/// </list>
|
|
/// </para>
|
|
/// </remarks>
|
|
/// <param name="iterator">The <see cref="IIterator"/> that will be used to monitor the iterative process. </param>
|
|
public BiCgStab(IIterator iterator) : this(null, iterator)
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="BiCgStab"/> class.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// When using this constructor the solver will use the <see cref="IIterator"/> with
|
|
/// the standard settings.
|
|
/// </remarks>
|
|
/// <param name="preconditioner">The <see cref="IPreConditioner"/> that will be used to precondition the matrix equation.</param>
|
|
public BiCgStab(IPreConditioner preconditioner) : this(preconditioner, null)
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="BiCgStab"/> class.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// <para>
|
|
/// The main advantages of using a user defined <see cref="IIterator"/> are:
|
|
/// <list type="number">
|
|
/// <item>It is possible to set the desired convergence limits.</item>
|
|
/// <item>
|
|
/// It is possible to check the reason for which the solver finished
|
|
/// the iterative procedure by calling the <see cref="IIterator.Status"/> property.
|
|
/// </item>
|
|
/// </list>
|
|
/// </para>
|
|
/// </remarks>
|
|
/// <param name="preconditioner">The <see cref="IPreConditioner"/> that will be used to precondition the matrix equation. </param>
|
|
/// <param name="iterator">The <see cref="IIterator"/> that will be used to monitor the iterative process. </param>
|
|
public BiCgStab(IPreConditioner preconditioner, IIterator iterator)
|
|
{
|
|
_iterator = iterator;
|
|
_preconditioner = preconditioner;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the <see cref="IPreConditioner"/> that will be used to precondition the iterative process.
|
|
/// </summary>
|
|
/// <param name="preconditioner">The preconditioner.</param>
|
|
public void SetPreconditioner(IPreConditioner preconditioner)
|
|
{
|
|
_preconditioner = preconditioner;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the <see cref="IIterator"/> that will be used to track the iterative process.
|
|
/// </summary>
|
|
/// <param name="iterator">The iterator.</param>
|
|
public void SetIterator(IIterator iterator)
|
|
{
|
|
_iterator = iterator;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the status of the iteration once the calculation is finished.
|
|
/// </summary>
|
|
public ICalculationStatus IterationResult
|
|
{
|
|
get
|
|
{
|
|
return (_iterator != null) ? _iterator.Status : DefaultStatus;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Stops the solve process.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Note that it may take an indetermined amount of time for the solver to actually stop the process.
|
|
/// </remarks>
|
|
public void StopSolve()
|
|
{
|
|
_hasBeenStopped = true;
|
|
}
|
|
|
|
/// <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="vector">The solution <see cref="Vector"/>, <c>b</c>.</param>
|
|
/// <returns>The result <see cref="Vector"/>, <c>x</c>.</returns>
|
|
public Vector Solve(Matrix matrix, Vector vector)
|
|
{
|
|
if (vector == null)
|
|
{
|
|
throw new ArgumentNullException();
|
|
}
|
|
|
|
Vector result = new DenseVector(matrix.RowCount);
|
|
Solve(matrix, vector, result);
|
|
return 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>
|
|
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>
|
|
/// Calculates the <c>true</c> residual of the matrix equation Ax = b according to: residual = b - Ax
|
|
/// </summary>
|
|
/// <param name="matrix">Instance of the <see cref="Matrix"/> A.</param>
|
|
/// <param name="residual">Residual values in <see cref="Vector"/>.</param>
|
|
/// <param name="x">Instance of the <see cref="Vector"/> x.</param>
|
|
/// <param name="b">Instance of the <see cref="Vector"/> b.</param>
|
|
private static void CalculateTrueResidual(Matrix matrix, Vector residual, Vector x, Vector b)
|
|
{
|
|
// -Ax = residual
|
|
matrix.Multiply(x, residual);
|
|
|
|
// Do not use residual = residual.Negate() because it creates another object
|
|
residual.Multiply(-1, residual);
|
|
|
|
// residual + b
|
|
residual.Add(b, residual);
|
|
}
|
|
|
|
/// <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>
|
|
private bool ShouldContinue(int iterationNumber, Vector result, Vector source, Vector residuals)
|
|
{
|
|
if (_hasBeenStopped)
|
|
{
|
|
_iterator.IterationCancelled();
|
|
return true;
|
|
}
|
|
|
|
_iterator.DetermineStatus(iterationNumber, result, source, residuals);
|
|
var status = _iterator.Status;
|
|
|
|
// 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.)
|
|
return (!status.TerminatesCalculation) && (!_hasBeenStopped);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Solves the matrix equation AX = B, where A is the coefficient matrix, B is the
|
|
/// solution matrix and X is the unknown matrix.
|
|
/// </summary>
|
|
/// <param name="matrix">The coefficient <see cref="Matrix"/>, <c>A</c>.</param>
|
|
/// <param name="input">The solution <see cref="Matrix"/>, <c>B</c>.</param>
|
|
/// <returns>The result <see cref="Matrix"/>, <c>X</c>.</returns>
|
|
public Matrix Solve(Matrix matrix, Matrix input)
|
|
{
|
|
if (matrix == null)
|
|
{
|
|
throw new ArgumentNullException("matrix");
|
|
}
|
|
|
|
if (input == null)
|
|
{
|
|
throw new ArgumentNullException("input");
|
|
}
|
|
|
|
var result = (Matrix)matrix.CreateMatrix(input.RowCount, input.ColumnCount);
|
|
Solve(matrix, input, result);
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Solves the matrix equation AX = B, where A is the coefficient matrix, B is the
|
|
/// solution matrix and X is the unknown matrix.
|
|
/// </summary>
|
|
/// <param name="matrix">The coefficient <see cref="Matrix"/>, <c>A</c>.</param>
|
|
/// <param name="input">The solution <see cref="Matrix"/>, <c>B</c>.</param>
|
|
/// <param name="result">The result <see cref="Matrix"/>, <c>X</c></param>
|
|
public void Solve(Matrix matrix, Matrix input, Matrix result)
|
|
{
|
|
if (matrix == null)
|
|
{
|
|
throw new ArgumentNullException("matrix");
|
|
}
|
|
|
|
if (input == null)
|
|
{
|
|
throw new ArgumentNullException("input");
|
|
}
|
|
|
|
if (result == null)
|
|
{
|
|
throw new ArgumentNullException("result");
|
|
}
|
|
|
|
if (matrix.RowCount != input.RowCount || input.RowCount != result.RowCount || input.ColumnCount != result.ColumnCount)
|
|
{
|
|
throw Matrix.DimensionsDontMatch<ArgumentException>(matrix, input, result);
|
|
}
|
|
|
|
for (var column = 0; column < input.ColumnCount; column++)
|
|
{
|
|
var solution = Solve(matrix, (Vector)input.Column(column));
|
|
foreach (var element in solution.GetIndexedEnumerator())
|
|
{
|
|
result.At(element.Item1, column, element.Item2);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|