//
// Math.NET Numerics, part of the Math.NET Project
// http://numerics.mathdotnet.com
// http://github.com/mathnet/mathnet-numerics
//
// Copyright (c) 2009-2013 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.
//
using System;
using IStation.Numerics.LinearAlgebra.Solvers;
namespace IStation.Numerics.LinearAlgebra.Complex32.Solvers
{
///
/// A Transpose Free Quasi-Minimal Residual (TFQMR) iterative matrix solver.
///
///
///
/// The TFQMR algorithm was taken from:
/// Iterative methods for sparse linear systems.
///
/// Yousef Saad
///
/// Algorithm is described in Chapter 7, section 7.4.3, page 219
///
///
/// The example code below provides an indication of the possible use of the
/// solver.
///
///
public sealed class TFQMR : IIterativeSolver
{
///
/// Calculates the true residual of the matrix equation Ax = b according to: residual = b - Ax
///
/// Instance of the A.
/// Residual values in .
/// Instance of the x.
/// Instance of the b.
static void CalculateTrueResidual(Matrix matrix, Vector residual, Vector x, Vector b)
{
// -Ax = residual
matrix.Multiply(x, residual);
residual.Multiply(-1, residual);
// residual + b
residual.Add(b, residual);
}
///
/// Is even?
///
/// Number to check
/// true if even, otherwise false
static bool IsEven(int number)
{
return number % 2 == 0;
}
///
/// Solves the matrix equation Ax = b, where A is the coefficient matrix, b is the
/// solution vector and x is the unknown vector.
///
/// The coefficient matrix, A.
/// The solution vector, b
/// The result vector, x
/// The iterator to use to control when to stop iterating.
/// The preconditioner to use for approximations.
public void Solve(Matrix matrix, Vector input, Vector result, Iterator iterator, IPreconditioner 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(input, matrix);
}
if (iterator == null)
{
iterator = new Iterator();
}
if (preconditioner == null)
{
preconditioner = new UnitPreconditioner();
}
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
Numerics.Complex32 alpha = 0;
Numerics.Complex32 eta = 0;
float theta = 0;
// Initialize
var tau = (float) input.L2Norm();
Numerics.Complex32 rho = tau*tau;
// Calculate the initial values for v
// M temp = yEven
preconditioner.Approximate(yeven, temp);
// v = A temp
matrix.Multiply(temp, v);
// Set uOdd
v.CopyTo(ueven);
// Start the iteration
var iterationNumber = 0;
while (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 = (float) pseudoResiduals.L2Norm()/tau;
var c = 1/(float) Math.Sqrt(1 + (theta*theta));
// tau = tau * theta * c
tau *= theta*c;
// eta = c^2 * alpha
eta = c*c*alpha;
// x = x + eta * d
d.Multiply(eta, temp1);
x.Add(temp1, temp2);
temp2.CopyTo(x);
// Check convergence and see if we can bail
if (iterator.DetermineStatus(iterationNumber, result, input, pseudoResiduals) != IterationStatus.Continue)
{
// Calculate the real values
preconditioner.Approximate(x, result);
// Calculate the true residual. Use the temp vector for that
// so that we don't pollute the pseudoResidual vector for no
// good reason.
CalculateTrueResidual(matrix, temp, result, input);
// Now recheck the convergence
if (iterator.DetermineStatus(iterationNumber, result, input, temp) != IterationStatus.Continue)
{
// We're all good now.
return;
}
}
// The odd step
if (!IsEven(iterationNumber))
{
if (rho.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++;
}
}
}
}