Browse Source

added andriy's complex LA

la-knuth
Marcus Cuda 16 years ago
parent
commit
a3e51bcc93
  1. 742
      src/Numerics/LinearAlgebra/Complex/DenseMatrix.cs
  2. 1513
      src/Numerics/LinearAlgebra/Complex/DenseVector.cs
  3. 1718
      src/Numerics/LinearAlgebra/Complex/DiagonalMatrix.cs
  4. 223
      src/Numerics/LinearAlgebra/Complex/Factorization/DenseCholesky.cs
  5. 224
      src/Numerics/LinearAlgebra/Complex/Factorization/DenseLU.cs
  6. 203
      src/Numerics/LinearAlgebra/Complex/Factorization/DenseQR.cs
  7. 217
      src/Numerics/LinearAlgebra/Complex/Factorization/DenseSvd.cs
  8. 325
      src/Numerics/LinearAlgebra/Complex/Factorization/GramSchmidt.cs
  9. 268
      src/Numerics/LinearAlgebra/Complex/Factorization/UserCholesky.cs
  10. 335
      src/Numerics/LinearAlgebra/Complex/Factorization/UserLU.cs
  11. 366
      src/Numerics/LinearAlgebra/Complex/Factorization/UserQR.cs
  12. 974
      src/Numerics/LinearAlgebra/Complex/Factorization/UserSvd.cs
  13. 521
      src/Numerics/LinearAlgebra/Complex/Solvers/Iterative/BiCgStab.cs
  14. 630
      src/Numerics/LinearAlgebra/Complex/Solvers/Iterative/CompositeSolver.cs
  15. 624
      src/Numerics/LinearAlgebra/Complex/Solvers/Iterative/GpBiCg.cs
  16. 783
      src/Numerics/LinearAlgebra/Complex/Solvers/Iterative/MlkBiCgStab.cs
  17. 528
      src/Numerics/LinearAlgebra/Complex/Solvers/Iterative/TFQMR.cs
  18. 329
      src/Numerics/LinearAlgebra/Complex/Solvers/Iterator.cs
  19. 151
      src/Numerics/LinearAlgebra/Complex/Solvers/Preconditioners/Diagonal.cs
  20. 730
      src/Numerics/LinearAlgebra/Complex/Solvers/Preconditioners/Ilutp.cs
  21. 228
      src/Numerics/LinearAlgebra/Complex/Solvers/Preconditioners/IlutpElementSorter.cs
  22. 261
      src/Numerics/LinearAlgebra/Complex/Solvers/Preconditioners/IncompleteLU.cs
  23. 140
      src/Numerics/LinearAlgebra/Complex/Solvers/Preconditioners/UnitPreconditioner.cs
  24. 394
      src/Numerics/LinearAlgebra/Complex/Solvers/StopCriterium/DivergenceStopCriterium.cs
  25. 202
      src/Numerics/LinearAlgebra/Complex/Solvers/StopCriterium/FailureStopCriterium.cs
  26. 228
      src/Numerics/LinearAlgebra/Complex/Solvers/StopCriterium/IterationCountStopCriterium.cs
  27. 410
      src/Numerics/LinearAlgebra/Complex/Solvers/StopCriterium/ResidualStopCriterium.cs
  28. 1710
      src/Numerics/LinearAlgebra/Complex/SparseMatrix.cs
  29. 1695
      src/Numerics/LinearAlgebra/Complex/SparseVector.cs
  30. 13
      src/Numerics/LinearAlgebra/Double/DenseMatrix.cs
  31. 10
      src/Numerics/LinearAlgebra/Double/DenseVector.cs
  32. 9
      src/Numerics/LinearAlgebra/Double/DiagonalMatrix.cs
  33. 10
      src/Numerics/LinearAlgebra/Double/Factorization/DenseCholesky.cs
  34. 10
      src/Numerics/LinearAlgebra/Double/Factorization/SparseCholesky.cs
  35. 10
      src/Numerics/LinearAlgebra/Double/Factorization/UserCholesky.cs
  36. 9
      src/Numerics/LinearAlgebra/Double/SparseMatrix.cs
  37. 11
      src/Numerics/LinearAlgebra/Double/SparseVector.cs
  38. 28
      src/Numerics/LinearAlgebra/Generic/Factorization/Cholesky.cs
  39. 41
      src/Numerics/LinearAlgebra/Generic/Factorization/ExtensionMethods.cs
  40. 11
      src/Numerics/LinearAlgebra/Generic/Factorization/LU.cs
  41. 11
      src/Numerics/LinearAlgebra/Generic/Factorization/QR.cs
  42. 11
      src/Numerics/LinearAlgebra/Generic/Factorization/Svd.cs
  43. 9
      src/Numerics/LinearAlgebra/Generic/Matrix.cs
  44. 2
      src/Numerics/LinearAlgebra/Generic/Solvers/IIterator.cs
  45. 2
      src/Numerics/LinearAlgebra/Generic/Solvers/Preconditioners/IPreConditioner.cs
  46. 2
      src/Numerics/LinearAlgebra/Generic/Solvers/StopCriterium/IIterationStopCriterium.cs
  47. 17
      src/Numerics/LinearAlgebra/Generic/Vector.cs
  48. 31
      src/Numerics/Numerics.csproj
  49. 14
      src/Numerics/Precision.cs
  50. 93
      src/Silverlight/Silverlight.csproj
  51. 157
      src/UnitTests/LinearAlgebraTests/Complex/DenseMatrixTests.cs
  52. 100
      src/UnitTests/LinearAlgebraTests/Complex/DenseVectorTest.TextHandling.cs
  53. 291
      src/UnitTests/LinearAlgebraTests/Complex/DenseVectorTests.cs
  54. 452
      src/UnitTests/LinearAlgebraTests/Complex/DiagonalMatrixTests.cs
  55. 305
      src/UnitTests/LinearAlgebraTests/Complex/Factorization/CholeskyTests.cs
  56. 356
      src/UnitTests/LinearAlgebraTests/Complex/Factorization/GramSchmidtTests.cs
  57. 371
      src/UnitTests/LinearAlgebraTests/Complex/Factorization/LUTests.cs
  58. 343
      src/UnitTests/LinearAlgebraTests/Complex/Factorization/QRTests.cs
  59. 376
      src/UnitTests/LinearAlgebraTests/Complex/Factorization/SvdTests.cs
  60. 304
      src/UnitTests/LinearAlgebraTests/Complex/Factorization/UserCholeskyTests.cs
  61. 370
      src/UnitTests/LinearAlgebraTests/Complex/Factorization/UserLUTests.cs
  62. 342
      src/UnitTests/LinearAlgebraTests/Complex/Factorization/UserQRTests.cs
  63. 376
      src/UnitTests/LinearAlgebraTests/Complex/Factorization/UserSvdTests.cs
  64. 184
      src/UnitTests/LinearAlgebraTests/Complex/MatrixLoader.cs
  65. 894
      src/UnitTests/LinearAlgebraTests/Complex/MatrixTests.Arithmetic.cs
  66. 1499
      src/UnitTests/LinearAlgebraTests/Complex/MatrixTests.cs
  67. 268
      src/UnitTests/LinearAlgebraTests/Complex/Solvers/Iterative/BiCgStabTest.cs
  68. 270
      src/UnitTests/LinearAlgebraTests/Complex/Solvers/Iterative/GpBiCgTest.cs
  69. 266
      src/UnitTests/LinearAlgebraTests/Complex/Solvers/Iterative/MlkBiCgStabTest.cs
  70. 268
      src/UnitTests/LinearAlgebraTests/Complex/Solvers/Iterative/TFQMRTest.cs
  71. 355
      src/UnitTests/LinearAlgebraTests/Complex/Solvers/IteratorTest.cs
  72. 34
      src/UnitTests/LinearAlgebraTests/Complex/Solvers/Preconditioners/DiagonalTest.cs
  73. 502
      src/UnitTests/LinearAlgebraTests/Complex/Solvers/Preconditioners/IluptElementSorterTest.cs
  74. 254
      src/UnitTests/LinearAlgebraTests/Complex/Solvers/Preconditioners/IlutpTest.cs
  75. 87
      src/UnitTests/LinearAlgebraTests/Complex/Solvers/Preconditioners/IncompleteLUTest.cs
  76. 128
      src/UnitTests/LinearAlgebraTests/Complex/Solvers/Preconditioners/PreConditionerTest.cs
  77. 30
      src/UnitTests/LinearAlgebraTests/Complex/Solvers/Preconditioners/UnitPreconditionerTest.cs
  78. 234
      src/UnitTests/LinearAlgebraTests/Complex/Solvers/StopCriterium/DivergenceStopCriteriumTest.cs
  79. 134
      src/UnitTests/LinearAlgebraTests/Complex/Solvers/StopCriterium/FailureStopCriteriumTest.cs
  80. 96
      src/UnitTests/LinearAlgebraTests/Complex/Solvers/StopCriterium/IterationCountStopCriteriumTest.cs
  81. 262
      src/UnitTests/LinearAlgebraTests/Complex/Solvers/StopCriterium/ResidualStopCriteriumTest.cs
  82. 164
      src/UnitTests/LinearAlgebraTests/Complex/SparseMatrixTests.cs
  83. 99
      src/UnitTests/LinearAlgebraTests/Complex/SparseVectorTest.TextHandling.cs
  84. 415
      src/UnitTests/LinearAlgebraTests/Complex/SparseVectorTest.cs
  85. 215
      src/UnitTests/LinearAlgebraTests/Complex/UserDefinedMatrixTests.cs
  86. 225
      src/UnitTests/LinearAlgebraTests/Complex/UserDefinedVectorTests.cs
  87. 902
      src/UnitTests/LinearAlgebraTests/Complex/VectorTests.Arithmetic.cs
  88. 91
      src/UnitTests/LinearAlgebraTests/Complex/VectorTests.Norm.cs
  89. 398
      src/UnitTests/LinearAlgebraTests/Complex/VectorTests.cs
  90. 2
      src/UnitTests/LinearAlgebraTests/Double/Factorization/CholeskyTests.cs
  91. 1
      src/UnitTests/LinearAlgebraTests/Double/Factorization/GramSchmidtTests.cs
  92. 4
      src/UnitTests/LinearAlgebraTests/Double/Factorization/LUTests.cs
  93. 1
      src/UnitTests/LinearAlgebraTests/Double/Factorization/QRTests.cs
  94. 1
      src/UnitTests/LinearAlgebraTests/Double/Factorization/SvdTests.cs
  95. 2
      src/UnitTests/LinearAlgebraTests/Double/Factorization/UserCholeskyTests.cs
  96. 2
      src/UnitTests/LinearAlgebraTests/Double/Factorization/UserLUTests.cs
  97. 1
      src/UnitTests/LinearAlgebraTests/Double/Factorization/UserQRTests.cs
  98. 1
      src/UnitTests/LinearAlgebraTests/Double/Factorization/UserSvdTests.cs
  99. 6
      src/UnitTests/LinearAlgebraTests/Double/UserDefinedMatrixTests.cs
  100. 6
      src/UnitTests/LinearAlgebraTests/Double/UserDefinedVectorTests.cs

742
src/Numerics/LinearAlgebra/Complex/DenseMatrix.cs

@ -0,0 +1,742 @@
// <copyright file="DenseMatrix.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>
namespace MathNet.Numerics.LinearAlgebra.Complex
{
using System;
using System.Numerics;
using Distributions;
using Generic;
using Properties;
using Threading;
/// <summary>
/// A Matrix class with dense storage. The underlying storage is a one dimensional array in column-major order.
/// </summary>
public class DenseMatrix : Matrix<Complex>
{
/// <summary>
/// Initializes a new instance of the <see cref="DenseMatrix"/> class. This matrix is square with a given size.
/// </summary>
/// <param name="order">the size of the square matrix.</param>
/// <exception cref="ArgumentException">
/// If <paramref name="order"/> is less than one.
/// </exception>
public DenseMatrix(int order)
: base(order)
{
Data = new Complex[order * order];
}
/// <summary>
/// Initializes a new instance of the <see cref="DenseMatrix"/> class.
/// </summary>
/// <param name="rows">
/// The number of rows.
/// </param>
/// <param name="columns">
/// The number of columns.
/// </param>
public DenseMatrix(int rows, int columns)
: base(rows, columns)
{
Data = new Complex[rows * columns];
}
/// <summary>
/// Initializes a new instance of the <see cref="DenseMatrix"/> class with all entries set to a particular value.
/// </summary>
/// <param name="rows">
/// The number of rows.
/// </param>
/// <param name="columns">
/// The number of columns.
/// </param>
/// <param name="value">The value which we assign to each element of the matrix.</param>
public DenseMatrix(int rows, int columns, Complex value)
: base(rows, columns)
{
Data = new Complex[rows * columns];
for (var i = 0; i < Data.Length; i++)
{
Data[i] = value;
}
}
/// <summary>
/// Initializes a new instance of the <see cref="DenseMatrix"/> class from a one dimensional array. This constructor
/// will reference the one dimensional array and not copy it.
/// </summary>
/// <param name="rows">The number of rows.</param>
/// <param name="columns">The number of columns.</param>
/// <param name="array">The one dimensional array to create this matrix from. This array should store the matrix in column-major order. <seealso cref="http://en.wikipedia.org/wiki/Row-major_order"/></param>
public DenseMatrix(int rows, int columns, Complex[] array)
: base(rows, columns)
{
Data = array;
}
/// <summary>
/// Initializes a new instance of the <see cref="DenseMatrix"/> class from a 2D array. This constructor
/// will allocate a completely new memory block for storing the dense matrix.
/// </summary>
/// <param name="array">The 2D array to create this matrix from.</param>
public DenseMatrix(Complex[,] array)
: base(array.GetLength(0), array.GetLength(1))
{
var rows = array.GetLength(0);
var columns = array.GetLength(1);
Data = new Complex[rows * columns];
for (var i = 0; i < rows; i++)
{
for (var j = 0; j < columns; j++)
{
Data[(j * rows) + i] = array[i, j];
}
}
}
/// <summary>
/// Gets the matrix's data.
/// </summary>
/// <value>The matrix's data.</value>
internal Complex[] Data
{
get;
private set;
}
/// <summary>
/// Creates a <c>DenseMatrix</c> for the given number of rows and columns.
/// </summary>
/// <param name="numberOfRows">
/// The number of rows.
/// </param>
/// <param name="numberOfColumns">
/// The number of columns.
/// </param>
/// <returns>
/// A <c>DenseMatrix</c> with the given dimensions.
/// </returns>
public override Matrix<Complex> CreateMatrix(int numberOfRows, int numberOfColumns)
{
return new DenseMatrix(numberOfRows, numberOfColumns);
}
/// <summary>
/// Creates a <see cref="Vector{T}"/> with a the given dimension.
/// </summary>
/// <param name="size">The size of the vector.</param>
/// <returns>
/// A <see cref="Vector{T}"/> with the given dimension.
/// </returns>
public override Vector<Complex> CreateVector(int size)
{
return new DenseVector(size);
}
/// <summary>
/// Retrieves the requested element without range checking.
/// </summary>
/// <param name="row">
/// The row of the element.
/// </param>
/// <param name="column">
/// The column of the element.
/// </param>
/// <returns>
/// The requested element.
/// </returns>
public override Complex At(int row, int column)
{
return Data[(column * RowCount) + row];
}
/// <summary>
/// Sets the value of the given element.
/// </summary>
/// <param name="row">
/// The row of the element.
/// </param>
/// <param name="column">
/// The column of the element.
/// </param>
/// <param name="value">
/// The value to set the element to.
/// </param>
public override void At(int row, int column, Complex value)
{
Data[(column * RowCount) + row] = value;
}
/// <summary>
/// Sets all values to zero.
/// </summary>
public override void Clear()
{
Array.Clear(Data, 0, Data.Length);
}
/// <summary>
/// Returns the transpose of this matrix.
/// </summary>
/// <returns>The transpose of this matrix.</returns>
public override Matrix<Complex> Transpose()
{
var ret = new DenseMatrix(ColumnCount, RowCount);
for (var j = 0; j < ColumnCount; j++)
{
var index = j * RowCount;
for (var i = 0; i < RowCount; i++)
{
ret.Data[(i * ColumnCount) + j] = Data[index + i];
}
}
return ret;
}
/// <summary>
/// Returns the conjugate transpose of this matrix.
/// </summary>
/// <returns>The conjugate transpose of this matrix.</returns>
public override Matrix<Complex> ConjugateTranspose()
{
var ret = new DenseMatrix(ColumnCount, RowCount);
for (var j = 0; j < ColumnCount; j++)
{
var index = j * RowCount;
for (var i = 0; i < RowCount; i++)
{
ret.Data[(i * ColumnCount) + j] = Data[index + i].Conjugate();
}
}
return ret;
}
/// <summary>Calculates the L1 norm.</summary>
/// <returns>The L1 norm of the matrix.</returns>
public override double L1Norm()
{
var norm = 0.0;
for (var j = 0; j < ColumnCount; j++)
{
var s = 0.0;
for (var i = 0; i < RowCount; i++)
{
s += Data[(j * RowCount) + i].Magnitude;
}
norm = Math.Max(norm, s);
}
return norm;
}
/// <summary>Calculates the Frobenius norm of this matrix.</summary>
/// <returns>The Frobenius norm of this matrix.</returns>
public override double FrobeniusNorm()
{
var transpose = (DenseMatrix)Transpose();
var aat = this * transpose;
var norm = 0.0;
for (var i = 0; i < RowCount; i++)
{
norm += aat.Data[(i * RowCount) + i].Magnitude;
}
norm = Math.Sqrt(norm);
return norm;
}
/// <summary>Calculates the infinity norm of this matrix.</summary>
/// <returns>The infinity norm of this matrix.</returns>
public override double InfinityNorm()
{
var norm = 0.0;
for (var i = 0; i < RowCount; i++)
{
var s = 0.0;
for (var j = 0; j < ColumnCount; j++)
{
s += Data[(j * RowCount) + i].Magnitude;
}
norm = Math.Max(norm, s);
}
return norm;
}
#region Elementary operations
/// <summary>
/// Adds another matrix to this matrix. The result will be written into this matrix.
/// </summary>
/// <param name="other">The matrix to add to this matrix.</param>
/// <exception cref="ArgumentNullException">If the other matrix is <see langword="null" />.</exception>
/// <exception cref="ArgumentOutOfRangeException">If the two matrices don't have the same dimensions.</exception>
public override void Add(Matrix<Complex> other)
{
var m = other as DenseMatrix;
if (m == null)
{
base.Add(other);
}
else
{
Add(m);
}
}
/// <summary>
/// Adds another <see cref="DenseMatrix"/> to this matrix. The result will be written into this matrix.
/// </summary>
/// <param name="other">The <see cref="DenseMatrix"/> to add to this matrix.</param>
/// <exception cref="ArgumentNullException">If the other matrix is <see langword="null" />.</exception>
/// <exception cref="ArgumentOutOfRangeException">If the two matrices don't have the same dimensions.</exception>
public void Add(DenseMatrix other)
{
if (other == null)
{
throw new ArgumentNullException("other");
}
if (other.RowCount != RowCount || other.ColumnCount != ColumnCount)
{
throw new ArgumentOutOfRangeException(Resources.ArgumentMatrixDimensions);
}
Control.LinearAlgebraProvider.AddArrays(Data, other.Data, Data);
}
/// <summary>
/// Subtracts another matrix from this matrix. The result will be written into this matrix.
/// </summary>
/// <param name="other">The matrix to subtract.</param>
/// <exception cref="ArgumentNullException">If the other matrix is <see langword="null" />.</exception>
/// <exception cref="ArgumentOutOfRangeException">If the two matrices don't have the same dimensions.</exception>
public override void Subtract(Matrix<Complex> other)
{
var m = other as DenseMatrix;
if (m == null)
{
base.Subtract(other);
}
else
{
Subtract(m);
}
}
/// <summary>
/// Subtracts another <see cref="DenseMatrix"/> from this matrix. The result will be written into this matrix.
/// </summary>
/// <param name="other">The <see cref="DenseMatrix"/> to subtract.</param>
/// <exception cref="ArgumentNullException">If the other matrix is <see langword="null" />.</exception>
/// <exception cref="ArgumentOutOfRangeException">If the two matrices don't have the same dimensions.</exception>
public void Subtract(DenseMatrix other)
{
if (other == null)
{
throw new ArgumentNullException("other");
}
if (other.RowCount != RowCount || other.ColumnCount != ColumnCount)
{
throw new ArgumentOutOfRangeException(Resources.ArgumentMatrixDimensions);
}
Control.LinearAlgebraProvider.SubtractArrays(Data, other.Data, Data);
}
/// <summary>
/// Multiplies each element of this matrix with a complex.
/// </summary>
/// <param name="complex">The complex to multiply with.</param>
public override void Multiply(Complex complex)
{
Control.LinearAlgebraProvider.ScaleArray(complex, Data);
}
/// <summary>
/// Multiplies this dense matrix with another dense matrix and places the results into the result dense matrix.
/// </summary>
/// <param name="other">The matrix to multiply with.</param>
/// <param name="result">The result of the multiplication.</param>
/// <exception cref="ArgumentNullException">If the other matrix is <see langword="null" />.</exception>
/// <exception cref="ArgumentNullException">If the result matrix is <see langword="null" />.</exception>
/// <exception cref="ArgumentException">If <strong>this.Columns != other.Rows</strong>.</exception>
/// <exception cref="ArgumentException">If the result matrix's dimensions are not the this.Rows x other.Columns.</exception>
public override void Multiply(Matrix<Complex> other, Matrix<Complex> result)
{
if (other == null)
{
throw new ArgumentNullException("other");
}
if (result == null)
{
throw new ArgumentNullException("result");
}
if (ColumnCount != other.RowCount)
{
throw new ArgumentException(Resources.ArgumentMatrixDimensions);
}
if (result.RowCount != RowCount || result.ColumnCount != other.ColumnCount)
{
throw new ArgumentException(Resources.ArgumentMatrixDimensions);
}
var m = other as DenseMatrix;
var r = result as DenseMatrix;
if (m == null || r == null)
{
base.Multiply(other, result);
}
else
{
Control.LinearAlgebraProvider.MatrixMultiply(
Data,
RowCount,
ColumnCount,
m.Data,
m.RowCount,
m.ColumnCount,
r.Data);
}
}
/// <summary>
/// Multiplies this matrix with another matrix and returns the result.
/// </summary>
/// <param name="other">The matrix to multiply with.</param>
/// <exception cref="ArgumentException">If <strong>this.Columns != other.Rows</strong>.</exception>
/// <exception cref="ArgumentNullException">If the other matrix is <see langword="null" />.</exception>
/// <returns>The result of multiplication.</returns>
public override Matrix<Complex> Multiply(Matrix<Complex> other)
{
if (other == null)
{
throw new ArgumentNullException("other");
}
if (ColumnCount != other.RowCount)
{
throw new ArgumentException(Resources.ArgumentMatrixDimensions);
}
var m = other as DenseMatrix;
if (m == null)
{
return base.Multiply(other);
}
var result = (DenseMatrix)CreateMatrix(RowCount, other.ColumnCount);
Multiply(other, result);
return result;
}
/// <summary>
/// Multiplies this dense matrix with transpose of another dense matrix and places the results into the result dense matrix.
/// </summary>
/// <param name="other">The matrix to multiply with.</param>
/// <param name="result">The result of the multiplication.</param>
/// <exception cref="ArgumentNullException">If the other matrix is <see langword="null" />.</exception>
/// <exception cref="ArgumentNullException">If the result matrix is <see langword="null" />.</exception>
/// <exception cref="ArgumentException">If <strong>this.Columns != other.Rows</strong>.</exception>
/// <exception cref="ArgumentException">If the result matrix's dimensions are not the this.Rows x other.Columns.</exception>
public override void TransposeAndMultiply(Matrix<Complex> other, Matrix<Complex> result)
{
var otherDense = other as DenseMatrix;
var resultDense = result as DenseMatrix;
if (otherDense == null || resultDense == null)
{
base.TransposeAndMultiply(other, result);
return;
}
if (ColumnCount != otherDense.ColumnCount)
{
throw new ArgumentException(Resources.ArgumentMatrixDimensions);
}
if ((resultDense.RowCount != RowCount) || (resultDense.ColumnCount != otherDense.RowCount))
{
throw new ArgumentException(Resources.ArgumentMatrixDimensions);
}
Control.LinearAlgebraProvider.MatrixMultiplyWithUpdate(
Algorithms.LinearAlgebra.Transpose.DontTranspose,
Algorithms.LinearAlgebra.Transpose.Transpose,
1.0,
Data,
RowCount,
ColumnCount,
otherDense.Data,
otherDense.RowCount,
otherDense.ColumnCount,
1.0,
resultDense.Data);
}
/// <summary>
/// Multiplies this matrix with transpose of another matrix and returns the result.
/// </summary>
/// <param name="other">The matrix to multiply with.</param>
/// <exception cref="ArgumentException">If <strong>this.Columns != other.Rows</strong>.</exception>
/// <exception cref="ArgumentNullException">If the other matrix is <see langword="null" />.</exception>
/// <returns>The result of multiplication.</returns>
public override Matrix<Complex> TransposeAndMultiply(Matrix<Complex> other)
{
var otherDense = other as DenseMatrix;
if (otherDense == null)
{
return base.TransposeAndMultiply(other);
}
if (ColumnCount != otherDense.ColumnCount)
{
throw new ArgumentException(Resources.ArgumentMatrixDimensions);
}
var result = (DenseMatrix)CreateMatrix(RowCount, other.RowCount);
TransposeAndMultiply(other, result);
return result;
}
/// <summary>
/// Multiplies two dense matrices.
/// </summary>
/// <param name="leftSide">The left matrix to multiply.</param>
/// <param name="rightSide">The right matrix to multiply.</param>
/// <returns>The result of multiplication.</returns>
/// <exception cref="ArgumentNullException">If <paramref name="leftSide"/> or <paramref name="rightSide"/> is <see langword="null" />.</exception>
/// <exception cref="ArgumentException">If the dimensions of <paramref name="leftSide"/> or <paramref name="rightSide"/> don't conform.</exception>
public static DenseMatrix operator *(DenseMatrix leftSide, DenseMatrix rightSide)
{
if (leftSide == null)
{
throw new ArgumentNullException("leftSide");
}
if (rightSide == null)
{
throw new ArgumentNullException("rightSide");
}
if (leftSide.ColumnCount != rightSide.RowCount)
{
throw new ArgumentException(Resources.ArgumentMatrixDimensions);
}
return (DenseMatrix)leftSide.Multiply(rightSide);
}
#endregion
#region Static constructors for special matrices.
/// <summary>
/// Initializes a square <see cref="DenseMatrix"/> with all zero's except for ones on the diagonal.
/// </summary>
/// <param name="order">the size of the square matrix.</param>
/// <returns>A dense identity matrix.</returns>
/// <exception cref="ArgumentException">
/// If <paramref name="order"/> is less than one.
/// </exception>
public static DenseMatrix Identity(int order)
{
var m = new DenseMatrix(order);
for (var i = 0; i < order; i++)
{
m[i, i] = Complex.One;
}
return m;
}
#endregion
/// <summary>
/// Negate each element of this matrix.
/// </summary>
/// <exception cref="ArgumentNullException">If the result matrix is <see langword="null" />.</exception>
/// <exception cref="ArgumentException">if the result matrix's dimensions are not the same as this matrix.</exception>
public override void Negate()
{
Multiply(-1);
}
/// <summary>
/// Generates matrix with random elements.
/// </summary>
/// <param name="numberOfRows">Number of rows.</param>
/// <param name="numberOfColumns">Number of columns.</param>
/// <param name="distribution">Continuous Random Distribution or Source</param>
/// <returns>
/// An <c>numberOfRows</c>-by-<c>numberOfColumns</c> matrix with elements distributed according to the provided distribution.
/// </returns>
/// <exception cref="ArgumentException">If the parameter <paramref name="numberOfRows"/> is not positive.</exception>
/// <exception cref="ArgumentException">If the parameter <paramref name="numberOfColumns"/> is not positive.</exception>
public override Matrix<Complex> Random(int numberOfRows, int numberOfColumns, IContinuousDistribution distribution)
{
if (numberOfRows < 1)
{
throw new ArgumentException(Resources.ArgumentMustBePositive, "numberOfRows");
}
if (numberOfColumns < 1)
{
throw new ArgumentException(Resources.ArgumentMustBePositive, "numberOfColumns");
}
var matrix = CreateMatrix(numberOfRows, numberOfColumns);
CommonParallel.For(
0,
ColumnCount,
j =>
{
for (var i = 0; i < matrix.RowCount; i++)
{
matrix[i, j] = new Complex(distribution.Sample(), distribution.Sample());
}
});
return matrix;
}
/// <summary>
/// Generates matrix with random elements.
/// </summary>
/// <param name="numberOfRows">Number of rows.</param>
/// <param name="numberOfColumns">Number of columns.</param>
/// <param name="distribution">Continuous Random Distribution or Source</param>
/// <returns>
/// An <c>numberOfRows</c>-by-<c>numberOfColumns</c> matrix with elements distributed according to the provided distribution.
/// </returns>
/// <exception cref="ArgumentException">If the parameter <paramref name="numberOfRows"/> is not positive.</exception>
/// <exception cref="ArgumentException">If the parameter <paramref name="numberOfColumns"/> is not positive.</exception>
public override Matrix<Complex> Random(int numberOfRows, int numberOfColumns, IDiscreteDistribution distribution)
{
if (numberOfRows < 1)
{
throw new ArgumentException(Resources.ArgumentMustBePositive, "numberOfRows");
}
if (numberOfColumns < 1)
{
throw new ArgumentException(Resources.ArgumentMustBePositive, "numberOfColumns");
}
var matrix = CreateMatrix(numberOfRows, numberOfColumns);
CommonParallel.For(
0,
ColumnCount,
j =>
{
for (var i = 0; i < matrix.RowCount; i++)
{
matrix[i, j] = new Complex(distribution.Sample(), distribution.Sample());
}
});
return matrix;
}
#region Simple arithmetic of type T
/// <summary>
/// Add two values T+T
/// </summary>
/// <param name="val1">Left operand value</param>
/// <param name="val2">Right operand value</param>
/// <returns>Result of addition</returns>
protected sealed override Complex AddT(Complex val1, Complex val2)
{
return val1 + val2;
}
/// <summary>
/// Subtract two values T-T
/// </summary>
/// <param name="val1">Left operand value</param>
/// <param name="val2">Right operand value</param>
/// <returns>Result of subtract</returns>
protected sealed override Complex SubtractT(Complex val1, Complex val2)
{
return val1 - val2;
}
/// <summary>
/// Multiply two values T*T
/// </summary>
/// <param name="val1">Left operand value</param>
/// <param name="val2">Right operand value</param>
/// <returns>Result of multiplication</returns>
protected sealed override Complex MultiplyT(Complex val1, Complex val2)
{
return val1 * val2;
}
/// <summary>
/// Divide two values T/T
/// </summary>
/// <param name="val1">Left operand value</param>
/// <param name="val2">Right operand value</param>
/// <returns>Result of divide</returns>
protected sealed override Complex DivideT(Complex val1, Complex val2)
{
return val1 / val2;
}
/// <summary>
/// Is equal to one?
/// </summary>
/// <param name="val1">Value to check</param>
/// <returns>True if one; otherwise false</returns>
protected sealed override bool IsOneT(Complex val1)
{
return val1.AlmostEqual(Complex.One);
}
/// <summary>
/// Take absolute value
/// </summary>
/// <param name="val1">Source alue</param>
/// <returns>True if one; otherwise false</returns>
protected sealed override double AbsoluteT(Complex val1)
{
return val1.Magnitude;
}
#endregion
}
}

1513
src/Numerics/LinearAlgebra/Complex/DenseVector.cs

File diff suppressed because it is too large

1718
src/Numerics/LinearAlgebra/Complex/DiagonalMatrix.cs

File diff suppressed because it is too large

223
src/Numerics/LinearAlgebra/Complex/Factorization/DenseCholesky.cs

@ -0,0 +1,223 @@
// <copyright file="DenseCholesky.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>
namespace MathNet.Numerics.LinearAlgebra.Complex.Factorization
{
using System;
using System.Numerics;
using Generic;
using Generic.Factorization;
using Properties;
using Threading;
/// <summary>
/// <para>A class which encapsulates the functionality of a Cholesky factorization for dense matrices.</para>
/// <para>For a symmetric, positive definite matrix A, the Cholesky factorization
/// is an lower triangular matrix L so that A = L*L'.</para>
/// </summary>
/// <remarks>
/// The computation of the Cholesky factorization is done at construction time. If the matrix is not symmetric
/// or positive definite, the constructor will throw an exception.
/// </remarks>
public class DenseCholesky : Cholesky<Complex>
{
/// <summary>
/// Initializes a new instance of the <see cref="DenseCholesky"/> class. This object will compute the
/// Cholesky factorization when the constructor is called and cache it's factorization.
/// </summary>
/// <param name="matrix">The matrix to factor.</param>
/// <exception cref="ArgumentNullException">If <paramref name="matrix"/> is <c>null</c>.</exception>
/// <exception cref="ArgumentException">If <paramref name="matrix"/> is not a square matrix.</exception>
/// <exception cref="ArgumentException">If <paramref name="matrix"/> is not positive definite.</exception>
public DenseCholesky(DenseMatrix matrix)
{
if (matrix == null)
{
throw new ArgumentNullException("matrix");
}
if (matrix.RowCount != matrix.ColumnCount)
{
throw new ArgumentException(Resources.ArgumentMatrixSquare);
}
// Create a new matrix for the Cholesky factor, then perform factorization (while overwriting).
var factor = (DenseMatrix)matrix.Clone();
Control.LinearAlgebraProvider.CholeskyFactor(factor.Data, factor.RowCount);
CholeskyFactor = factor;
}
/// <summary>
/// Solves a system of linear equations, <b>AX = B</b>, with A Cholesky factorized.
/// </summary>
/// <param name="input">The right hand side <see cref="Matrix{T}"/>, <b>B</b>.</param>
/// <param name="result">The left hand side <see cref="Matrix{T}"/>, <b>X</b>.</param>
public override void Solve(Matrix<Complex> input, Matrix<Complex> result)
{
// Check for proper arguments.
if (input == null)
{
throw new ArgumentNullException("input");
}
if (result == null)
{
throw new ArgumentNullException("result");
}
// Check for proper dimensions.
if (result.RowCount != input.RowCount)
{
throw new ArgumentException(Resources.ArgumentMatrixSameRowDimension);
}
if (result.ColumnCount != input.ColumnCount)
{
throw new ArgumentException(Resources.ArgumentMatrixSameColumnDimension);
}
if (input.RowCount != CholeskyFactor.RowCount)
{
throw new ArgumentException(Resources.ArgumentMatrixDimensions);
}
var dinput = input as DenseMatrix;
if (dinput == null)
{
throw new NotImplementedException("Can only do Cholesky factorization for dense matrices at the moment.");
}
var dresult = result as DenseMatrix;
if (dresult == null)
{
throw new NotImplementedException("Can only do Cholesky factorization for dense matrices at the moment.");
}
// Copy the contents of input to result.
CommonParallel.For(0, dinput.Data.Length, index => dresult.Data[index] = dinput.Data[index]);
// Cholesky solve by overwriting result.
var dfactor = (DenseMatrix)CholeskyFactor;
Control.LinearAlgebraProvider.CholeskySolveFactored(dfactor.Data, dfactor.RowCount, dresult.Data, dresult.RowCount, dresult.ColumnCount);
}
/// <summary>
/// Solves a system of linear equations, <b>Ax = b</b>, with A Cholesky factorized.
/// </summary>
/// <param name="input">The right hand side vector, <b>b</b>.</param>
/// <param name="result">The left hand side <see cref="Matrix{T}"/>, <b>x</b>.</param>
public override void Solve(Vector<Complex> input, Vector<Complex> result)
{
// Check for proper arguments.
if (input == null)
{
throw new ArgumentNullException("input");
}
if (result == null)
{
throw new ArgumentNullException("result");
}
// Check for proper dimensions.
if (input.Count != result.Count)
{
throw new ArgumentException(Resources.ArgumentVectorsSameLength);
}
if (input.Count != CholeskyFactor.RowCount)
{
throw new ArgumentException(Resources.ArgumentMatrixDimensions);
}
var dinput = input as DenseVector;
if (dinput == null)
{
throw new NotImplementedException("Can only do Cholesky factorization for dense vectors at the moment.");
}
var dresult = result as DenseVector;
if (dresult == null)
{
throw new NotImplementedException("Can only do Cholesky factorization for dense vectors at the moment.");
}
// Copy the contents of input to result.
CommonParallel.For(0, dinput.Data.Length, index => dresult.Data[index] = dinput.Data[index]);
// Cholesky solve by overwriting result.
var dfactor = (DenseMatrix)CholeskyFactor;
Control.LinearAlgebraProvider.CholeskySolveFactored(dfactor.Data, dfactor.RowCount, dresult.Data, dresult.Count, 1);
}
#region Simple arithmetic of type T
/// <summary>
/// Add two values T+T
/// </summary>
/// <param name="val1">Left operand value</param>
/// <param name="val2">Right operand value</param>
/// <returns>Result of addition</returns>
protected sealed override Complex AddT(Complex val1, Complex val2)
{
return val1 + val2;
}
/// <summary>
/// Multiply two values T*T
/// </summary>
/// <param name="val1">Left operand value</param>
/// <param name="val2">Right operand value</param>
/// <returns>Result of multiplication</returns>
protected sealed override Complex MultiplyT(Complex val1, Complex val2)
{
return val1 * val2;
}
/// <summary>
/// Returns the natural (base e) logarithm of a specified number.
/// </summary>
/// <param name="val1"> A number whose logarithm is to be found</param>
/// <returns>Natural (base e) logarithm </returns>
protected sealed override Complex LogT(Complex val1)
{
return val1.NaturalLogarithm();
}
/// <summary>
/// Get value of type T equal to one
/// </summary>
/// <returns>One value</returns>
protected sealed override Complex OneValueT
{
get { return Complex.One; }
}
#endregion
}
}

224
src/Numerics/LinearAlgebra/Complex/Factorization/DenseLU.cs

@ -0,0 +1,224 @@
// <copyright file="DenseLU.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>
namespace MathNet.Numerics.LinearAlgebra.Complex.Factorization
{
using System;
using System.Numerics;
using Generic;
using Generic.Factorization;
using Properties;
using Threading;
/// <summary>
/// <para>A class which encapsulates the functionality of an LU factorization.</para>
/// <para>For a matrix A, the LU factorization is a pair of lower triangular matrix L and
/// upper triangular matrix U so that A = L*U.</para>
/// </summary>
/// <remarks>
/// The computation of the LU factorization is done at construction time.
/// </remarks>
public class DenseLU : LU<Complex>
{
/// <summary>
/// Initializes a new instance of the <see cref="DenseLU"/> class. This object will compute the
/// LU factorization when the constructor is called and cache it's factorization.
/// </summary>
/// <param name="matrix">The matrix to factor.</param>
/// <exception cref="ArgumentNullException">If <paramref name="matrix"/> is <c>null</c>.</exception>
/// <exception cref="ArgumentException">If <paramref name="matrix"/> is not a square matrix.</exception>
public DenseLU(DenseMatrix matrix)
{
if (matrix == null)
{
throw new ArgumentNullException("matrix");
}
if (matrix.RowCount != matrix.ColumnCount)
{
throw new ArgumentException(Resources.ArgumentMatrixSquare);
}
// Create an array for the pivot indices.
Pivots = new int[matrix.RowCount];
// Create a new matrix for the LU factors, then perform factorization (while overwriting).
var factors = (DenseMatrix)matrix.Clone();
Control.LinearAlgebraProvider.LUFactor(factors.Data, factors.RowCount, Pivots);
Factors = factors;
}
/// <summary>
/// Solves a system of linear equations, <c>AX = B</c>, with A LU factorized.
/// </summary>
/// <param name="input">The right hand side <see cref="Matrix{T}"/>, <c>B</c>.</param>
/// <param name="result">The left hand side <see cref="Matrix{T}"/>, <c>X</c>.</param>
public override void Solve(Matrix<Complex> input, Matrix<Complex> result)
{
// Check for proper arguments.
if (input == null)
{
throw new ArgumentNullException("input");
}
if (result == null)
{
throw new ArgumentNullException("result");
}
// Check for proper dimensions.
if (result.RowCount != input.RowCount)
{
throw new ArgumentException(Resources.ArgumentMatrixSameRowDimension);
}
if (result.ColumnCount != input.ColumnCount)
{
throw new ArgumentException(Resources.ArgumentMatrixSameColumnDimension);
}
if (input.RowCount != Factors.RowCount)
{
throw new ArgumentException(Resources.ArgumentMatrixDimensions);
}
var dinput = input as DenseMatrix;
if (dinput == null)
{
throw new NotImplementedException("Can only do LU factorization for dense matrices at the moment.");
}
var dresult = result as DenseMatrix;
if (dresult == null)
{
throw new NotImplementedException("Can only do LU factorization for dense matrices at the moment.");
}
// Copy the contents of input to result.
CommonParallel.For(0, dinput.Data.Length, index => dresult.Data[index] = dinput.Data[index]);
// LU solve by overwriting result.
var dfactors = (DenseMatrix)Factors;
Control.LinearAlgebraProvider.LUSolveFactored(input.ColumnCount, dfactors.Data, dfactors.RowCount, Pivots, dresult.Data);
}
/// <summary>
/// Solves a system of linear equations, <c>Ax = b</c>, with A LU factorized.
/// </summary>
/// <param name="input">The right hand side vector, <c>b</c>.</param>
/// <param name="result">The left hand side <see cref="Matrix{T}"/>, <c>x</c>.</param>
public override void Solve(Vector<Complex> input, Vector<Complex> result)
{
// Check for proper arguments.
if (input == null)
{
throw new ArgumentNullException("input");
}
if (result == null)
{
throw new ArgumentNullException("result");
}
// Check for proper dimensions.
if (input.Count != result.Count)
{
throw new ArgumentException(Resources.ArgumentVectorsSameLength);
}
if (input.Count != Factors.RowCount)
{
throw new ArgumentException(Resources.ArgumentMatrixDimensions);
}
var dinput = input as DenseVector;
if (dinput == null)
{
throw new NotImplementedException("Can only do LU factorization for dense vectors at the moment.");
}
var dresult = result as DenseVector;
if (dresult == null)
{
throw new NotImplementedException("Can only do LU factorization for dense vectors at the moment.");
}
// Copy the contents of input to result.
CommonParallel.For(0, dinput.Data.Length, index => dresult.Data[index] = dinput.Data[index]);
// LU solve by overwriting result.
var dfactors = (DenseMatrix)Factors;
Control.LinearAlgebraProvider.LUSolveFactored(1, dfactors.Data, dfactors.RowCount, Pivots, dresult.Data);
}
/// <summary>
/// Returns the inverse of this matrix. The inverse is calculated using LU decomposition.
/// </summary>
/// <returns>The inverse of this matrix.</returns>
public override Matrix<Complex> Inverse()
{
var result = (DenseMatrix)Factors.Clone();
Control.LinearAlgebraProvider.LUInverseFactored(result.Data, result.RowCount, Pivots);
return result;
}
#region Simple arithmetic of type T
/// <summary>
/// Multiply two values T*T
/// </summary>
/// <param name="val1">Left operand value</param>
/// <param name="val2">Right operand value</param>
/// <returns>Result of multiplication</returns>
protected sealed override Complex MultiplyT(Complex val1, Complex val2)
{
return val1 * val2;
}
/// <summary>
/// Get value of type T equal to one
/// </summary>
/// <returns>One value</returns>
protected sealed override Complex OneValueT
{
get { return Complex.One; }
}
/// <summary>
/// Get value of type T equal to minus one
/// </summary>
/// <returns>One value</returns>
protected sealed override Complex MinusOneValueT
{
get { return -Complex.One; }
}
#endregion
}
}

203
src/Numerics/LinearAlgebra/Complex/Factorization/DenseQR.cs

@ -0,0 +1,203 @@
// <copyright file="DenseQR.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>
namespace MathNet.Numerics.LinearAlgebra.Complex.Factorization
{
using System;
using System.Numerics;
using Generic;
using Generic.Factorization;
using Properties;
/// <summary>
/// <para>A class which encapsulates the functionality of the QR decomposition.</para>
/// <para>Any real square matrix A may be decomposed as A = QR where Q is an orthogonal matrix
/// (its columns are orthogonal unit vectors meaning QTQ = I) and R is an upper triangular matrix
/// (also called right triangular matrix).</para>
/// </summary>
/// <remarks>
/// The computation of the QR decomposition is done at construction time by Householder transformation.
/// </remarks>
public class DenseQR : QR<Complex>
{
/// <summary>
/// Initializes a new instance of the <see cref="DenseQR"/> class. This object will compute the
/// QR factorization when the constructor is called and cache it's factorization.
/// </summary>
/// <param name="matrix">The matrix to factor.</param>
/// <exception cref="ArgumentNullException">If <paramref name="matrix"/> is <c>null</c>.</exception>
/// <exception cref="ArgumentException">If <paramref name="matrix"/> row count is less then column count</exception>
public DenseQR(DenseMatrix matrix)
{
if (matrix == null)
{
throw new ArgumentNullException("matrix");
}
if (matrix.RowCount < matrix.ColumnCount)
{
throw new ArgumentException(Resources.ArgumentMatrixDimensions);
}
MatrixR = matrix.Clone();
MatrixQ = new DenseMatrix(matrix.RowCount);
Control.LinearAlgebraProvider.QRFactor(((DenseMatrix)MatrixR).Data, matrix.RowCount, matrix.ColumnCount, ((DenseMatrix)MatrixQ).Data);
}
/// <summary>
/// Solves a system of linear equations, <b>AX = B</b>, with A QR factorized.
/// </summary>
/// <param name="input">The right hand side <see cref="Matrix{T}"/>, <b>B</b>.</param>
/// <param name="result">The left hand side <see cref="Matrix{T}"/>, <b>X</b>.</param>
public override void Solve(Matrix<Complex> input, Matrix<Complex> result)
{
// Check for proper arguments.
if (input == null)
{
throw new ArgumentNullException("input");
}
if (result == null)
{
throw new ArgumentNullException("result");
}
// The solution X should have the same number of columns as B
if (input.ColumnCount != result.ColumnCount)
{
throw new ArgumentException(Resources.ArgumentMatrixSameColumnDimension);
}
// The dimension compatibility conditions for X = A\B require the two matrices A and B to have the same number of rows
if (MatrixR.RowCount != input.RowCount)
{
throw new ArgumentException(Resources.ArgumentMatrixSameRowDimension);
}
// The solution X row dimension is equal to the column dimension of A
if (MatrixR.ColumnCount != result.RowCount)
{
throw new ArgumentException(Resources.ArgumentMatrixSameColumnDimension);
}
var dinput = input as DenseMatrix;
if (dinput == null)
{
throw new NotImplementedException("Can only do QR factorization for dense matrices at the moment.");
}
var dresult = result as DenseMatrix;
if (dresult == null)
{
throw new NotImplementedException("Can only do QR factorization for dense matrices at the moment.");
}
Control.LinearAlgebraProvider.QRSolveFactored(((DenseMatrix)MatrixQ).Data, ((DenseMatrix)MatrixR).Data, MatrixR.RowCount, MatrixR.ColumnCount, dinput.Data, input.ColumnCount, dresult.Data);
}
/// <summary>
/// Solves a system of linear equations, <b>Ax = b</b>, with A QR factorized.
/// </summary>
/// <param name="input">The right hand side vector, <b>b</b>.</param>
/// <param name="result">The left hand side <see cref="Matrix{T}"/>, <b>x</b>.</param>
public override void Solve(Vector<Complex> input, Vector<Complex> result)
{
if (input == null)
{
throw new ArgumentNullException("input");
}
if (result == null)
{
throw new ArgumentNullException("result");
}
// Ax=b where A is an m x n matrix
// Check that b is a column vector with m entries
if (MatrixR.RowCount != input.Count)
{
throw new ArgumentException(Resources.ArgumentVectorsSameLength);
}
// Check that x is a column vector with n entries
if (MatrixR.ColumnCount != result.Count)
{
throw new ArgumentException(Resources.ArgumentMatrixDimensions);
}
var dinput = input as DenseVector;
if (dinput == null)
{
throw new NotImplementedException("Can only do QR factorization for dense vectors at the moment.");
}
var dresult = result as DenseVector;
if (dresult == null)
{
throw new NotImplementedException("Can only do QR factorization for dense vectors at the moment.");
}
Control.LinearAlgebraProvider.QRSolveFactored(((DenseMatrix)MatrixQ).Data, ((DenseMatrix)MatrixR).Data, MatrixR.RowCount, MatrixR.ColumnCount, dinput.Data, 1, dresult.Data);
}
#region Simple arithmetic of type T
/// <summary>
/// Multiply two values T*T
/// </summary>
/// <param name="val1">Left operand value</param>
/// <param name="val2">Right operand value</param>
/// <returns>Result of multiplication</returns>
protected sealed override Complex MultiplyT(Complex val1, Complex val2)
{
return val1 * val2;
}
/// <summary>
/// Returns the absolute value of a specified number.
/// </summary>
/// <param name="val1"> A number whose absolute is to be found</param>
/// <returns>Absolute value </returns>
protected sealed override double AbsoluteT(Complex val1)
{
return val1.Magnitude;
}
/// <summary>
/// Get value of type T equal to one
/// </summary>
/// <returns>One value</returns>
protected sealed override Complex OneValueT
{
get { return Complex.One; }
}
#endregion
}
}

217
src/Numerics/LinearAlgebra/Complex/Factorization/DenseSvd.cs

@ -0,0 +1,217 @@
// <copyright file="DenseSvd.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>
namespace MathNet.Numerics.LinearAlgebra.Complex.Factorization
{
using System;
using System.Numerics;
using Generic;
using Generic.Factorization;
using Properties;
/// <summary>
/// <para>A class which encapsulates the functionality of the singular value decomposition (SVD) for <see cref="DenseMatrix"/>.</para>
/// <para>Suppose M is an m-by-n matrix whose entries are real numbers.
/// Then there exists a factorization of the form M = UΣVT where:
/// - U is an m-by-m unitary matrix;
/// - Σ is m-by-n diagonal matrix with nonnegative real numbers on the diagonal;
/// - VT denotes transpose of V, an n-by-n unitary matrix;
/// Such a factorization is called a singular-value decomposition of M. A common convention is to order the diagonal
/// entries Σ(i,i) in descending order. In this case, the diagonal matrix Σ is uniquely determined
/// by M (though the matrices U and V are not). The diagonal entries of Σ are known as the singular values of M.</para>
/// </summary>
/// <remarks>
/// The computation of the singular value decomposition is done at construction time.
/// </remarks>
public class DenseSvd : Svd<Complex>
{
/// <summary>
/// Initializes a new instance of the <see cref="DenseSvd"/> class. This object will compute the
/// the singular value decomposition when the constructor is called and cache it's decomposition.
/// </summary>
/// <param name="matrix">The matrix to factor.</param>
/// <param name="computeVectors">Compute the singular U and VT vectors or not.</param>
/// <exception cref="ArgumentNullException">If <paramref name="matrix"/> is <b>null</b>.</exception>
/// <exception cref="ArgumentException">If SVD algorithm failed to converge with matrix <paramref name="matrix"/>.</exception>
public DenseSvd(DenseMatrix matrix, bool computeVectors)
{
if (matrix == null)
{
throw new ArgumentNullException("matrix");
}
ComputeVectors = computeVectors;
var nm = Math.Min(matrix.RowCount, matrix.ColumnCount);
VectorS = new DenseVector(nm);
MatrixU = new DenseMatrix(matrix.RowCount);
MatrixVT = new DenseMatrix(matrix.ColumnCount);
Control.LinearAlgebraProvider.SingularValueDecomposition(computeVectors, ((DenseMatrix)matrix.Clone()).Data, matrix.RowCount, matrix.ColumnCount, ((DenseVector)VectorS).Data, ((DenseMatrix)MatrixU).Data, ((DenseMatrix)MatrixVT).Data);
}
/// <summary>
/// Solves a system of linear equations, <b>AX = B</b>, with A SVD factorized.
/// </summary>
/// <param name="input">The right hand side <see cref="Matrix{T}"/>, <b>B</b>.</param>
/// <param name="result">The left hand side <see cref="Matrix{T}"/>, <b>X</b>.</param>
public override void Solve(Matrix<Complex> input, Matrix<Complex> result)
{
// Check for proper arguments.
if (input == null)
{
throw new ArgumentNullException("input");
}
if (result == null)
{
throw new ArgumentNullException("result");
}
if (!ComputeVectors)
{
throw new InvalidOperationException(Resources.SingularVectorsNotComputed);
}
// The solution X should have the same number of columns as B
if (input.ColumnCount != result.ColumnCount)
{
throw new ArgumentException(Resources.ArgumentMatrixSameColumnDimension);
}
// The dimension compatibility conditions for X = A\B require the two matrices A and B to have the same number of rows
if (MatrixU.RowCount != input.RowCount)
{
throw new ArgumentException(Resources.ArgumentMatrixSameRowDimension);
}
// The solution X row dimension is equal to the column dimension of A
if (MatrixVT.ColumnCount != result.RowCount)
{
throw new ArgumentException(Resources.ArgumentMatrixSameColumnDimension);
}
var dinput = input as DenseMatrix;
if (dinput == null)
{
throw new NotImplementedException("Can only do SVD factorization for dense matrices at the moment.");
}
var dresult = result as DenseMatrix;
if (dresult == null)
{
throw new NotImplementedException("Can only do SVD factorization for dense matrices at the moment.");
}
Control.LinearAlgebraProvider.SvdSolveFactored(MatrixU.RowCount, MatrixVT.ColumnCount, ((DenseVector)VectorS).Data, ((DenseMatrix)MatrixU).Data, ((DenseMatrix)MatrixVT).Data, dinput.Data, input.ColumnCount, dresult.Data);
}
/// <summary>
/// Solves a system of linear equations, <b>Ax = b</b>, with A SVD factorized.
/// </summary>
/// <param name="input">The right hand side vector, <b>b</b>.</param>
/// <param name="result">The left hand side <see cref="Matrix{T}"/>, <b>x</b>.</param>
public override void Solve(Vector<Complex> input, Vector<Complex> result)
{
if (input == null)
{
throw new ArgumentNullException("input");
}
if (result == null)
{
throw new ArgumentNullException("result");
}
if (!ComputeVectors)
{
throw new InvalidOperationException(Resources.SingularVectorsNotComputed);
}
// Ax=b where A is an m x n matrix
// Check that b is a column vector with m entries
if (MatrixU.RowCount != input.Count)
{
throw new ArgumentException(Resources.ArgumentVectorsSameLength);
}
// Check that x is a column vector with n entries
if (MatrixVT.ColumnCount != result.Count)
{
throw new ArgumentException(Resources.ArgumentMatrixDimensions);
}
var dinput = input as DenseVector;
if (dinput == null)
{
throw new NotImplementedException("Can only do SVD factorization for dense vectors at the moment.");
}
var dresult = result as DenseVector;
if (dresult == null)
{
throw new NotImplementedException("Can only do SVD factorization for dense vectors at the moment.");
}
Control.LinearAlgebraProvider.SvdSolveFactored(MatrixU.RowCount, MatrixVT.ColumnCount, ((DenseVector)VectorS).Data, ((DenseMatrix)MatrixU).Data, ((DenseMatrix)MatrixVT).Data, dinput.Data, 1, dresult.Data);
}
#region Simple arithmetic of type T
/// <summary>
/// Multiply two values T*T
/// </summary>
/// <param name="val1">Left operand value</param>
/// <param name="val2">Right operand value</param>
/// <returns>Result of multiplication</returns>
protected sealed override Complex MultiplyT(Complex val1, Complex val2)
{
return val1 * val2;
}
/// <summary>
/// Returns the absolute value of a specified number.
/// </summary>
/// <param name="val1"> A number whose absolute is to be found</param>
/// <returns>Absolute value </returns>
protected sealed override double AbsoluteT(Complex val1)
{
return val1.Magnitude;
}
/// <summary>
/// Get value of type T equal to one
/// </summary>
/// <returns>One value</returns>
protected sealed override Complex OneValueT
{
get { return Complex.One; }
}
#endregion
}
}

325
src/Numerics/LinearAlgebra/Complex/Factorization/GramSchmidt.cs

@ -0,0 +1,325 @@
// <copyright file="GramSchmidt.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>
namespace MathNet.Numerics.LinearAlgebra.Complex.Factorization
{
using System;
using System.Numerics;
using Generic;
using Generic.Factorization;
using Properties;
/// <summary>
/// <para>A class which encapsulates the functionality of the QR decomposition Modified Gram-Schmidt Orthogonalization.</para>
/// <para>Any complex square matrix A may be decomposed as A = QR where Q is an unitary mxn matrix and R is an nxn upper triangular matrix.</para>
/// </summary>
/// <remarks>
/// The computation of the QR decomposition is done at construction time by modified Gram-Schmidt Orthogonalization.
/// </remarks>
public class GramSchmidt : QR<Complex>
{
/// <summary>
/// Initializes a new instance of the <see cref="GramSchmidt"/> class. This object creates an unitary matrix
/// using the modified Gram-Schmidt method.
/// </summary>
/// <param name="matrix">The matrix to factor.</param>
/// <exception cref="ArgumentNullException">If <paramref name="matrix"/> is <c>null</c>.</exception>
/// <exception cref="ArgumentException">If <paramref name="matrix"/> row count is less then column count</exception>
/// <exception cref="ArgumentException">If <paramref name="matrix"/> is rank deficient</exception>
public GramSchmidt(Matrix<Complex> matrix)
{
if (matrix == null)
{
throw new ArgumentNullException("matrix");
}
if (matrix.RowCount < matrix.ColumnCount)
{
throw new ArgumentException(Resources.ArgumentMatrixDimensions);
}
MatrixQ = matrix.Clone();
MatrixR = matrix.CreateMatrix(matrix.ColumnCount, matrix.ColumnCount);
for (var k = 0; k < MatrixQ.ColumnCount; k++)
{
var norm = MatrixQ.Column(k).Norm(2);
if (norm == 0.0)
{
throw new ArgumentException(Resources.ArgumentMatrixNotRankDeficient);
}
MatrixR.At(k, k, norm);
for (var i = 0; i < MatrixQ.RowCount; i++)
{
MatrixQ.At(i, k, MatrixQ.At(i, k) / norm);
}
for (var j = k + 1; j < MatrixQ.ColumnCount; j++)
{
var dot = Complex.Zero;
for (int i = 0; i < MatrixQ.RowCount; i++)
{
dot += MatrixQ.Column(k)[i].Conjugate() * MatrixQ.Column(j)[i];
}
MatrixR.At(k, j, dot);
for (var i = 0; i < MatrixQ.RowCount; i++)
{
var value = MatrixQ.At(i, j) - (MatrixQ.At(i, k) * dot);
MatrixQ.At(i, j, value);
}
}
}
}
/// <summary>
/// Gets a value indicating whether the matrix is full rank or not.
/// </summary>
/// <value><c>true</c> if the matrix is full rank; otherwise <c>false</c>.</value>
public override bool IsFullRank
{
get
{
return true;
}
}
/// <summary>
/// Gets the determinant of the matrix for which the QR matrix was computed.
/// </summary>
public override double Determinant
{
get
{
if (MatrixQ.RowCount != MatrixQ.ColumnCount)
{
throw new ArgumentException(Resources.ArgumentMatrixSquare);
}
var det = Complex.One;
for (var i = 0; i < MatrixR.ColumnCount; i++)
{
det *= MatrixR.At(i, i);
if (MatrixR.At(i, i).Magnitude.AlmostEqualInDecimalPlaces(0.0, 15))
{
return 0;
}
}
return det.Magnitude;
}
}
/// <summary>
/// Solves a system of linear equations, <b>AX = B</b>, with A QR factorized.
/// </summary>
/// <param name="input">The right hand side <see cref="Matrix{T}"/>, <b>B</b>.</param>
/// <param name="result">The left hand side <see cref="Matrix{T}"/>, <b>X</b>.</param>
public override void Solve(Matrix<Complex> input, Matrix<Complex> result)
{
// Check for proper arguments.
if (input == null)
{
throw new ArgumentNullException("input");
}
if (result == null)
{
throw new ArgumentNullException("result");
}
// The solution X should have the same number of columns as B
if (input.ColumnCount != result.ColumnCount)
{
throw new ArgumentException(Resources.ArgumentMatrixSameColumnDimension);
}
// The dimension compatibility conditions for X = A\B require the two matrices A and B to have the same number of rows
if (MatrixQ.RowCount != input.RowCount)
{
throw new ArgumentException(Resources.ArgumentMatrixSameRowDimension);
}
// The solution X row dimension is equal to the column dimension of A
if (MatrixQ.ColumnCount != result.RowCount)
{
throw new ArgumentException(Resources.ArgumentMatrixSameColumnDimension);
}
var inputCopy = input.Clone();
// Compute Y = transpose(Q)*B
var column = new Complex[MatrixQ.RowCount];
for (var j = 0; j < input.ColumnCount; j++)
{
for (var k = 0; k < MatrixQ.RowCount; k++)
{
column[k] = inputCopy.At(k, j);
}
for (var i = 0; i < MatrixQ.ColumnCount; i++)
{
var s = Complex.Zero;
for (var k = 0; k < MatrixQ.RowCount; k++)
{
s += MatrixQ.At(k, i).Conjugate() * column[k];
}
inputCopy.At(i, j, s);
}
}
// Solve R*X = Y;
for (var k = MatrixQ.ColumnCount - 1; k >= 0; k--)
{
for (var j = 0; j < input.ColumnCount; j++)
{
inputCopy.At(k, j, inputCopy.At(k, j) / MatrixR.At(k, k));
}
for (var i = 0; i < k; i++)
{
for (var j = 0; j < input.ColumnCount; j++)
{
inputCopy.At(i, j, inputCopy.At(i, j) - (inputCopy.At(k, j) * MatrixR.At(i, k)));
}
}
}
for (var i = 0; i < MatrixR.ColumnCount; i++)
{
for (var j = 0; j < input.ColumnCount; j++)
{
result.At(i, j, inputCopy.At(i, j));
}
}
}
/// <summary>
/// Solves a system of linear equations, <b>Ax = b</b>, with A QR factorized.
/// </summary>
/// <param name="input">The right hand side vector, <b>b</b>.</param>
/// <param name="result">The left hand side <see cref="Matrix{T}"/>, <b>x</b>.</param>
public override void Solve(Vector<Complex> input, Vector<Complex> result)
{
if (input == null)
{
throw new ArgumentNullException("input");
}
if (result == null)
{
throw new ArgumentNullException("result");
}
// Ax=b where A is an m x n matrix
// Check that b is a column vector with m entries
if (MatrixQ.RowCount != input.Count)
{
throw new ArgumentException(Resources.ArgumentVectorsSameLength);
}
// Check that x is a column vector with n entries
if (MatrixQ.ColumnCount != result.Count)
{
throw new ArgumentException(Resources.ArgumentMatrixDimensions);
}
var inputCopy = input.Clone();
// Compute Y = transpose(Q)*B
var column = new Complex[MatrixQ.RowCount];
for (var k = 0; k < MatrixQ.RowCount; k++)
{
column[k] = inputCopy[k];
}
for (var i = 0; i < MatrixQ.ColumnCount; i++)
{
var s = Complex.Zero;
for (var k = 0; k < MatrixQ.RowCount; k++)
{
s += MatrixQ.At(k, i).Conjugate() * column[k];
}
inputCopy[i] = s;
}
// Solve R*X = Y;
for (var k = MatrixQ.ColumnCount - 1; k >= 0; k--)
{
inputCopy[k] /= MatrixR.At(k, k);
for (var i = 0; i < k; i++)
{
inputCopy[i] -= inputCopy[k] * MatrixR.At(i, k);
}
}
for (var i = 0; i < MatrixR.ColumnCount; i++)
{
result[i] = inputCopy[i];
}
}
#region Simple arithmetic of type T
/// <summary>
/// Multiply two values T*T
/// </summary>
/// <param name="val1">Left operand value</param>
/// <param name="val2">Right operand value</param>
/// <returns>Result of multiplication</returns>
protected sealed override Complex MultiplyT(Complex val1, Complex val2)
{
return val1 * val2;
}
/// <summary>
/// Returns the absolute value of a specified number.
/// </summary>
/// <param name="val1"> A number whose absolute is to be found</param>
/// <returns>Absolute value </returns>
protected sealed override double AbsoluteT(Complex val1)
{
return val1.Magnitude;
}
/// <summary>
/// Get value of type T equal to one
/// </summary>
/// <returns>One value</returns>
protected sealed override Complex OneValueT
{
get { return Complex.One; }
}
#endregion
}
}

268
src/Numerics/LinearAlgebra/Complex/Factorization/UserCholesky.cs

@ -0,0 +1,268 @@
// <copyright file="UserCholesky.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>
namespace MathNet.Numerics.LinearAlgebra.Complex.Factorization
{
using System;
using System.Numerics;
using Generic;
using Generic.Factorization;
using Properties;
/// <summary>
/// <para>A class which encapsulates the functionality of a Cholesky factorization for user matrices.</para>
/// <para>For a symmetric, positive definite matrix A, the Cholesky factorization
/// is an lower triangular matrix L so that A = L*L'.</para>
/// </summary>
/// <remarks>
/// The computation of the Cholesky factorization is done at construction time. If the matrix is not symmetric
/// or positive definite, the constructor will throw an exception.
/// </remarks>
public class UserCholesky : Cholesky<Complex>
{
/// <summary>
/// Initializes a new instance of the <see cref="UserCholesky"/> class. This object will compute the
/// Cholesky factorization when the constructor is called and cache it's factorization.
/// </summary>
/// <param name="matrix">The matrix to factor.</param>
/// <exception cref="ArgumentNullException">If <paramref name="matrix"/> is <c>null</c>.</exception>
/// <exception cref="ArgumentException">If <paramref name="matrix"/> is not a square matrix.</exception>
/// <exception cref="ArgumentException">If <paramref name="matrix"/> is not positive definite.</exception>
public UserCholesky(Matrix<Complex> matrix)
{
if (matrix == null)
{
throw new ArgumentNullException("matrix");
}
if (matrix.RowCount != matrix.ColumnCount)
{
throw new ArgumentException(Resources.ArgumentMatrixSquare);
}
// Create a new matrix for the Cholesky factor, then perform factorization (while overwriting).
CholeskyFactor = matrix.Clone();
for (var i = 0; i < CholeskyFactor.RowCount; i++)
{
var d = Complex.Zero;
for (var j = 0; j < i; j++)
{
var s = Complex.Zero;
for (var k = 0; k < j; k++)
{
s += CholeskyFactor.At(i, k) * CholeskyFactor.At(j, k).Conjugate();
}
s = (matrix.At(i, j) - s) / CholeskyFactor.At(j, j);
CholeskyFactor.At(i, j, s);
d += s * s.Conjugate();
}
d = matrix.At(i, i) - d;
if (d.Real <= 0)
{
throw new ArgumentException(Resources.ArgumentMatrixPositiveDefinite);
}
CholeskyFactor.At(i, i, d.SquareRoot());
for (var k = i + 1; k < CholeskyFactor.RowCount; k++)
{
CholeskyFactor.At(i, k, 0.0);
}
}
}
/// <summary>
/// Solves a system of linear equations, <b>AX = B</b>, with A Cholesky factorized.
/// </summary>
/// <param name="input">The right hand side <see cref="Matrix{T}"/>, <b>B</b>.</param>
/// <param name="result">The left hand side <see cref="Matrix{T}"/>, <b>X</b>.</param>
public override void Solve(Matrix<Complex> input, Matrix<Complex> result)
{
if (input == null)
{
throw new ArgumentNullException("input");
}
if (result == null)
{
throw new ArgumentNullException("result");
}
// Check for proper dimensions.
if (result.RowCount != input.RowCount)
{
throw new ArgumentException(Resources.ArgumentMatrixSameRowDimension);
}
if (result.ColumnCount != input.ColumnCount)
{
throw new ArgumentException(Resources.ArgumentMatrixSameColumnDimension);
}
if (input.RowCount != CholeskyFactor.RowCount)
{
throw new ArgumentException(Resources.ArgumentMatrixDimensions);
}
input.CopyTo(result);
var order = CholeskyFactor.RowCount;
for (var c = 0; c < result.ColumnCount; c++)
{
// Solve L*Y = B;
Complex sum;
for (var i = 0; i < order; i++)
{
sum = result.At(i, c);
for (var k = i - 1; k >= 0; k--)
{
sum -= CholeskyFactor.At(i, k) * result.At(k, c);
}
result.At(i, c, sum / CholeskyFactor.At(i, i));
}
// Solve L'*X = Y;
for (var i = order - 1; i >= 0; i--)
{
sum = result.At(i, c);
for (var k = i + 1; k < order; k++)
{
sum -= CholeskyFactor.At(k, i).Conjugate() * result.At(k, c);
}
result.At(i, c, sum / CholeskyFactor.At(i, i));
}
}
}
/// <summary>
/// Solves a system of linear equations, <b>Ax = b</b>, with A Cholesky factorized.
/// </summary>
/// <param name="input">The right hand side vector, <b>b</b>.</param>
/// <param name="result">The left hand side <see cref="Matrix{T}"/>, <b>x</b>.</param>
public override void Solve(Vector<Complex> input, Vector<Complex> result)
{
// Check for proper arguments.
if (input == null)
{
throw new ArgumentNullException("input");
}
if (result == null)
{
throw new ArgumentNullException("result");
}
// Check for proper dimensions.
if (input.Count != result.Count)
{
throw new ArgumentException(Resources.ArgumentVectorsSameLength);
}
if (input.Count != CholeskyFactor.RowCount)
{
throw new ArgumentException(Resources.ArgumentMatrixDimensions);
}
input.CopyTo(result);
var order = CholeskyFactor.RowCount;
// Solve L*Y = B;
Complex sum;
for (var i = 0; i < order; i++)
{
sum = result[i];
for (var k = i - 1; k >= 0; k--)
{
sum -= CholeskyFactor.At(i, k) * result[k];
}
result[i] = sum / CholeskyFactor.At(i, i);
}
// Solve L'*X = Y;
for (var i = order - 1; i >= 0; i--)
{
sum = result[i];
for (var k = i + 1; k < order; k++)
{
sum -= CholeskyFactor.At(k, i).Conjugate() * result[k];
}
result[i] = sum / CholeskyFactor.At(i, i);
}
}
#region Simple arithmetic of type T
/// <summary>
/// Add two values T+T
/// </summary>
/// <param name="val1">Left operand value</param>
/// <param name="val2">Right operand value</param>
/// <returns>Result of addition</returns>
protected sealed override Complex AddT(Complex val1, Complex val2)
{
return val1 + val2;
}
/// <summary>
/// Multiply two values T*T
/// </summary>
/// <param name="val1">Left operand value</param>
/// <param name="val2">Right operand value</param>
/// <returns>Result of multiplication</returns>
protected sealed override Complex MultiplyT(Complex val1, Complex val2)
{
return val1 * val2;
}
/// <summary>
/// Returns the natural (base e) logarithm of a specified number.
/// </summary>
/// <param name="val1"> A number whose logarithm is to be found</param>
/// <returns>Natural (base e) logarithm </returns>
protected sealed override Complex LogT(Complex val1)
{
return val1.NaturalLogarithm();
}
/// <summary>
/// Get value of type T equal to one
/// </summary>
/// <returns>One value</returns>
protected sealed override Complex OneValueT
{
get { return Complex.One; }
}
#endregion
}
}

335
src/Numerics/LinearAlgebra/Complex/Factorization/UserLU.cs

@ -0,0 +1,335 @@
// <copyright file="UserLU.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>
namespace MathNet.Numerics.LinearAlgebra.Complex.Factorization
{
using System;
using System.Numerics;
using Generic;
using Generic.Factorization;
using Properties;
/// <summary>
/// <para>A class which encapsulates the functionality of an LU factorization.</para>
/// <para>For a matrix A, the LU factorization is a pair of lower triangular matrix L and
/// upper triangular matrix U so that A = L*U.</para>
/// </summary>
/// <remarks>
/// The computation of the LU factorization is done at construction time.
/// </remarks>
public class UserLU : LU<Complex>
{
/// <summary>
/// Initializes a new instance of the <see cref="UserLU"/> class. This object will compute the
/// LU factorization when the constructor is called and cache it's factorization.
/// </summary>
/// <param name="matrix">The matrix to factor.</param>
/// <exception cref="ArgumentNullException">If <paramref name="matrix"/> is <c>null</c>.</exception>
/// <exception cref="ArgumentException">If <paramref name="matrix"/> is not a square matrix.</exception>
public UserLU(Matrix<Complex> matrix)
{
if (matrix == null)
{
throw new ArgumentNullException("matrix");
}
if (matrix.RowCount != matrix.ColumnCount)
{
throw new ArgumentException(Resources.ArgumentMatrixSquare);
}
// Create an array for the pivot indices.
var order = matrix.RowCount;
Factors = matrix.Clone();
Pivots = new int[order];
// Initialize the pivot matrix to the identity permutation.
for (var i = 0; i < order; i++)
{
Pivots[i] = i;
}
var vectorLUcolj = new Complex[order];
for (var j = 0; j < order; j++)
{
// Make a copy of the j-th column to localize references.
for (var i = 0; i < order; i++)
{
vectorLUcolj[i] = Factors.At(i, j);
}
// Apply previous transformations.
for (var i = 0; i < order; i++)
{
var kmax = Math.Min(i, j);
var s = Complex.Zero;
for (var k = 0; k < kmax; k++)
{
s += Factors.At(i, k) * vectorLUcolj[k];
}
vectorLUcolj[i] -= s;
Factors.At(i, j, vectorLUcolj[i]);
}
// Find pivot and exchange if necessary.
var p = j;
for (var i = j + 1; i < order; i++)
{
if (vectorLUcolj[i].Magnitude > vectorLUcolj[p].Magnitude)
{
p = i;
}
}
if (p != j)
{
for (var k = 0; k < order; k++)
{
var temp = Factors.At(p, k);
Factors.At(p, k, Factors.At(j, k));
Factors.At(j, k, temp);
}
Pivots[j] = p;
}
// Compute multipliers.
if (j < order & Factors.At(j, j) != 0.0)
{
for (var i = j + 1; i < order; i++)
{
Factors.At(i, j, (Factors.At(i, j) / Factors.At(j, j)));
}
}
}
}
/// <summary>
/// Solves a system of linear equations, <c>AX = B</c>, with A LU factorized.
/// </summary>
/// <param name="input">The right hand side <see cref="Matrix{T}"/>, <c>B</c>.</param>
/// <param name="result">The left hand side <see cref="Matrix{T}"/>, <c>X</c>.</param>
public override void Solve(Matrix<Complex> input, Matrix<Complex> result)
{
// Check for proper arguments.
if (input == null)
{
throw new ArgumentNullException("input");
}
if (result == null)
{
throw new ArgumentNullException("result");
}
// Check for proper dimensions.
if (result.RowCount != input.RowCount)
{
throw new ArgumentException(Resources.ArgumentMatrixSameRowDimension);
}
if (result.ColumnCount != input.ColumnCount)
{
throw new ArgumentException(Resources.ArgumentMatrixSameColumnDimension);
}
if (input.RowCount != Factors.RowCount)
{
throw new ArgumentException(Resources.ArgumentMatrixDimensions);
}
// Copy the contents of input to result.
input.CopyTo(result);
for (var i = 0; i < Pivots.Length; i++)
{
if (Pivots[i] == i)
{
continue;
}
var p = Pivots[i];
for (var j = 0; j < result.ColumnCount; j++)
{
var temp = result.At(p, j);
result.At(p, j, result.At(i, j));
result.At(i, j, temp);
}
}
var order = Factors.RowCount;
// Solve L*Y = P*B
for (var k = 0; k < order; k++)
{
for (var i = k + 1; i < order; i++)
{
for (var j = 0; j < result.ColumnCount; j++)
{
var temp = result.At(k, j) * Factors.At(i, k);
result.At(i, j, result.At(i, j) - temp);
}
}
}
// Solve U*X = Y;
for (var k = order - 1; k >= 0; k--)
{
for (var j = 0; j < result.ColumnCount; j++)
{
result.At(k, j, (result.At(k, j) / Factors.At(k, k)));
}
for (var i = 0; i < k; i++)
{
for (var j = 0; j < result.ColumnCount; j++)
{
var temp = result.At(k, j) * Factors.At(i, k);
result.At(i, j, result.At(i, j) - temp);
}
}
}
}
/// <summary>
/// Solves a system of linear equations, <c>Ax = b</c>, with A LU factorized.
/// </summary>
/// <param name="input">The right hand side vector, <c>b</c>.</param>
/// <param name="result">The left hand side <see cref="Matrix{T}"/>, <c>x</c>.</param>
public override void Solve(Vector<Complex> input, Vector<Complex> result)
{
// Check for proper arguments.
if (input == null)
{
throw new ArgumentNullException("input");
}
if (result == null)
{
throw new ArgumentNullException("result");
}
// Check for proper dimensions.
if (input.Count != result.Count)
{
throw new ArgumentException(Resources.ArgumentVectorsSameLength);
}
if (input.Count != Factors.RowCount)
{
throw new ArgumentException(Resources.ArgumentMatrixDimensions);
}
// Copy the contents of input to result.
input.CopyTo(result);
for (var i = 0; i < Pivots.Length; i++)
{
if (Pivots[i] == i)
{
continue;
}
var p = Pivots[i];
var temp = result[p];
result[p] = result[i];
result[i] = temp;
}
var order = Factors.RowCount;
// Solve L*Y = P*B
for (var k = 0; k < order; k++)
{
for (var i = k + 1; i < order; i++)
{
result[i] -= result[k] * Factors.At(i, k);
}
}
// Solve U*X = Y;
for (var k = order - 1; k >= 0; k--)
{
result[k] /= Factors.At(k, k);
for (var i = 0; i < k; i++)
{
result[i] -= result[k] * Factors.At(i, k);
}
}
}
/// <summary>
/// Returns the inverse of this matrix. The inverse is calculated using LU decomposition.
/// </summary>
/// <returns>The inverse of this matrix.</returns>
public override Matrix<Complex> Inverse()
{
var order = Factors.RowCount;
var inverse = Factors.CreateMatrix(order, order);
for (var i = 0; i < order; i++)
{
inverse.At(i, i, 1.0);
}
return Solve(inverse);
}
#region Simple arithmetic of type T
/// <summary>
/// Multiply two values T*T
/// </summary>
/// <param name="val1">Left operand value</param>
/// <param name="val2">Right operand value</param>
/// <returns>Result of multiplication</returns>
protected sealed override Complex MultiplyT(Complex val1, Complex val2)
{
return val1 * val2;
}
/// <summary>
/// Get value of type T equal to one
/// </summary>
/// <returns>One value</returns>
protected sealed override Complex OneValueT
{
get { return Complex.One; }
}
/// <summary>
/// Get value of type T equal to minus one
/// </summary>
/// <returns>One value</returns>
protected sealed override Complex MinusOneValueT
{
get { return -Complex.One; }
}
#endregion
}
}

366
src/Numerics/LinearAlgebra/Complex/Factorization/UserQR.cs

@ -0,0 +1,366 @@
// <copyright file="UserQR.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>
namespace MathNet.Numerics.LinearAlgebra.Complex.Factorization
{
using System;
using System.Linq;
using System.Numerics;
using Generic;
using Generic.Factorization;
using Properties;
/// <summary>
/// <para>A class which encapsulates the functionality of the QR decomposition.</para>
/// <para>Any real square matrix A may be decomposed as A = QR where Q is an orthogonal matrix
/// (its columns are orthogonal unit vectors meaning QTQ = I) and R is an upper triangular matrix
/// (also called right triangular matrix).</para>
/// </summary>
/// <remarks>
/// The computation of the QR decomposition is done at construction time by Householder transformation.
/// </remarks>
public class UserQR : QR<Complex>
{
/// <summary>
/// Initializes a new instance of the <see cref="UserQR"/> class. This object will compute the
/// QR factorization when the constructor is called and cache it's factorization.
/// </summary>
/// <param name="matrix">The matrix to factor.</param>
/// <exception cref="ArgumentNullException">If <paramref name="matrix"/> is <c>null</c>.</exception>
public UserQR(Matrix<Complex> matrix)
{
if (matrix == null)
{
throw new ArgumentNullException("matrix");
}
if (matrix.RowCount < matrix.ColumnCount)
{
throw new ArgumentException(Resources.ArgumentMatrixDimensions);
}
MatrixR = matrix.Clone();
MatrixQ = matrix.CreateMatrix(matrix.RowCount, matrix.RowCount);
for (var i = 0; i < matrix.RowCount; i++)
{
MatrixQ.At(i, i, 1.0);
}
var minmn = Math.Min(matrix.RowCount, matrix.ColumnCount);
var u = new Complex[minmn][];
for (var i = 0; i < minmn; i++)
{
u[i] = GenerateColumn(MatrixR, i, matrix.RowCount - 1, i);
ComputeQR(u[i], MatrixR, i, matrix.RowCount - 1, i + 1, matrix.ColumnCount - 1);
}
for (var i = minmn - 1; i >= 0; i--)
{
ComputeQR(u[i], MatrixQ, i, matrix.RowCount - 1, i, matrix.RowCount - 1);
}
}
/// <summary>
/// Generate column from initial matrix to work array
/// </summary>
/// <param name="a">Initial matrix</param>
/// <param name="rowStart">The firts row</param>
/// <param name="rowEnd">The last row</param>
/// <param name="column">Column index</param>
/// <returns>Generated vector</returns>
private static Complex[] GenerateColumn(Matrix<Complex> a, int rowStart, int rowEnd, int column)
{
var ru = rowEnd - rowStart + 1;
var u = new Complex[ru];
for (var i = rowStart; i <= rowEnd; i++)
{
u[i - rowStart] = a.At(i, column);
a.At(i, column, 0.0);
}
var norm = u.Aggregate(Complex.Zero, (current, t) => current + (t.Magnitude * t.Magnitude));
norm = norm.SquareRoot();
if (rowStart == rowEnd || norm.Magnitude == 0)
{
a.At(rowStart, column, -u[0]);
u[0] = Math.Sqrt(2.0);
return u;
}
if (u[0].Magnitude != 0.0)
{
norm = norm.Magnitude * (u[0] / u[0].Magnitude);
}
a.At(rowStart, column, -norm);
for (var i = 0; i < ru; i++)
{
u[i] /= norm;
}
u[0] += 1.0;
var s = (1.0 / u[0]).SquareRoot();
for (var i = 0; i < ru; i++)
{
u[i] = u[i].Conjugate() * s;
}
return u;
}
/// <summary>
/// Compute matrix Q or R
/// </summary>
/// <param name="u">H vectors values</param>
/// <param name="a">Matrix to calculate</param>
/// <param name="rowStart">Starting row index</param>
/// <param name="rowEnd">Ending row index</param>
/// <param name="columnStart">Starting column index</param>
/// <param name="columnEnd">Ending column index</param>
private static void ComputeQR(Complex[] u, Matrix<Complex> a, int rowStart, int rowEnd, int columnStart, int columnEnd)
{
if (rowEnd < rowStart || columnEnd < columnStart)
{
return;
}
var v = new Complex[columnEnd - columnStart + 1];
for (var j = columnStart; j <= columnEnd; j++)
{
v[j - columnStart] = 0.0;
}
for (var i = rowStart; i <= rowEnd; i++)
{
for (var j = columnStart; j <= columnEnd; j++)
{
v[j - columnStart] = v[j - columnStart] + (u[i - rowStart] * a.At(i, j));
}
}
for (var i = rowStart; i <= rowEnd; i++)
{
for (var j = columnStart; j <= columnEnd; j++)
{
a.At(i, j, a.At(i, j) - (u[i - rowStart].Conjugate() * v[j - columnStart]));
}
}
}
/// <summary>
/// Solves a system of linear equations, <b>AX = B</b>, with A QR factorized.
/// </summary>
/// <param name="input">The right hand side <see cref="Matrix{T}"/>, <b>B</b>.</param>
/// <param name="result">The left hand side <see cref="Matrix{T}"/>, <b>X</b>.</param>
public override void Solve(Matrix<Complex> input, Matrix<Complex> result)
{
// Check for proper arguments.
if (input == null)
{
throw new ArgumentNullException("input");
}
if (result == null)
{
throw new ArgumentNullException("result");
}
// The solution X should have the same number of columns as B
if (input.ColumnCount != result.ColumnCount)
{
throw new ArgumentException(Resources.ArgumentMatrixSameColumnDimension);
}
// The dimension compatibility conditions for X = A\B require the two matrices A and B to have the same number of rows
if (MatrixR.RowCount != input.RowCount)
{
throw new ArgumentException(Resources.ArgumentMatrixSameRowDimension);
}
// The solution X row dimension is equal to the column dimension of A
if (MatrixR.ColumnCount != result.RowCount)
{
throw new ArgumentException(Resources.ArgumentMatrixSameColumnDimension);
}
var inputCopy = input.Clone();
// Compute Y = transpose(Q)*B
var column = new Complex[MatrixR.RowCount];
for (var j = 0; j < input.ColumnCount; j++)
{
for (var k = 0; k < MatrixR.RowCount; k++)
{
column[k] = inputCopy.At(k, j);
}
for (var i = 0; i < MatrixR.RowCount; i++)
{
var s = Complex.Zero;
for (var k = 0; k < MatrixR.RowCount; k++)
{
s += MatrixQ.At(k, i).Conjugate() * column[k];
}
inputCopy.At(i, j, s);
}
}
// Solve R*X = Y;
for (var k = MatrixR.ColumnCount - 1; k >= 0; k--)
{
for (var j = 0; j < input.ColumnCount; j++)
{
inputCopy.At(k, j, inputCopy.At(k, j) / MatrixR.At(k, k));
}
for (var i = 0; i < k; i++)
{
for (var j = 0; j < input.ColumnCount; j++)
{
inputCopy.At(i, j, inputCopy.At(i, j) - (inputCopy.At(k, j) * MatrixR.At(i, k)));
}
}
}
for (var i = 0; i < MatrixR.ColumnCount; i++)
{
for (var j = 0; j < inputCopy.ColumnCount; j++)
{
result.At(i, j, inputCopy.At(i, j));
}
}
}
/// <summary>
/// Solves a system of linear equations, <b>Ax = b</b>, with A QR factorized.
/// </summary>
/// <param name="input">The right hand side vector, <b>b</b>.</param>
/// <param name="result">The left hand side <see cref="Matrix{T}"/>, <b>x</b>.</param>
public override void Solve(Vector<Complex> input, Vector<Complex> result)
{
if (input == null)
{
throw new ArgumentNullException("input");
}
if (result == null)
{
throw new ArgumentNullException("result");
}
// Ax=b where A is an m x n matrix
// Check that b is a column vector with m entries
if (MatrixR.RowCount != input.Count)
{
throw new ArgumentException(Resources.ArgumentVectorsSameLength);
}
// Check that x is a column vector with n entries
if (MatrixR.ColumnCount != result.Count)
{
throw new ArgumentException(Resources.ArgumentMatrixDimensions);
}
var inputCopy = input.Clone();
// Compute Y = transpose(Q)*B
var column = new Complex[MatrixR.RowCount];
for (var k = 0; k < MatrixR.RowCount; k++)
{
column[k] = inputCopy[k];
}
for (var i = 0; i < MatrixR.RowCount; i++)
{
var s = Complex.Zero;
for (var k = 0; k < MatrixR.RowCount; k++)
{
s += MatrixQ.At(k, i).Conjugate() * column[k];
}
inputCopy[i] = s;
}
// Solve R*X = Y;
for (var k = MatrixR.ColumnCount - 1; k >= 0; k--)
{
inputCopy[k] /= MatrixR.At(k, k);
for (var i = 0; i < k; i++)
{
inputCopy[i] -= inputCopy[k] * MatrixR.At(i, k);
}
}
for (var i = 0; i < MatrixR.ColumnCount; i++)
{
result[i] = inputCopy[i];
}
}
#region Simple arithmetic of type T
/// <summary>
/// Multiply two values T*T
/// </summary>
/// <param name="val1">Left operand value</param>
/// <param name="val2">Right operand value</param>
/// <returns>Result of multiplication</returns>
protected sealed override Complex MultiplyT(Complex val1, Complex val2)
{
return val1 * val2;
}
/// <summary>
/// Returns the absolute value of a specified number.
/// </summary>
/// <param name="val1"> A number whose absolute is to be found</param>
/// <returns>Absolute value </returns>
protected sealed override double AbsoluteT(Complex val1)
{
return val1.Magnitude;
}
/// <summary>
/// Get value of type T equal to one
/// </summary>
/// <returns>One value</returns>
protected sealed override Complex OneValueT
{
get { return Complex.One; }
}
#endregion
}
}

974
src/Numerics/LinearAlgebra/Complex/Factorization/UserSvd.cs

@ -0,0 +1,974 @@
// <copyright file="UserSvd.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>
namespace MathNet.Numerics.LinearAlgebra.Complex.Factorization
{
using System;
using System.Numerics;
using Generic;
using Generic.Factorization;
using Properties;
/// <summary>
/// <para>A class which encapsulates the functionality of the singular value decomposition (SVD) for <see cref="Matrix{T}"/>.</para>
/// <para>Suppose M is an m-by-n matrix whose entries are real numbers.
/// Then there exists a factorization of the form M = UΣVT where:
/// - U is an m-by-m unitary matrix;
/// - Σ is m-by-n diagonal matrix with nonnegative real numbers on the diagonal;
/// - VT denotes transpose of V, an n-by-n unitary matrix;
/// Such a factorization is called a singular-value decomposition of M. A common convention is to order the diagonal
/// entries Σ(i,i) in descending order. In this case, the diagonal matrix Σ is uniquely determined
/// by M (though the matrices U and V are not). The diagonal entries of Σ are known as the singular values of M.</para>
/// </summary>
/// <remarks>
/// The computation of the singular value decomposition is done at construction time.
/// </remarks>
public class UserSvd : Svd<Complex>
{
/// <summary>
/// Initializes a new instance of the <see cref="UserSvd"/> class. This object will compute the
/// the singular value decomposition when the constructor is called and cache it's decomposition.
/// </summary>
/// <param name="matrix">The matrix to factor.</param>
/// <param name="computeVectors">Compute the singular U and VT vectors or not.</param>
/// <exception cref="ArgumentNullException">If <paramref name="matrix"/> is <b>null</b>.</exception>
/// <exception cref="ArgumentException">If SVD algorithm failed to converge with matrix <paramref name="matrix"/>.</exception>
public UserSvd(Matrix<Complex> matrix, bool computeVectors)
{
if (matrix == null)
{
throw new ArgumentNullException("matrix");
}
ComputeVectors = computeVectors;
var nm = Math.Min(matrix.RowCount + 1, matrix.ColumnCount);
var matrixCopy = matrix.Clone();
VectorS = matrixCopy.CreateVector(nm);
MatrixU = matrixCopy.CreateMatrix(matrixCopy.RowCount, matrixCopy.RowCount);
MatrixVT = matrixCopy.CreateMatrix(matrixCopy.ColumnCount, matrixCopy.ColumnCount);
const int Maxiter = 1000;
var e = new Complex[matrixCopy.ColumnCount];
var work = new Complex[matrixCopy.RowCount];
int i, j;
int l, lp1;
var cs = 0.0;
var sn = 0.0;
Complex t;
var ncu = matrixCopy.RowCount;
// Reduce matrixCopy to bidiagonal form, storing the diagonal elements
// In s and the super-diagonal elements in e.
var nct = Math.Min(matrixCopy.RowCount - 1, matrixCopy.ColumnCount);
var nrt = Math.Max(0, Math.Min(matrixCopy.ColumnCount - 2, matrixCopy.RowCount));
var lu = Math.Max(nct, nrt);
for (l = 0; l < lu; l++)
{
lp1 = l + 1;
if (l < nct)
{
// Compute the transformation for the l-th column and place the l-th diagonal in VectorS[l].
VectorS[l] = Cnrm2Column(matrixCopy, matrixCopy.RowCount, l, l);
if (VectorS[l].Magnitude != 0.0)
{
if (matrixCopy.At(l, l).Magnitude != 0.0)
{
VectorS[l] = Csign(VectorS[l], matrixCopy.At(l, l));
}
CscalColumn(matrixCopy, matrixCopy.RowCount, l, l, 1.0 / VectorS[l]);
matrixCopy.At(l, l, (Complex.One + matrixCopy.At(l, l)));
}
VectorS[l] = -VectorS[l];
}
for (j = lp1; j < matrixCopy.ColumnCount; j++)
{
if (l < nct)
{
if (VectorS[l].Magnitude != 0.0)
{
// Apply the transformation.
t = -Cdotc(matrixCopy, matrixCopy.RowCount, l, j, l) / matrixCopy.At(l, l);
if (t != Complex.Zero)
{
for (var ii = l; ii < matrixCopy.RowCount; ii++)
{
matrixCopy.At(ii, j, matrixCopy.At(ii, j) + (t * matrixCopy.At(ii, l)));
}
}
}
}
// Place the l-th row of matrixCopy into e for the
// Subsequent calculation of the row transformation.
e[j] = matrixCopy.At(l, j).Conjugate();
}
if (ComputeVectors && l < nct)
{
// Place the transformation in u for subsequent back multiplication.
for (i = l; i < matrixCopy.RowCount; i++)
{
MatrixU.At(i, l, matrixCopy.At(i, l));
}
}
if (l >= nrt)
{
continue;
}
// Compute the l-th row transformation and place the l-th super-diagonal in e(l).
var enorm = Cnrm2Vector(e, lp1);
e[l] = enorm;
if (e[l].Magnitude != 0.0)
{
if (e[lp1].Magnitude != 0.0)
{
e[l] = Csign(e[l], e[lp1]);
}
CscalVector(e, lp1, 1.0 / e[l]);
e[lp1] = Complex.One + e[lp1];
}
e[l] = -e[l].Conjugate();
if (lp1 < matrixCopy.RowCount && e[l].Magnitude != 0.0)
{
// Apply the transformation.
for (i = lp1; i < matrixCopy.RowCount; i++)
{
work[i] = Complex.Zero;
}
for (j = lp1; j < matrixCopy.ColumnCount; j++)
{
if (e[j] != Complex.Zero)
{
for (var ii = lp1; ii < matrixCopy.RowCount; ii++)
{
work[ii] += e[j] * matrixCopy.At(ii, j);
}
}
}
for (j = lp1; j < matrixCopy.ColumnCount; j++)
{
var ww = (-e[j] / e[lp1]).Conjugate();
if (ww != Complex.Zero)
{
for (var ii = lp1; ii < matrixCopy.RowCount; ii++)
{
matrixCopy.At(ii, j, matrixCopy.At(ii, j) + (ww * work[ii]));
}
}
}
}
if (ComputeVectors)
{
// Place the transformation in v for subsequent back multiplication.
for (i = lp1; i < matrixCopy.ColumnCount; i++)
{
MatrixVT.At(i, l, e[i]);
}
}
}
// Set up the final bidiagonal matrixCopy or order m.
var m = Math.Min(matrixCopy.ColumnCount, matrixCopy.RowCount + 1);
var nctp1 = nct + 1;
var nrtp1 = nrt + 1;
if (nct < matrixCopy.ColumnCount)
{
VectorS[nctp1 - 1] = matrixCopy.At((nctp1 - 1), (nctp1 - 1));
}
if (matrixCopy.RowCount < m)
{
VectorS[m - 1] = Complex.Zero;
}
if (nrtp1 < m)
{
e[nrtp1 - 1] = matrixCopy.At((nrtp1 - 1), (m - 1));
}
e[m - 1] = Complex.Zero;
// If required, generate u.
if (ComputeVectors)
{
for (j = nctp1 - 1; j < ncu; j++)
{
for (i = 0; i < matrixCopy.RowCount; i++)
{
MatrixU.At(i, j, Complex.Zero);
}
MatrixU.At(j, j, Complex.One);
}
for (l = nct - 1; l >= 0; l--)
{
if (VectorS[l].Magnitude != 0.0)
{
for (j = l + 1; j < ncu; j++)
{
t = -Cdotc(MatrixU, matrixCopy.RowCount, l, j, l) / MatrixU.At(l, l);
if (t != Complex.Zero)
{
for (var ii = l; ii < matrixCopy.RowCount; ii++)
{
MatrixU.At(ii, j, MatrixU.At(ii, j) + (t * MatrixU.At(ii, l)));
}
}
}
CscalColumn(MatrixU, matrixCopy.RowCount, l, l, -1.0);
MatrixU.At(l, l, Complex.One + MatrixU.At(l, l));
for (i = 0; i < l; i++)
{
MatrixU.At(i, l, Complex.Zero);
}
}
else
{
for (i = 0; i < matrixCopy.RowCount; i++)
{
MatrixU.At(i, l, Complex.Zero);
}
MatrixU.At(l, l, Complex.One);
}
}
}
// If it is required, generate v.
if (ComputeVectors)
{
for (l = matrixCopy.ColumnCount - 1; l >= 0; l--)
{
lp1 = l + 1;
if (l < nrt)
{
if (e[l].Magnitude != 0.0)
{
for (j = lp1; j < matrixCopy.ColumnCount; j++)
{
t = -Cdotc(MatrixVT, matrixCopy.ColumnCount, l, j, lp1) / MatrixVT.At(lp1, l);
if (t != Complex.Zero)
{
for (var ii = l; ii < matrixCopy.ColumnCount; ii++)
{
MatrixVT.At(ii, j, MatrixVT.At(ii, j) + (t * MatrixVT.At(ii, l)));
}
}
}
}
}
for (i = 0; i < matrixCopy.ColumnCount; i++)
{
MatrixVT.At(i, l, Complex.Zero);
}
MatrixVT.At(l, l, Complex.One);
}
}
// Transform s and e so that they are real .
for (i = 0; i < m; i++)
{
Complex r;
if (VectorS[i].Magnitude != 0.0)
{
t = VectorS[i].Magnitude;
r = VectorS[i] / t;
VectorS[i] = t;
if (i < m - 1)
{
e[i] = e[i] / r;
}
if (ComputeVectors)
{
CscalColumn(MatrixU, matrixCopy.RowCount, i, 0, r);
}
}
// Exit
if (i == m - 1)
{
break;
}
if (e[i].Magnitude != 0.0)
{
t = e[i].Magnitude;
r = t / e[i];
e[i] = t;
VectorS[i + 1] = VectorS[i + 1] * r;
if (ComputeVectors)
{
CscalColumn(MatrixVT, matrixCopy.ColumnCount, i + 1, 0, r);
}
}
}
// Main iteration loop for the singular values.
var mn = m;
var iter = 0;
while (m > 0)
{
// Quit if all the singular values have been found. If too many iterations have been performed,
// throw exception that Convergence Failed
if (iter >= Maxiter)
{
throw new ArgumentException(Resources.ConvergenceFailed);
}
// This section of the program inspects for negligible elements in the s and e arrays. On
// completion the variables kase and l are set as follows.
// Kase = 1 if VectorS[m] and e[l-1] are negligible and l < m
// Kase = 2 if VectorS[l] is negligible and l < m
// Kase = 3 if e[l-1] is negligible, l < m, and VectorS[l, ..., VectorS[m] are not negligible (qr step).
// Лase = 4 if e[m-1] is negligible (convergence).
double ztest;
double test;
for (l = m - 2; l >= 0; l--)
{
test = VectorS[l].Magnitude + VectorS[l + 1].Magnitude;
ztest = test + e[l].Magnitude;
if (ztest.AlmostEqualInDecimalPlaces(test, 15))
{
e[l] = Complex.Zero;
break;
}
}
int kase;
if (l == m - 2)
{
kase = 4;
}
else
{
int ls;
for (ls = m - 1; ls > l; ls--)
{
test = 0.0;
if (ls != m - 1)
{
test = test + e[ls].Magnitude;
}
if (ls != l + 1)
{
test = test + e[ls - 1].Magnitude;
}
ztest = test + VectorS[ls].Magnitude;
if (ztest.AlmostEqualInDecimalPlaces(test, 15))
{
VectorS[ls] = Complex.Zero;
break;
}
}
if (ls == l)
{
kase = 3;
}
else if (ls == m - 1)
{
kase = 1;
}
else
{
kase = 2;
l = ls;
}
}
l = l + 1;
// Perform the task indicated by kase.
int k;
double f;
switch (kase)
{
// Deflate negligible VectorS[m].
case 1:
f = e[m - 2].Real;
e[m - 2] = Complex.Zero;
double t1;
for (var kk = l; kk < m - 1; kk++)
{
k = m - 2 - kk + l;
t1 = VectorS[k].Real;
Srotg(ref t1, ref f, ref cs, ref sn);
VectorS[k] = t1;
if (k != l)
{
f = -sn * e[k - 1].Real;
e[k - 1] = cs * e[k - 1];
}
if (ComputeVectors)
{
Csrot(MatrixVT, matrixCopy.ColumnCount, k, m - 1, cs, sn);
}
}
break;
// Split at negligible VectorS[l].
case 2:
f = e[l - 1].Real;
e[l - 1] = Complex.Zero;
for (k = l; k < m; k++)
{
t1 = VectorS[k].Real;
Srotg(ref t1, ref f, ref cs, ref sn);
VectorS[k] = t1;
f = -sn * e[k].Real;
e[k] = cs * e[k];
if (ComputeVectors)
{
Csrot(MatrixU, matrixCopy.RowCount, k, l - 1, cs, sn);
}
}
break;
// Perform one qr step.
case 3:
// Calculate the shift.
var scale = 0.0;
scale = Math.Max(scale, VectorS[m - 1].Magnitude);
scale = Math.Max(scale, VectorS[m - 2].Magnitude);
scale = Math.Max(scale, e[m - 2].Magnitude);
scale = Math.Max(scale, VectorS[l].Magnitude);
scale = Math.Max(scale, e[l].Magnitude);
var sm = VectorS[m - 1].Real / scale;
var smm1 = VectorS[m - 2].Real / scale;
var emm1 = e[m - 2].Real / scale;
var sl = VectorS[l].Real / scale;
var el = e[l].Real / scale;
var b = (((smm1 + sm) * (smm1 - sm)) + (emm1 * emm1)) / 2.0;
var c = (sm * emm1) * (sm * emm1);
var shift = 0.0;
if (b != 0.0 || c != 0.0)
{
shift = Math.Sqrt((b * b) + c);
if (b < 0.0)
{
shift = -shift;
}
shift = c / (b + shift);
}
f = ((sl + sm) * (sl - sm)) + shift;
var g = sl * el;
// Chase zeros.
for (k = l; k < m - 1; k++)
{
Srotg(ref f, ref g, ref cs, ref sn);
if (k != l)
{
e[k - 1] = f;
}
f = (cs * VectorS[k].Real) + (sn * e[k].Real);
e[k] = (cs * e[k]) - (sn * VectorS[k]);
g = sn * VectorS[k + 1].Real;
VectorS[k + 1] = cs * VectorS[k + 1];
if (ComputeVectors)
{
Csrot(MatrixVT, matrixCopy.ColumnCount, k, k + 1, cs, sn);
}
Srotg(ref f, ref g, ref cs, ref sn);
VectorS[k] = f;
f = (cs * e[k].Real) + (sn * VectorS[k + 1].Real);
VectorS[k + 1] = (-sn * e[k]) + (cs * VectorS[k + 1]);
g = sn * e[k + 1].Real;
e[k + 1] = cs * e[k + 1];
if (ComputeVectors && k < matrixCopy.RowCount)
{
Csrot(MatrixU, matrixCopy.RowCount, k, k + 1, cs, sn);
}
}
e[m - 2] = f;
iter = iter + 1;
break;
// Convergence.
case 4:
// Make the singular value positive
if (VectorS[l].Real < 0.0)
{
VectorS[l] = -VectorS[l];
if (ComputeVectors)
{
CscalColumn(MatrixVT, matrixCopy.ColumnCount, l, 0, -1.0);
}
}
// Order the singular value.
while (l != mn - 1)
{
if (VectorS[l].Real >= VectorS[l + 1].Real)
{
break;
}
t = VectorS[l];
VectorS[l] = VectorS[l + 1];
VectorS[l + 1] = t;
if (ComputeVectors && l < matrixCopy.ColumnCount)
{
Swap(MatrixVT, matrixCopy.ColumnCount, l, l + 1);
}
if (ComputeVectors && l < matrixCopy.RowCount)
{
Swap(MatrixU, matrixCopy.RowCount, l, l + 1);
}
l = l + 1;
}
iter = 0;
m = m - 1;
break;
}
}
if (ComputeVectors)
{
MatrixVT = MatrixVT.ConjugateTranspose();
}
// Adjust the size of s if rows < columns. We are using ported copy of linpack's svd code and it uses
// a singular vector of length mRows+1 when mRows < mColumns. The last element is not used and needs to be removed.
// we should port lapack's svd routine to remove this problem.
if (matrixCopy.RowCount < matrixCopy.ColumnCount)
{
nm--;
var tmp = matrixCopy.CreateVector(nm);
for (i = 0; i < nm; i++)
{
tmp[i] = VectorS[i];
}
VectorS = tmp;
}
}
/// <summary>
/// Calculates absolute value of <paramref name="z1"/> multiplied on signum function of <paramref name="z2"/>
/// </summary>
/// <param name="z1">Complex value z1</param>
/// <param name="z2">Complex value z2</param>
/// <returns>Result multiplication of signum function and absolute value</returns>
private static Complex Csign(Complex z1, Complex z2)
{
return z1.Magnitude * (z2 / z2.Magnitude);
}
/// <summary>
/// Interchanges two vectors <paramref name="columnA"/> and <paramref name="columnB"/>
/// </summary>
/// <param name="a">Source matrix</param>
/// <param name="rowCount">The number of rows in <paramref name="a"/></param>
/// <param name="columnA">Column A index to swap</param>
/// <param name="columnB">Column B index to swap</param>
private static void Swap(Matrix<Complex> a, int rowCount, int columnA, int columnB)
{
for (var i = 0; i < rowCount; i++)
{
var z = a.At(i, columnA);
a.At(i, columnA, a.At(i, columnB));
a.At(i, columnB, z);
}
}
/// <summary>
/// Scale column <paramref name="column"/> by <paramref name="z"/> starting from row <paramref name="rowStart"/>
/// </summary>
/// <param name="a">Source matrix</param>
/// <param name="rowCount">The number of rows in <paramref name="a"/> </param>
/// <param name="column">Column to scale</param>
/// <param name="rowStart">Row to scale from</param>
/// <param name="z">Scale value</param>
private static void CscalColumn(Matrix<Complex> a, int rowCount, int column, int rowStart, Complex z)
{
for (var i = rowStart; i < rowCount; i++)
{
a.At(i, column, a.At(i, column) * z);
}
}
/// <summary>
/// Scale vector <paramref name="a"/> by <paramref name="z"/> starting from index <paramref name="start"/>
/// </summary>
/// <param name="a">Source vector</param>
/// <param name="start">Row to scale from</param>
/// <param name="z">Scale value</param>
private static void CscalVector(Complex[] a, int start, Complex z)
{
for (var i = start; i < a.Length; i++)
{
a[i] = a[i] * z;
}
}
/// <summary>
/// Given the Cartesian coordinates (da, db) of a point p, these fucntion return the parameters da, db, c, and s
/// associated with the Givens rotation that zeros the y-coordinate of the point.
/// </summary>
/// <param name="da">Provides the x-coordinate of the point p. On exit contains the parameter r associated with the Givens rotation</param>
/// <param name="db">Provides the y-coordinate of the point p. On exit contains the parameter z associated with the Givens rotation</param>
/// <param name="c">Contains the parameter c associated with the Givens rotation</param>
/// <param name="s">Contains the parameter s associated with the Givens rotation</param>
/// <remarks>This is equivalent to the DROTG LAPACK routine.</remarks>
private static void Srotg(ref double da, ref double db, ref double c, ref double s)
{
double r, z;
var roe = db;
var absda = Math.Abs(da);
var absdb = Math.Abs(db);
if (absda > absdb)
{
roe = da;
}
var scale = absda + absdb;
if (scale == 0.0)
{
c = 1.0;
s = 0.0;
r = 0.0;
z = 0.0;
}
else
{
var sda = da / scale;
var sdb = db / scale;
r = scale * Math.Sqrt((sda * sda) + (sdb * sdb));
if (roe < 0.0)
{
r = -r;
}
c = da / r;
s = db / r;
z = 1.0;
if (absda > absdb)
{
z = s;
}
if (absdb >= absda && c != 0.0)
{
z = 1.0 / c;
}
}
da = r;
db = z;
}
/// <summary>dded
/// Calculate Norm 2 of the column <paramref name="column"/> in matrix <paramref name="a"/> starting from row <paramref name="rowStart"/>
/// </summary>
/// <param name="a">Source matrix</param>
/// <param name="rowCount">The number of rows in <paramref name="a"/></param>
/// <param name="column">Column index</param>
/// <param name="rowStart">Start row index</param>
/// <returns>Norm2 (Euclidean norm) of trhe column</returns>
private static double Cnrm2Column(Matrix<Complex> a, int rowCount, int column, int rowStart)
{
var s = 0.0;
for (var i = rowStart; i < rowCount; i++)
{
s += a.At(i, column).Magnitude * a.At(i, column).Magnitude;
}
return Math.Sqrt(s);
}
/// <summary>
/// Calculate Norm 2 of the vector <paramref name="a"/> starting from index <paramref name="rowStart"/>
/// </summary>
/// <param name="a">Source vector</param>
/// <param name="rowStart">Start index</param>
/// <returns>Norm2 (Euclidean norm) of the vector</returns>
private static double Cnrm2Vector(Complex[] a, int rowStart)
{
var s = 0.0;
for (var i = rowStart; i < a.Length; i++)
{
s += a[i].Magnitude * a[i].Magnitude;
}
return Math.Sqrt(s);
}
/// <summary>
/// Calculate dot product of <paramref name="columnA"/> and <paramref name="columnB"/> conjugating the first vector.
/// </summary>
/// <param name="a">Source matrix</param>
/// <param name="rowCount">The number of rows in <paramref name="a"/></param>
/// <param name="columnA">Index of column A</param>
/// <param name="columnB">Index of column B</param>
/// <param name="rowStart">Starting row index</param>
/// <returns>Dot product value</returns>
private static Complex Cdotc(Matrix<Complex> a, int rowCount, int columnA, int columnB, int rowStart)
{
var z = Complex.Zero;
for (var i = rowStart; i < rowCount; i++)
{
z += a.At(i, columnA).Conjugate() * a.At(i, columnB);
}
return z;
}
/// <summary>
/// Performs rotation of points in the plane. Given two vectors x <paramref name="columnA"/> and y <paramref name="columnB"/>,
/// each vector element of these vectors is replaced as follows: x(i) = c*x(i) + s*y(i); y(i) = c*y(i) - s*x(i)
/// </summary>
/// <param name="a">Source matrix</param>
/// <param name="rowCount">The number of rows in <paramref name="a"/></param>
/// <param name="columnA">Index of column A</param>
/// <param name="columnB">Index of column B</param>
/// <param name="c">scalar cos value</param>
/// <param name="s">scalar sin value</param>
private static void Csrot(Matrix<Complex> a, int rowCount, int columnA, int columnB, double c, double s)
{
for (var i = 0; i < rowCount; i++)
{
var z = (c * a.At(i, columnA)) + (s * a.At(i, columnB));
var tmp = (c * a.At(i, columnB)) - (s * a.At(i, columnA));
a.At(i, columnB, tmp);
a.At(i, columnA, z);
}
}
/// <summary>
/// Solves a system of linear equations, <b>AX = B</b>, with A SVD factorized.
/// </summary>
/// <param name="input">The right hand side <see cref="Matrix{T}"/>, <b>B</b>.</param>
/// <param name="result">The left hand side <see cref="Matrix{T}"/>, <b>X</b>.</param>
public override void Solve(Matrix<Complex> input, Matrix<Complex> result)
{
// Check for proper arguments.
if (input == null)
{
throw new ArgumentNullException("input");
}
if (result == null)
{
throw new ArgumentNullException("result");
}
if (!ComputeVectors)
{
throw new InvalidOperationException(Resources.SingularVectorsNotComputed);
}
// The solution X should have the same number of columns as B
if (input.ColumnCount != result.ColumnCount)
{
throw new ArgumentException(Resources.ArgumentMatrixSameColumnDimension);
}
// The dimension compatibility conditions for X = A\B require the two matrices A and B to have the same number of rows
if (MatrixU.RowCount != input.RowCount)
{
throw new ArgumentException(Resources.ArgumentMatrixSameRowDimension);
}
// The solution X row dimension is equal to the column dimension of A
if (MatrixVT.ColumnCount != result.RowCount)
{
throw new ArgumentException(Resources.ArgumentMatrixSameColumnDimension);
}
var mn = Math.Min(MatrixU.RowCount, MatrixVT.ColumnCount);
var bn = input.ColumnCount;
var tmp = new Complex[MatrixVT.ColumnCount];
for (var k = 0; k < bn; k++)
{
for (var j = 0; j < MatrixVT.ColumnCount; j++)
{
var value = Complex.Zero;
if (j < mn)
{
for (var i = 0; i < MatrixU.RowCount; i++)
{
value += MatrixU.At(i, j).Conjugate() * input.At(i, k);
}
value /= VectorS[j];
}
tmp[j] = value;
}
for (var j = 0; j < MatrixVT.ColumnCount; j++)
{
var value = Complex.Zero;
for (var i = 0; i < MatrixVT.ColumnCount; i++)
{
value += MatrixVT.At(i, j).Conjugate() * tmp[i];
}
result[j, k] = value;
}
}
}
/// <summary>
/// Solves a system of linear equations, <b>Ax = b</b>, with A SVD factorized.
/// </summary>
/// <param name="input">The right hand side vector, <b>b</b>.</param>
/// <param name="result">The left hand side <see cref="Matrix{T}"/>, <b>x</b>.</param>
public override void Solve(Vector<Complex> input, Vector<Complex> result)
{
if (input == null)
{
throw new ArgumentNullException("input");
}
if (result == null)
{
throw new ArgumentNullException("result");
}
if (!ComputeVectors)
{
throw new InvalidOperationException(Resources.SingularVectorsNotComputed);
}
// Ax=b where A is an m x n matrix
// Check that b is a column vector with m entries
if (MatrixU.RowCount != input.Count)
{
throw new ArgumentException(Resources.ArgumentVectorsSameLength);
}
// Check that x is a column vector with n entries
if (MatrixVT.ColumnCount != result.Count)
{
throw new ArgumentException(Resources.ArgumentMatrixDimensions);
}
var mn = Math.Min(MatrixU.RowCount, MatrixVT.ColumnCount);
var tmp = new Complex[MatrixVT.ColumnCount];
for (var j = 0; j < MatrixVT.ColumnCount; j++)
{
var value = Complex.Zero;
if (j < mn)
{
for (var i = 0; i < MatrixU.RowCount; i++)
{
value += MatrixU.At(i, j).Conjugate() * input[i];
}
value /= VectorS[j];
}
tmp[j] = value;
}
for (var j = 0; j < MatrixVT.ColumnCount; j++)
{
var value = Complex.Zero;
for (var i = 0; i < MatrixVT.ColumnCount; i++)
{
value += MatrixVT.At(i, j).Conjugate() * tmp[i];
}
result[j] = value;
}
}
#region Simple arithmetic of type T
/// <summary>
/// Multiply two values T*T
/// </summary>
/// <param name="val1">Left operand value</param>
/// <param name="val2">Right operand value</param>
/// <returns>Result of multiplication</returns>
protected sealed override Complex MultiplyT(Complex val1, Complex val2)
{
return val1 * val2;
}
/// <summary>
/// Returns the absolute value of a specified number.
/// </summary>
/// <param name="val1"> A number whose absolute is to be found</param>
/// <returns>Absolute value </returns>
protected sealed override double AbsoluteT(Complex val1)
{
return val1.Magnitude;
}
/// <summary>
/// Get value of type T equal to one
/// </summary>
/// <returns>One value</returns>
protected sealed override Complex OneValueT
{
get { return Complex.One; }
}
#endregion
}
}

521
src/Numerics/LinearAlgebra/Complex/Solvers/Iterative/BiCgStab.cs

@ -0,0 +1,521 @@
// <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>
namespace MathNet.Numerics.LinearAlgebra.Complex.Solvers.Iterative
{
using System;
using System.Numerics;
using Generic;
using Generic.Solvers;
using Generic.Solvers.Preconditioners;
using Generic.Solvers.Status;
using Preconditioners;
using Properties;
/// <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<Complex>
{
/// <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<Complex> _preconditioner;
/// <summary>
/// The iterative process controller.
/// </summary>
private IIterator<Complex> _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{T}"/> 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{T}"/> 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{T}.Status"/> property.
/// </item>
/// </list>
/// </para>
/// </remarks>
/// <param name="iterator">The <see cref="IIterator{T}"/> that will be used to monitor the iterative process. </param>
public BiCgStab(IIterator<Complex> 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{T}"/> with
/// the standard settings.
/// </remarks>
/// <param name="preconditioner">The <see cref="IPreConditioner{T}"/> that will be used to precondition the matrix equation.</param>
public BiCgStab(IPreConditioner<Complex> 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{T}"/> 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{T}.Status"/> property.
/// </item>
/// </list>
/// </para>
/// </remarks>
/// <param name="preconditioner">The <see cref="IPreConditioner{T}"/> that will be used to precondition the matrix equation. </param>
/// <param name="iterator">The <see cref="IIterator{T}"/> that will be used to monitor the iterative process. </param>
public BiCgStab(IPreConditioner<Complex> preconditioner, IIterator<Complex> iterator)
{
_iterator = iterator;
_preconditioner = preconditioner;
}
/// <summary>
/// Sets the <see cref="IPreConditioner{T}"/> that will be used to precondition the iterative process.
/// </summary>
/// <param name="preconditioner">The preconditioner.</param>
public void SetPreconditioner(IPreConditioner<Complex> preconditioner)
{
_preconditioner = preconditioner;
}
/// <summary>
/// Sets the <see cref="IIterator{T}"/> that will be used to track the iterative process.
/// </summary>
/// <param name="iterator">The iterator.</param>
public void SetIterator(IIterator<Complex> 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{T}"/>, <c>A</c>.</param>
/// <param name="vector">The solution <see cref="Vector{T}"/>, <c>b</c>.</param>
/// <returns>The result <see cref="Vector{T}"/>, <c>x</c>.</returns>
public Vector<Complex> Solve(Matrix<Complex> matrix, Vector<Complex> vector)
{
if (vector == null)
{
throw new ArgumentNullException();
}
Vector<Complex> 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{T}"/>, <c>A</c>.</param>
/// <param name="input">The solution <see cref="Vector{T}"/>, <c>b</c>.</param>
/// <param name="result">The result <see cref="Vector{T}"/>, <c>x</c>.</param>
public void Solve(Matrix<Complex> matrix, Vector<Complex> input, Vector<Complex> result)
{
// If we were stopped before, we are no longer
// We're doing this at the start of the method to ensure
// that we can use these fields immediately.
_hasBeenStopped = false;
// Parameters checks
if (matrix == null)
{
throw new ArgumentNullException("matrix");
}
if (matrix.RowCount != matrix.ColumnCount)
{
throw new ArgumentException(Resources.ArgumentMatrixSquare, "matrix");
}
if (input == null)
{
throw new ArgumentNullException("input");
}
if (result == null)
{
throw new ArgumentNullException("result");
}
if (result.Count != input.Count)
{
throw new ArgumentException(Resources.ArgumentVectorsSameLength);
}
// Initialize the solver fields
// Set the convergence monitor
if (_iterator == null)
{
_iterator = Iterator.CreateDefault();
}
if (_preconditioner == null)
{
_preconditioner = new UnitPreconditioner();
}
_preconditioner.Initialize(matrix);
// Compute r_0 = b - Ax_0 for some initial guess x_0
// In this case we take x_0 = vector
// This is basically a SAXPY so it could be made a lot faster
Vector<Complex> residuals = new DenseVector(matrix.RowCount);
CalculateTrueResidual(matrix, residuals, result, input);
// Choose r~ (for example, r~ = r_0)
var tempResiduals = residuals.Clone();
var temp = result.Clone();
// create five temporary vectors needed to hold temporary
// coefficients. All vectors are mangled in each iteration.
// These are defined here to prevent stressing the garbage collector
Vector<Complex> vecP = new DenseVector(residuals.Count);
Vector<Complex> vecPdash = new DenseVector(residuals.Count);
Vector<Complex> nu = new DenseVector(residuals.Count);
Vector<Complex> vecS = new DenseVector(residuals.Count);
Vector<Complex> vecSdash = new DenseVector(residuals.Count);
Vector<Complex> t = 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))
vecP.Add(nu.Multiply(-omega), vecP);
vecP.Multiply(beta, vecP);
vecP.Add(residuals, 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
residuals.Add(nu.Multiply(-alpha), 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, temp);
temp.Add(result, 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, t);
// omega_i = temp^T s / temp^T temp
omega = t.DotProduct(vecS) / t.DotProduct(t);
// x_i = x_(i-1) + alpha_i p^ + omega_i s^
result.Add(vecSdash.Multiply(omega), result);
result.Add(vecPdash.Multiply(alpha), result);
t.Multiply(-omega, residuals);
residuals.Add(vecS, residuals);
// 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 true residual of the matrix equation Ax = b according to: residual = b - Ax
/// </summary>
/// <param name="matrix">Instance of the <see cref="Matrix{T}"/> A.</param>
/// <param name="residual">Residual values in <see cref="Vector{T}"/>.</param>
/// <param name="x">Instance of the <see cref="Vector{T}"/> x.</param>
/// <param name="b">Instance of the <see cref="Vector{T}"/> b.</param>
private static void CalculateTrueResidual(Matrix<Complex> matrix, Vector<Complex> residual, Vector<Complex> x, Vector<Complex> 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{T}"/>.</param>
/// <param name="source">Source <see cref="Vector{T}"/>.</param>
/// <param name="residuals">Residual <see cref="Vector{T}"/>.</param>
/// <returns><c>true</c> if continue, otherwise <c>false</c></returns>
private bool ShouldContinue(int iterationNumber, Vector<Complex> result, Vector<Complex> source, Vector<Complex> 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{T}"/>, <c>A</c>.</param>
/// <param name="input">The solution <see cref="Matrix{T}"/>, <c>B</c>.</param>
/// <returns>The result <see cref="Matrix{T}"/>, <c>X</c>.</returns>
public Matrix<Complex> Solve(Matrix<Complex> matrix, Matrix<Complex> input)
{
if (matrix == null)
{
throw new ArgumentNullException("matrix");
}
if (input == null)
{
throw new ArgumentNullException("input");
}
var result = 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{T}"/>, <c>A</c>.</param>
/// <param name="input">The solution <see cref="Matrix{T}"/>, <c>B</c>.</param>
/// <param name="result">The result <see cref="Matrix{T}"/>, <c>X</c></param>
public void Solve(Matrix<Complex> matrix, Matrix<Complex> input, Matrix<Complex> 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 new ArgumentException(Resources.ArgumentMatrixDimensions);
}
for (var column = 0; column < input.ColumnCount; column++)
{
var solution = Solve(matrix, input.Column(column));
foreach (var element in solution.GetIndexedEnumerator())
{
result.At(element.Key, column, element.Value);
}
}
}
}
}

630
src/Numerics/LinearAlgebra/Complex/Solvers/Iterative/CompositeSolver.cs

@ -0,0 +1,630 @@
// <copyright file="CompositeSolver.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>
namespace MathNet.Numerics.LinearAlgebra.Complex.Solvers.Iterative
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Numerics;
using System.Reflection;
using Generic;
using Generic.Solvers;
using Generic.Solvers.Status;
using Properties;
/// <summary>
/// A composite matrix solver. The actual solver is made by a sequence of
/// matrix solvers.
/// </summary>
/// <remarks>
/// <para>
/// Solver based on:<br />
/// Faster PDE-based simulations using robust composite linear solvers<br />
/// S. Bhowmicka, P. Raghavan a,*, L. McInnes b, B. Norris<br />
/// Future Generation Computer Systems, Vol 20, 2004, pp 373–387<br />
/// </para>
/// <para>
/// Note that if an iterator is passed to this solver it will be used for all the sub-solvers.
/// </para>
/// </remarks>
public sealed class CompositeSolver : IIterativeSolver<Complex>
{
#region Internal class - DoubleComparer
/// <summary>
/// An <c>IComparer</c> used to compare double precision floating points.
/// </summary>
/// NOTE: The instance of this class is used only in <see cref="SolverSetups"/>. If C# suppports interface inheritence
/// NOTE: and methods in anonymous types, then this class should be deleted and anonymous type implemented with IComaprer support
/// NOTE: in <see cref="SolverSetups"/> constructor
public sealed class DoubleComparer : IComparer<double>
{
/// <summary>
/// Compares two double values based on the selected comparison method.
/// </summary>
/// <param name="x">The first double to compare.</param>
/// <param name="y">The second double to compare.</param>
/// <returns>
/// A 32-bit signed integer that indicates the relative order of the objects being compared. The return
/// value has the following meanings:
/// Value Meaning Less than zero This object is less than the other parameter.
/// Zero This object is equal to other.
/// Greater than zero This object is greater than other.
/// </returns>
public int Compare(double x, double y)
{
return x.CompareTo(y, 1);
}
}
#endregion
/// <summary>
/// The default status used if the solver is not running.
/// </summary>
private static readonly ICalculationStatus NonRunningStatus = new CalculationIndetermined();
/// <summary>
/// The default status used if the solver is running.
/// </summary>
private static readonly ICalculationStatus RunningStatus = new CalculationRunning();
#if SILVERLIGHT
private static readonly Dictionary<double, List<IIterativeSolverSetup<Complex>>> SolverSetups = new Dictionary<double, List<IIterativeSolverSetup<Complex>>>();
#else
/// <summary>
/// The collection of iterative solver setups. Stored based on the
/// ratio between the relative speed and relative accuracy.
/// </summary>
private static readonly SortedList<double, List<IIterativeSolverSetup<Complex>>> SolverSetups = new SortedList<double, List<IIterativeSolverSetup<Complex>>>(new DoubleComparer());
#endif
#region Solver information loading methods
/// <summary>
/// Loads all the available <see cref="IIterativeSolverSetup{T}"/> objects from the MathNet.Numerics assembly.
/// </summary>
public static void LoadSolverInformation()
{
LoadSolverInformation(new Type[0]);
}
/// <summary>
/// Loads the available <see cref="IIterativeSolverSetup{T}"/> objects from the MathNet.Numerics assembly.
/// </summary>
/// <param name="typesToExclude">The <see cref="IIterativeSolver{T}"/> types that should not be loaded.</param>
public static void LoadSolverInformation(Type[] typesToExclude)
{
LoadSolverInformationFromAssembly(Assembly.GetExecutingAssembly(), typesToExclude);
}
/// <summary>
/// Loads the available <see cref="IIterativeSolverSetup{T}"/> objects from the assembly specified by the file location.
/// </summary>
/// <param name="assemblyLocation">The fully qualified path to the assembly.</param>
public static void LoadSolverInformationFromAssembly(string assemblyLocation)
{
LoadSolverInformationFromAssembly(assemblyLocation, new Type[0]);
}
/// <summary>
/// Loads the available <see cref="IIterativeSolverSetup{T}"/> objects from the assembly specified by the file location.
/// </summary>
/// <param name="assemblyLocation">The fully qualified path to the assembly.</param>
/// <param name="typesToExclude">The <see cref="IIterativeSolver{T}"/> types that should not be loaded. </param>
public static void LoadSolverInformationFromAssembly(string assemblyLocation, params Type[] typesToExclude)
{
if (assemblyLocation == null)
{
throw new ArgumentNullException("assemblyLocation");
}
if (assemblyLocation.Length == 0)
{
throw new ArgumentException();
}
if (!File.Exists(assemblyLocation))
{
throw new FileNotFoundException();
}
// Get the assembly name
var assemblyFileName = Path.GetFileNameWithoutExtension(assemblyLocation);
// Now load the assembly with an AssemblyName
var assemblyName = new AssemblyName(assemblyFileName);
var assembly = Assembly.Load(assemblyName);
// <ay throws:
// ArgumentNullException --> Can't get this because we checked that the file exists.
// FileNotFoundException --> Can't get this because we checked that the file exists.
// FileLoadException
// BadImageFormatException
// Now we can load the solver information.
LoadSolverInformationFromAssembly(assembly, typesToExclude);
}
/// <summary>
/// Loads the available <see cref="IIterativeSolverSetup{T}"/> objects from the assembly specified by the assembly name.
/// </summary>
/// <param name="assemblyName">The <see cref="AssemblyName"/> of the assembly that should be searched for setup objects. </param>
public static void LoadSolverInformationFromAssembly(AssemblyName assemblyName)
{
LoadSolverInformationFromAssembly(assemblyName, new Type[0]);
}
/// <summary>
/// Loads the available <see cref="IIterativeSolverSetup{T}"/> objects from the assembly specified by the assembly name.
/// </summary>
/// <param name="assemblyName">The <see cref="AssemblyName"/> of the assembly that should be searched for setup objects.</param>
/// <param name="typesToExclude">The <see cref="IIterativeSolver{T}"/> types that should not be loaded.</param>
public static void LoadSolverInformationFromAssembly(AssemblyName assemblyName, params Type[] typesToExclude)
{
if (assemblyName == null)
{
throw new ArgumentNullException("assemblyName");
}
var assembly = Assembly.Load(assemblyName);
// May throw:
// ArgumentNullException --> Can't get this because we checked it.
// FileNotFoundException
// FileLoadException
// BadImageFormatException
// Now we can load the solver information.
LoadSolverInformationFromAssembly(assembly, typesToExclude);
}
/// <summary>
/// Loads the available <see cref="IIterativeSolverSetup{T}"/> objects from the assembly specified by the type.
/// </summary>
/// <param name="typeInAssembly">The type in the assembly which should be searched for setup objects.</param>
public static void LoadSolverInformationFromAssembly(Type typeInAssembly)
{
LoadSolverInformationFromAssembly(typeInAssembly, new Type[0]);
}
/// <summary>
/// Loads the available <see cref="IIterativeSolverSetup{T}"/> objects from the assembly specified by the type.
/// </summary>
/// <param name="typeInAssembly">The type in the assembly which should be searched for setup objects.</param>
/// <param name="typesToExclude">The <see cref="IIterativeSolver{T}"/> types that should not be loaded.</param>
public static void LoadSolverInformationFromAssembly(Type typeInAssembly, params Type[] typesToExclude)
{
if (typeInAssembly == null)
{
throw new ArgumentNullException("typeInAssembly");
}
LoadSolverInformationFromAssembly(typeInAssembly.Assembly, typesToExclude);
}
/// <summary>
/// Loads the available <see cref="IIterativeSolverSetup{T}"/> objects from the specified assembly.
/// </summary>
/// <param name="assembly">The assembly which will be searched for setup objects.</param>
public static void LoadSolverInformationFromAssembly(Assembly assembly)
{
LoadSolverInformationFromAssembly(assembly, new Type[0]);
}
/// <summary>
/// Loads the available <see cref="IIterativeSolverSetup{T}"/> objects from the specified assembly.
/// </summary>
/// <param name="assembly">The assembly which will be searched for setup objects.</param>
/// <param name="typesToExclude">The <see cref="IIterativeSolver{T}"/> types that should not be loaded.</param>
public static void LoadSolverInformationFromAssembly(Assembly assembly, params Type[] typesToExclude)
{
if (assembly == null)
{
throw new ArgumentNullException("Assembly");
}
if (typesToExclude == null)
{
throw new ArgumentNullException("typesToExclude");
}
var excludedTypes = new List<Type>(typesToExclude);
// Load all the types in the assembly
// Find all the types that implement IIterativeSolverSetup
// Create an object of each of these types
// Get the type of the iterative solver that will be instantiated by the setup object
// Check if it's on the excluding list, if so throw the setup object away otherwise keep it.
var interfaceTypes = new List<Type>();
foreach (var type in assembly.GetTypes().Where(type => (!type.IsAbstract && !type.IsEnum && !type.IsInterface && type.IsVisible)))
{
interfaceTypes.AddRange(type.GetInterfaces());
if (!interfaceTypes.Any(match => typeof(IIterativeSolverSetup<Complex>).IsAssignableFrom(match)))
{
continue;
}
// See if we actually want this type of iterative solver
IIterativeSolverSetup<Complex> setup;
try
{
// If something goes wrong we just ignore it and move on with the next type.
// There should probably be a log somewhere indicating that something went wrong?
setup = (IIterativeSolverSetup<Complex>)Activator.CreateInstance(type);
}
catch (ArgumentException)
{
continue;
}
catch (NotSupportedException)
{
continue;
}
catch (TargetInvocationException)
{
continue;
}
catch (MethodAccessException)
{
continue;
}
catch (MissingMethodException)
{
continue;
}
catch (MemberAccessException)
{
continue;
}
catch (TypeLoadException)
{
continue;
}
if (excludedTypes.Any(match => match.IsAssignableFrom(setup.SolverType) ||
match.IsAssignableFrom(setup.PreconditionerType)))
{
continue;
}
// Ok we want the solver, so store the object
var ratio = setup.SolutionSpeed / setup.Reliability;
if (!SolverSetups.ContainsKey(ratio))
{
SolverSetups.Add(ratio, new List<IIterativeSolverSetup<Complex>>());
}
var list = SolverSetups[ratio];
list.Add(setup);
}
}
#endregion
/// <summary>
/// The collection of solvers that will be used to
/// </summary>
private readonly List<IIterativeSolver<Complex>> _solvers = new List<IIterativeSolver<Complex>>();
/// <summary>
/// The status of the calculation.
/// </summary>
private ICalculationStatus _status = NonRunningStatus;
/// <summary>
/// The iterator that is used to control the iteration process.
/// </summary>
private IIterator<Complex> _iterator;
/// <summary>
/// A flag indicating if the solver has been stopped or not.
/// </summary>
private bool _hasBeenStopped;
/// <summary>
/// The solver that is currently running. Reference is used to be able to stop the
/// solver if the user cancels the solve process.
/// </summary>
private IIterativeSolver<Complex> _currentSolver;
/// <summary>
/// Initializes a new instance of the <see cref="CompositeSolver"/> class with the default iterator.
/// </summary>
public CompositeSolver() : this(null)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="CompositeSolver"/> class with the specified iterator.
/// </summary>
/// <param name="iterator">The iterator that will be used to control the iteration process. </param>
public CompositeSolver(IIterator<Complex> iterator)
{
_iterator = iterator;
}
/// <summary>
/// Sets the <c>IIterator</c> that will be used to track the iterative process.
/// </summary>
/// <param name="iterator">The iterator.</param>
public void SetIterator(IIterator<Complex> iterator)
{
_iterator = iterator;
}
/// <summary>
/// Gets the status of the iteration once the calculation is finished.
/// </summary>
public ICalculationStatus IterationResult
{
get
{
return _status;
}
}
/// <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;
if (_currentSolver != null)
{
_currentSolver.StopSolve();
}
}
/// <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="vector">The solution vector, <c>b</c>.</param>
/// <returns>The result vector, <c>x</c>.</returns>
public Vector<Complex> Solve(Matrix<Complex> matrix, Vector<Complex> vector)
{
if (vector == null)
{
throw new ArgumentNullException();
}
Vector<Complex> 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 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<Complex> matrix, Vector<Complex> input, Vector<Complex> result)
{
// If we were stopped before, we are no longer
// We're doing this at the start of the method to ensure
// that we can use these fields immediately.
_hasBeenStopped = false;
_currentSolver = null;
// 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);
}
// Initialize the solver fields
// Set the convergence monitor
if (_iterator == null)
{
_iterator = Iterator.CreateDefault();
}
// Load the solvers into our own internal data structure
// Once we have solvers we can always reuse them.
if (_solvers.Count == 0)
{
LoadSolvers();
}
// Create a copy of the solution and result vectors so we can use them
// later on
var internalInput = input.Clone();
var internalResult = result.Clone();
foreach (var solver in _solvers.TakeWhile(solver => !_hasBeenStopped))
{
// Store a reference to the solver so we can stop it.
_currentSolver = solver;
try
{
// Reset the iterator and pass it to the solver
_iterator.ResetToPrecalculationState();
solver.SetIterator(_iterator);
// Start the solver
solver.Solve(matrix, internalInput, internalResult);
}
catch (Exception)
{
// The solver broke down.
// Log a message about this
// Switch to the next preconditioner.
// Reset the solution vector to the previous solution
input.CopyTo(internalInput);
_status = RunningStatus;
continue;
}
// There was no fatal breakdown so check the status
if (_iterator.Status is CalculationConverged)
{
// We're done
break;
}
// We're not done
// Either:
// - calculation finished without convergence
if (_iterator.Status is CalculationStoppedWithoutConvergence)
{
// Copy the internal result to the result vector and
// continue with the calculation.
internalInput.CopyTo(input);
}
else
{
// - calculation failed --> restart with the original vector
// - calculation diverged --> restart with the original vector
// - Some unknown status occurred --> To be safe restart.
input.CopyTo(internalInput);
}
}
// Inside the loop we already copied the final results (if there are any)
// So no need to do that again.
// Clean up
// No longer need the current solver
_currentSolver = null;
// Set the final status
_status = _iterator.Status;
}
/// <summary>
/// Load solvers
/// </summary>
private void LoadSolvers()
{
if (SolverSetups.Count == 0)
{
throw new Exception("IIterativeSolverSetup objects not found");
}
#if SILVERLIGHT
foreach (var setup in SolverSetups.OrderBy(solver => solver.Key, new DoubleComparer()).Select(pair => pair.Value).SelectMany(setups => setups))
#else
foreach (var setup in SolverSetups.Select(pair => pair.Value).SelectMany(setups => setups))
#endif
{
_solvers.Add(setup.CreateNew());
}
}
/// <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 matrix, <c>A</c>.</param>
/// <param name="input">The solution matrix, <c>B</c>.</param>
/// <returns>The result matrix, <c>X</c>.</returns>
public Matrix<Complex> Solve(Matrix<Complex> matrix, Matrix<Complex> input)
{
if (matrix == null)
{
throw new ArgumentNullException("matrix");
}
if (input == null)
{
throw new ArgumentNullException("input");
}
var result = 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 matrix, <c>A</c>.</param>
/// <param name="input">The solution matrix, <c>B</c>.</param>
/// <param name="result">The result matrix, <c>X</c></param>
public void Solve(Matrix<Complex> matrix, Matrix<Complex> input, Matrix<Complex> 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 new ArgumentException(Resources.ArgumentMatrixDimensions);
}
for (var column = 0; column < input.ColumnCount; column++)
{
var solution = Solve(matrix, input.Column(column));
foreach (var element in solution.GetIndexedEnumerator())
{
result.At(element.Key, column, element.Value);
}
}
}
}
}

624
src/Numerics/LinearAlgebra/Complex/Solvers/Iterative/GpBiCg.cs

@ -0,0 +1,624 @@
// <copyright file="GpBiCg.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>
namespace MathNet.Numerics.LinearAlgebra.Complex.Solvers.Iterative
{
using System;
using System.Numerics;
using Generic;
using Generic.Solvers;
using Generic.Solvers.Preconditioners;
using Generic.Solvers.Status;
using Preconditioners;
using Properties;
/// <summary>
/// A Generalized Product Bi-Conjugate Gradient iterative matrix solver.
/// </summary>
/// <remarks>
/// <para>
/// The Generalized Product Bi-Conjugate Gradient (GPBiCG) solver is an
/// alternative version of the Bi-Conjugate Gradient stabilized (CG) solver.
/// Unlike the CG solver the GPBiCG solver 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 GPBiCG algorithm was taken from: <br/>
/// GPBiCG(m,l): A hybrid of BiCGSTAB and GPBiCG methods with
/// efficiency and robustness
/// <br/>
/// S. Fujino
/// <br/>
/// Applied Numerical Mathematics, Volume 41, 2002, pp 107 - 117
/// <br/>
/// </para>
/// <para>
/// The example code below provides an indication of the possible use of the
/// solver.
/// </para>
/// </remarks>
public sealed class GpBiCg : IIterativeSolver<Complex>
{
/// <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 <c>null</c>, in which case the default
/// pre-conditioner will be used.
/// </summary>
private IPreConditioner<Complex> _preconditioner;
/// <summary>
/// The iterative process controller.
/// </summary>
private IIterator<Complex> _iterator;
/// <summary>
/// Indicates the number of <c>BiCGStab</c> steps should be taken
/// before switching.
/// </summary>
private int _numberOfBiCgStabSteps = 1;
/// <summary>
/// Indicates the number of <c>GPBiCG</c> steps should be taken
/// before switching.
/// </summary>
private int _numberOfGpbiCgSteps = 4;
/// <summary>
/// Indicates if the user has stopped the solver.
/// </summary>
private bool _hasBeenStopped;
/// <summary>
/// Initializes a new instance of the <see cref="GpBiCg"/> class.
/// </summary>
/// <remarks>
/// When using this constructor the solver will use the <see cref="IIterator{T}"/> with
/// the standard settings and a default preconditioner.
/// </remarks>
public GpBiCg() : this(null, null)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="GpBiCg"/> 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{T}"/> 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{T}.Status"/> property.
/// </item>
/// </list>
/// </para>
/// </remarks>
/// <param name="iterator">The <see cref="IIterator{T}"/> that will be used to monitor the iterative process.</param>
public GpBiCg(IIterator<Complex> iterator) : this(null, iterator)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="GpBiCg"/> class.
/// </summary>
/// <remarks>
/// When using this constructor the solver will use the <see cref="IIterator{T}"/> with
/// the standard settings.
/// </remarks>
/// <param name="preconditioner">The <see cref="IPreConditioner{T}"/> that will be used to precondition the matrix equation.</param>
public GpBiCg(IPreConditioner<Complex> preconditioner) : this(preconditioner, null)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="GpBiCg"/> class.
/// </summary>
/// <remarks>
/// <para>
/// The main advantages of using a user defined <see cref="IIterator{T}"/> 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{T}.Status"/> property.
/// </item>
/// </list>
/// </para>
/// </remarks>
/// <param name="preconditioner">The <see cref="IPreConditioner{T}"/> that will be used to precondition the matrix equation.</param>
/// <param name="iterator">The <see cref="IIterator{T}"/> that will be used to monitor the iterative process.</param>
public GpBiCg(IPreConditioner<Complex> preconditioner, IIterator<Complex> iterator)
{
_iterator = iterator;
_preconditioner = preconditioner;
}
/// <summary>
/// Gets or sets the number of steps taken with the <c>BiCgStab</c> algorithm
/// before switching over to the <c>GPBiCG</c> algorithm.
/// </summary>
public int NumberOfBiCgStabSteps
{
get
{
return _numberOfBiCgStabSteps;
}
set
{
if (value < 0)
{
throw new ArgumentOutOfRangeException("value");
}
_numberOfBiCgStabSteps = value;
}
}
/// <summary>
/// Gets or sets the number of steps taken with the <c>GPBiCG</c> algorithm
/// before switching over to the <c>BiCgStab</c> algorithm.
/// </summary>
public int NumberOfGpBiCgSteps
{
get
{
return _numberOfGpbiCgSteps;
}
set
{
if (value < 0)
{
throw new ArgumentOutOfRangeException("value");
}
_numberOfGpbiCgSteps = value;
}
}
/// <summary>
/// Sets the <see cref="IPreConditioner{T}"/> that will be used to precondition the iterative process.
/// </summary>
/// <param name="preconditioner">The preconditioner.</param>
public void SetPreconditioner(IPreConditioner<Complex> preconditioner)
{
_preconditioner = preconditioner;
}
/// <summary>
/// Sets the <see cref="IIterator{T}"/> that will be used to track the iterative process.
/// </summary>
/// <param name="iterator">The iterator.</param>
public void SetIterator(IIterator<Complex> 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 matrix, <c>A</c>.</param>
/// <param name="vector">The solution vector, <c>b</c>.</param>
/// <returns>The result vector, <c>x</c>.</returns>
public Vector<Complex> Solve(Matrix<Complex> matrix, Vector<Complex> vector)
{
if (vector == null)
{
throw new ArgumentNullException();
}
Vector<Complex> 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 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<Complex> matrix, Vector<Complex> input, Vector<Complex> result)
{
// If we were stopped before, we are no longer
// We're doing this at the start of the method to ensure
// that we can use these fields immediately.
_hasBeenStopped = false;
// 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);
}
// 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<Complex> xtemp = new DenseVector(input.Count);
// r_0 = b - Ax_0
// This is basically a SAXPY so it could be made a lot faster
Vector<Complex> 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<Complex> rdash = new DenseVector(residuals);
// t_-1 = 0
Vector<Complex> t = new DenseVector(residuals.Count);
Vector<Complex> t0 = new DenseVector(residuals.Count);
// w_-1 = 0
Vector<Complex> w = new DenseVector(residuals.Count);
// Define the remaining temporary vectors
Vector<Complex> c = new DenseVector(residuals.Count);
Vector<Complex> p = new DenseVector(residuals.Count);
Vector<Complex> s = new DenseVector(residuals.Count);
Vector<Complex> u = new DenseVector(residuals.Count);
Vector<Complex> y = new DenseVector(residuals.Count);
Vector<Complex> z = new DenseVector(residuals.Count);
Vector<Complex> temp = 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);
residuals.Add(temp.Multiply(beta), 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);
y.Add(temp.Multiply(alpha), y);
// Store the old value of t in t0
t.CopyTo(t0);
// t_k = r_k - alpha_k s_k
residuals.Add(s.Multiply(-alpha), 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))
t0.Add(u.Multiply(beta), temp);
temp.Subtract(residuals, temp);
temp.Multiply(eta, temp);
temp.Add(s.Multiply(sigma), u);
// z_k = sigma_k r_k +_ eta_k z_(k-1) - alpha_k u_k
z.Multiply(eta, z);
z.Add(u.Multiply(-alpha), z);
z.Add(residuals.Multiply(sigma), z);
// x_(k+1) = x_k + alpha_k p_k + z_k
xtemp.Add(p.Multiply(alpha), xtemp);
xtemp.Add(z, 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);
t.Add(y.Multiply(-eta), residuals);
residuals.Add(c.Multiply(-sigma), 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
c.Add(s.Multiply(beta), 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>
/// Calculates the true residual of the matrix equation Ax = b according to: residual = b - Ax
/// </summary>
/// <param name="matrix">Instance of the <see cref="Matrix{T}"/> A.</param>
/// <param name="residual">Residual values in <see cref="Vector{T}"/>.</param>
/// <param name="x">Instance of the <see cref="Vector{T}"/> x.</param>
/// <param name="b">Instance of the <see cref="Vector{T}"/> b.</param>
private static void CalculateTrueResidual(Matrix<Complex> matrix, Vector<Complex> residual, Vector<Complex> x, Vector<Complex> b)
{
// -Ax = residual
matrix.Multiply(x, residual);
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{T}"/>.</param>
/// <param name="source">Source <see cref="Vector{T}"/>.</param>
/// <param name="residuals">Residual <see cref="Vector{T}"/>.</param>
/// <returns><c>true</c> if continue, otherwise <c>false</c></returns>
private bool ShouldContinue(int iterationNumber, Vector<Complex> result, Vector<Complex> source, Vector<Complex> 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>
/// Decide if to do steps with BiCgStab
/// </summary>
/// <param name="iterationNumber">Number of iteration</param>
/// <returns><c>true</c> if yes, otherwise <c>false</c></returns>
private bool ShouldRunBiCgStabSteps(int iterationNumber)
{
// Run the first steps as BiCGStab
// The number of steps past a whole iteration set
var difference = iterationNumber % (_numberOfBiCgStabSteps + _numberOfGpbiCgSteps);
// Do steps with BiCGStab if:
// - The difference is zero or more (i.e. we have done zero or more complete cycles)
// - The difference is less than the number of BiCGStab steps that should be taken
return (difference >= 0) && (difference < _numberOfBiCgStabSteps);
}
/// <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 matrix, <c>A</c>.</param>
/// <param name="input">The solution matrix, <c>B</c>.</param>
/// <returns>The result matrix, <c>X</c>.</returns>
public Matrix<Complex> Solve(Matrix<Complex> matrix, Matrix<Complex> input)
{
if (matrix == null)
{
throw new ArgumentNullException("matrix");
}
if (input == null)
{
throw new ArgumentNullException("input");
}
var result = 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 matrix, <c>A</c>.</param>
/// <param name="input">The solution matrix, <c>B</c>.</param>
/// <param name="result">The result matrix, <c>X</c></param>
public void Solve(Matrix<Complex> matrix, Matrix<Complex> input, Matrix<Complex> 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 new ArgumentException(Resources.ArgumentMatrixDimensions);
}
for (var column = 0; column < input.ColumnCount; column++)
{
var solution = Solve(matrix, input.Column(column));
foreach (var element in solution.GetIndexedEnumerator())
{
result.At(element.Key, column, element.Value);
}
}
}
}
}

783
src/Numerics/LinearAlgebra/Complex/Solvers/Iterative/MlkBiCgStab.cs

@ -0,0 +1,783 @@
// <copyright file="MlkBiCgStab.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>
namespace MathNet.Numerics.LinearAlgebra.Complex.Solvers.Iterative
{
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Numerics;
using Distributions;
using Factorization;
using Generic;
using Generic.Solvers;
using Generic.Solvers.Preconditioners;
using Generic.Solvers.Status;
using Preconditioners;
using Properties;
/// <summary>
/// A Multiple-Lanczos Bi-Conjugate Gradient stabilized iterative matrix solver.
/// </summary>
/// <remarks>
/// <para>
/// The Multiple-Lanczos Bi-Conjugate Gradient stabilized (ML(k)-BiCGStab) solver is an 'improvement'
/// of the standard BiCgStab solver.
/// </para>
/// <para>
/// The algorithm was taken from: <br/>
/// ML(k)BiCGSTAB: A BiCGSTAB variant based on multiple Lanczos starting vectors
/// <br/>
/// Man-chung Yeung and Tony F. Chan
/// <br/>
/// SIAM Journal of Scientific Computing
/// <br/>
/// Volume 21, Number 4, pp. 1263 - 1290
/// </para>
/// <para>
/// The example code below provides an indication of the possible use of the
/// solver.
/// </para>
/// </remarks>
public sealed class MlkBiCgStab : IIterativeSolver<Complex>
{
/// <summary>
/// The default number of starting vectors.
/// </summary>
private const int DefaultNumberOfStartingVectors = 50;
/// <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<Complex> _preconditioner;
/// <summary>
/// The iterative process controller.
/// </summary>
private IIterator<Complex> _iterator;
/// <summary>
/// The collection of starting vectors which are used as the basis for the Krylov sub-space.
/// </summary>
private IList<Vector<Complex>> _startingVectors;
/// <summary>
/// The number of starting vectors used by the algorithm
/// </summary>
private int _numberOfStartingVectors = DefaultNumberOfStartingVectors;
/// <summary>
/// Indicates if the user has stopped the solver.
/// </summary>
private bool _hasBeenStopped;
/// <summary>
/// Initializes a new instance of the <see cref="MlkBiCgStab"/> class.
/// </summary>
/// <remarks>
/// When using this constructor the solver will use the <see cref="IIterator{T}"/> with
/// the standard settings and a default preconditioner.
/// </remarks>
public MlkBiCgStab() : this(null, null)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="MlkBiCgStab"/> 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{T}"/> 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{T}.Status"/> property.
/// </item>
/// </list>
/// </para>
/// </remarks>
/// <param name="iterator">The <see cref="IIterator{T}"/> that will be used to monitor the iterative process.</param>
public MlkBiCgStab(IIterator<Complex> iterator) : this(null, iterator)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="MlkBiCgStab"/> class.
/// </summary>
/// <remarks>
/// When using this constructor the solver will use the <see cref="IIterator{T}"/> with
/// the standard settings.
/// </remarks>
/// <param name="preconditioner">The <see cref="IPreConditioner{T}"/> that will be used to precondition the matrix equation.</param>
public MlkBiCgStab(IPreConditioner<Complex> preconditioner) : this(preconditioner, null)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="MlkBiCgStab"/> class.
/// </summary>
/// <remarks>
/// <para>
/// The main advantages of using a user defined <see cref="IIterator{T}"/> 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{T}.Status"/> property.
/// </item>
/// </list>
/// </para>
/// </remarks>
/// <param name="preconditioner">The <see cref="IPreConditioner{T}"/> that will be used to precondition the matrix equation.</param>
/// <param name="iterator">The <see cref="IIterator{T}"/> that will be used to monitor the iterative process.</param>
public MlkBiCgStab(IPreConditioner<Complex> preconditioner, IIterator<Complex> iterator)
{
_iterator = iterator;
_preconditioner = preconditioner;
}
/// <summary>
/// Gets or sets the number of starting vectors.
/// </summary>
/// <remarks>
/// Must be larger than 1 and smaller than the number of variables in the matrix that
/// for which this solver will be used.
/// </remarks>
public int NumberOfStartingVectors
{
[DebuggerStepThrough]
get
{
return _numberOfStartingVectors;
}
[DebuggerStepThrough]
set
{
if (value <= 1)
{
throw new ArgumentOutOfRangeException("value");
}
_numberOfStartingVectors = value;
}
}
/// <summary>
/// Resets the number of starting vectors to the default value.
/// </summary>
public void ResetNumberOfStartingVectors()
{
_numberOfStartingVectors = DefaultNumberOfStartingVectors;
}
/// <summary>
/// Sets the <see cref="IPreConditioner{T}"/> that will be used to precondition the iterative process.
/// </summary>
/// <param name="preconditioner">The preconditioner.</param>
public void SetPreconditioner(IPreConditioner<Complex> preconditioner)
{
_preconditioner = preconditioner;
}
/// <summary>
/// Sets the <see cref="IIterator{T}"/> that will be used to track the iterative process.
/// </summary>
/// <param name="iterator">The iterator.</param>
public void SetIterator(IIterator<Complex> iterator)
{
_iterator = iterator;
}
/// <summary>
/// Gets or sets a series of orthonormal vectors which will be used as basis for the
/// Krylov sub-space.
/// </summary>
public IList<Vector<Complex>> StartingVectors
{
[DebuggerStepThrough]
get
{
return _startingVectors;
}
[DebuggerStepThrough]
set
{
if ((value == null) || (value.Count == 0))
{
_startingVectors = null;
}
else
{
_startingVectors = value;
}
}
}
/// <summary>
/// Gets the status of the iteration once the calculation is finished.
/// </summary>
public ICalculationStatus IterationResult
{
[DebuggerStepThrough]
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 matrix, <c>A</c>.</param>
/// <param name="vector">The solution vector, <c>b</c>.</param>
/// <returns>The result vector, <c>x</c>.</returns>
public Vector<Complex> Solve(Matrix<Complex> matrix, Vector<Complex> vector)
{
if (vector == null)
{
throw new ArgumentNullException();
}
Vector<Complex> 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 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<Complex> matrix, Vector<Complex> input, Vector<Complex> result)
{
// If we were stopped before, we are no longer
// We're doing this at the start of the method to ensure
// that we can use these fields immediately.
_hasBeenStopped = false;
// 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);
}
// 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<Complex> 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<Complex> residuals = new DenseVector(matrix.RowCount);
CalculateTrueResidual(matrix, residuals, xtemp, input);
// Define the temporary values
var c = new Complex[k];
// Define the temporary vectors
Vector<Complex> gtemp = new DenseVector(residuals.Count);
Vector<Complex> u = new DenseVector(residuals.Count);
Vector<Complex> utemp = new DenseVector(residuals.Count);
Vector<Complex> temp = new DenseVector(residuals.Count);
Vector<Complex> temp1 = new DenseVector(residuals.Count);
Vector<Complex> zd = new DenseVector(residuals.Count);
Vector<Complex> zg = new DenseVector(residuals.Count);
Vector<Complex> 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)
residuals.Add(w[k - 1].Multiply(-alpha), 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;
// x_(jk+1) = x_((j-1)k_k) - rho_(j+1) u~_(jk+1) + alpha_(jk+1) g~_((j-1)k+k)
xtemp.Add(utemp.Multiply(-rho), xtemp);
gtemp.Multiply(alpha, gtemp);
xtemp.Add(gtemp, xtemp);
// 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, residuals);
// 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)
zd.Add(d[s].Multiply(beta), zd);
// z_g = z_g + beta^(jk+i)_((j-1)k+s) g_((j-1)k+s)
zg.Add(g[s].Multiply(beta), zg);
// z_w = z_w + beta^(jk+i)_((j-1)k+s) w_((j-1)k+s)
zw.Add(w[s].Multiply(beta), 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))
residuals.Add(zw.Multiply(rho), temp);
beta = -_startingVectors[0].DotProduct(temp) / beta;
// z_g = z_g + beta^(jk+i)_((j-1)k+k) g_((j-1)k+k)
zg.Add(g[k - 1].Multiply(beta), zg);
// z_w = rho_(j+1) (z_w + beta^(jk+i)_((j-1)k+k) w_((j-1)k+k))
zw.Add(w[k - 1].Multiply(beta), 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)
zd.Add(d[s].Multiply(beta), zd);
// z_g = z_g + beta^(jk+i)_(jk+s) * g_(jk+s)
zg.Add(g[s].Multiply(beta), 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)
u.Add(d[i].Multiply(-alpha), 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)
xtemp.Add(gtemp.Multiply(rho * alpha), 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)
residuals.Add(w[i].Multiply(-rho * alpha), 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>
/// Gets the number of starting vectors to create
/// </summary>
/// <param name="maximumNumberOfStartingVectors">Maximum number</param>
/// <param name="numberOfVariables">Number of variables</param>
/// <returns>Number of starting vectors to create</returns>
private static int NumberOfStartingVectorsToCreate(int maximumNumberOfStartingVectors, int numberOfVariables)
{
// Create no more starting vectors than the size of the problem - 1
return Math.Min(maximumNumberOfStartingVectors, (numberOfVariables - 1));
}
/// <summary>
/// Returns an array of starting vectors.
/// </summary>
/// <param name="maximumNumberOfStartingVectors">The maximum number of starting vectors that should be created.</param>
/// <param name="numberOfVariables">The number of variables.</param>
/// <returns>
/// An array with starting vectors. The array will never be larger than the
/// <paramref name="maximumNumberOfStartingVectors"/> but it may be smaller if
/// the <paramref name="numberOfVariables"/> is smaller than
/// the <paramref name="maximumNumberOfStartingVectors"/>.
/// </returns>
private static IList<Vector<Complex>> CreateStartingVectors(int maximumNumberOfStartingVectors, int numberOfVariables)
{
// Create no more starting vectors than the size of the problem - 1
// Get random values and then orthogonalize them with
// modified Gramm - Schmidt
var count = NumberOfStartingVectorsToCreate(maximumNumberOfStartingVectors, numberOfVariables);
// Get a random set of samples based on the standard normal distribution with
// mean = 0 and sd = 1
var distribution = new Normal();
Matrix<Complex> matrix = new DenseMatrix(numberOfVariables, count);
for (var i = 0; i < matrix.ColumnCount; i++)
{
var samples = new Complex[matrix.RowCount];
var samplesRe = distribution.Samples().Take(matrix.RowCount).ToArray();
var samplesIm = distribution.Samples().Take(matrix.RowCount).ToArray();
for (int j = 0; j < matrix.RowCount; j++)
{
samples[j] = new Complex(samplesRe[j], samplesIm[j]);
}
// Set the column
matrix.SetColumn(i, samples);
}
// Compute the orthogonalization.
var gs = new GramSchmidt(matrix);
var orthogonalMatrix = gs.Q;
// Now transfer this to vectors
var result = new List<Vector<Complex>>();
for (var i = 0; i < orthogonalMatrix.ColumnCount; i++)
{
result.Add(orthogonalMatrix.Column(i));
// Normalize the result vector
result[i].Multiply(1 / result[i].Norm(2), result[i]);
}
return result;
}
/// <summary>
/// Create random vecrors array
/// </summary>
/// <param name="arraySize">Number of vectors</param>
/// <param name="vectorSize">Size of each vector</param>
/// <returns>Array of random vectors</returns>
private static Vector<Complex>[] CreateVectorArray(int arraySize, int vectorSize)
{
var result = new Vector<Complex>[arraySize];
for (var i = 0; i < result.Length; i++)
{
result[i] = new DenseVector(vectorSize);
}
return result;
}
/// <summary>
/// Calculates the true residual of the matrix equation Ax = b according to: residual = b - Ax
/// </summary>
/// <param name="matrix">Source <see cref="Matrix{T}"/>A.</param>
/// <param name="residual">Residual <see cref="Vector{T}"/> data.</param>
/// <param name="x">x <see cref="Vector{T}"/> data.</param>
/// <param name="b">b <see cref="Vector{T}"/> data.</param>
private static void CalculateTrueResidual(Matrix<Complex> matrix, Vector<Complex> residual, Vector<Complex> x, Vector<Complex> b)
{
// -Ax = residual
matrix.Multiply(x, residual);
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{T}"/>.</param>
/// <param name="source">Source <see cref="Vector{T}"/>.</param>
/// <param name="residuals">Residual <see cref="Vector{T}"/>.</param>
/// <returns><c>true</c> if continue, otherwise <c>false</c></returns>
private bool ShouldContinue(int iterationNumber, Vector<Complex> result, Vector<Complex> source, Vector<Complex> 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 matrix, <c>A</c>.</param>
/// <param name="input">The solution matrix, <c>B</c>.</param>
/// <returns>The result matrix, <c>X</c>.</returns>
public Matrix<Complex> Solve(Matrix<Complex> matrix, Matrix<Complex> input)
{
if (matrix == null)
{
throw new ArgumentNullException("matrix");
}
if (input == null)
{
throw new ArgumentNullException("input");
}
var result = 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 matrix, <c>A</c>.</param>
/// <param name="input">The solution matrix, <c>B</c>.</param>
/// <param name="result">The result matrix, <c>X</c></param>
public void Solve(Matrix<Complex> matrix, Matrix<Complex> input, Matrix<Complex> 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 new ArgumentException(Resources.ArgumentMatrixDimensions);
}
for (var column = 0; column < input.ColumnCount; column++)
{
var solution = Solve(matrix, input.Column(column));
foreach (var element in solution.GetIndexedEnumerator())
{
result.At(element.Key, column, element.Value);
}
}
}
}
}

528
src/Numerics/LinearAlgebra/Complex/Solvers/Iterative/TFQMR.cs

@ -0,0 +1,528 @@
// <copyright file="TFQMR.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>
namespace MathNet.Numerics.LinearAlgebra.Complex.Solvers.Iterative
{
using System;
using System.Numerics;
using Generic;
using Generic.Solvers;
using Generic.Solvers.Preconditioners;
using Generic.Solvers.Status;
using Preconditioners;
using Properties;
/// <summary>
/// A Transpose Free Quasi-Minimal Residual (TFQMR) iterative matrix solver.
/// </summary>
/// <remarks>
/// <para>
/// The TFQMR algorithm was taken from: <br/>
/// Iterative methods for sparse linear systems.
/// <br/>
/// Yousef Saad
/// <br/>
/// Algorithm is described in Chapter 7, section 7.4.3, page 219
/// </para>
/// <para>
/// The example code below provides an indication of the possible use of the
/// solver.
/// </para>
/// </remarks>
public sealed class TFQMR : IIterativeSolver<Complex>
{
/// <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<Complex> _preconditioner;
/// <summary>
/// The iterative process controller.
/// </summary>
private IIterator<Complex> _iterator;
/// <summary>
/// Indicates if the user has stopped the solver.
/// </summary>
private bool _hasBeenStopped;
/// <summary>
/// Initializes a new instance of the <see cref="TFQMR"/> class.
/// </summary>
/// <remarks>
/// When using this constructor the solver will use the <see cref="IIterator{T}"/> with
/// the standard settings and a default preconditioner.
/// </remarks>
public TFQMR() : this(null, null)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="TFQMR"/> 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{T}"/> 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{T}.Status"/> property.
/// </item>
/// </list>
/// </para>
/// </remarks>
/// <param name="iterator">The <see cref="IIterator{T}"/> that will be used to monitor the iterative process.</param>
public TFQMR(IIterator<Complex> iterator) : this(null, iterator)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="TFQMR"/> class.
/// </summary>
/// <remarks>
/// When using this constructor the solver will use the <see cref="IIterator{T}"/> with
/// the standard settings.
/// </remarks>
/// <param name="preconditioner">The <see cref="IPreConditioner{T}"/> that will be used to precondition the matrix equation.</param>
public TFQMR(IPreConditioner<Complex> preconditioner) : this(preconditioner, null)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="TFQMR"/> class.
/// </summary>
/// <remarks>
/// <para>
/// The main advantages of using a user defined <see cref="IIterator{T}"/> 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{T}.Status"/> property.
/// </item>
/// </list>
/// </para>
/// </remarks>
/// <param name="preconditioner">The <see cref="IPreConditioner{T}"/> that will be used to precondition the matrix equation.</param>
/// <param name="iterator">The <see cref="IIterator{T}"/> that will be used to monitor the iterative process.</param>
public TFQMR(IPreConditioner<Complex> preconditioner, IIterator<Complex> iterator)
{
_iterator = iterator;
_preconditioner = preconditioner;
}
/// <summary>
/// Sets the <see cref="IPreConditioner{T}"/> that will be used to precondition the iterative process.
/// </summary>
/// <param name="preconditioner">The preconditioner.</param>
public void SetPreconditioner(IPreConditioner<Complex> preconditioner)
{
_preconditioner = preconditioner;
}
/// <summary>
/// Sets the <see cref="IIterator{T}"/> that will be used to track the iterative process.
/// </summary>
/// <param name="iterator">The iterator.</param>
public void SetIterator(IIterator<Complex> 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 matrix, <c>A</c>.</param>
/// <param name="vector">The solution vector, <c>b</c>.</param>
/// <returns>The result vector, <c>x</c>.</returns>
public Vector<Complex> Solve(Matrix<Complex> matrix, Vector<Complex> vector)
{
if (vector == null)
{
throw new ArgumentNullException();
}
Vector<Complex> 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 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<Complex> matrix, Vector<Complex> input, Vector<Complex> result)
{
// If we were stopped before, we are no longer
// We're doing this at the start of the method to ensure
// that we can use these fields immediately.
_hasBeenStopped = false;
// 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);
}
// 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);
// Initialize
var startNorm = input.Norm(2);
// Define the scalars
Complex alpha = 0;
Complex eta = 0;
double theta = 0;
var tau = startNorm;
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
yeven.Add(v.Multiply(-alpha), 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
pseudoResiduals.Add(uinternal.Multiply(-alpha), 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
x.Add(d.Multiply(eta), 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
pseudoResiduals.Add(yodd.Multiply(beta), yeven);
// Solve M temp = yOdd
_preconditioner.Approximate(yeven, temp);
// uOdd = A temp
matrix.Multiply(temp, ueven);
// v = uEven + beta * (uOdd + beta * v)
uodd.Add(v.Multiply(beta), temp);
ueven.Add(temp.Multiply(beta), v);
}
// Calculate the real values
_preconditioner.Approximate(x, result);
iterationNumber++;
}
}
/// <summary>
/// Calculates the true residual of the matrix equation Ax = b according to: residual = b - Ax
/// </summary>
/// <param name="matrix">Instance of the <see cref="Matrix{T}"/> A.</param>
/// <param name="residual">Residual values in <see cref="Vector{T}"/>.</param>
/// <param name="x">Instance of the <see cref="Vector{T}"/> x.</param>
/// <param name="b">Instance of the <see cref="Vector{T}"/> b.</param>
private static void CalculateTrueResidual(Matrix<Complex> matrix, Vector<Complex> residual, Vector<Complex> x, Vector<Complex> b)
{
// -Ax = residual
matrix.Multiply(x, residual);
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{T}"/>.</param>
/// <param name="source">Source <see cref="Vector{T}"/>.</param>
/// <param name="residuals">Residual <see cref="Vector{T}"/>.</param>
/// <returns><c>true</c> if continue, otherwise <c>false</c></returns>
private bool ShouldContinue(int iterationNumber, Vector<Complex> result, Vector<Complex> source, Vector<Complex> 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>
/// Is <paramref name="number"/> even?
/// </summary>
/// <param name="number">Number to check</param>
/// <returns><c>true</c> if <paramref name="number"/> even, otherwise <c>false</c></returns>
private static bool IsEven(int number)
{
return number % 2 == 0;
}
/// <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 matrix, <c>A</c>.</param>
/// <param name="input">The solution matrix, <c>B</c>.</param>
/// <returns>The result matrix, <c>X</c>.</returns>
public Matrix<Complex> Solve(Matrix<Complex> matrix, Matrix<Complex> input)
{
if (matrix == null)
{
throw new ArgumentNullException("matrix");
}
if (input == null)
{
throw new ArgumentNullException("input");
}
var result = 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 matrix, <c>A</c>.</param>
/// <param name="input">The solution matrix, <c>B</c>.</param>
/// <param name="result">The result matrix, <c>X</c></param>
public void Solve(Matrix<Complex> matrix, Matrix<Complex> input, Matrix<Complex> 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 new ArgumentException(Resources.ArgumentMatrixDimensions);
}
for (var column = 0; column < input.ColumnCount; column++)
{
var solution = Solve(matrix, input.Column(column));
foreach (var element in solution.GetIndexedEnumerator())
{
result.At(element.Key, column, element.Value);
}
}
}
}
}

329
src/Numerics/LinearAlgebra/Complex/Solvers/Iterator.cs

@ -0,0 +1,329 @@
// <copyright file="Iterator.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>
namespace MathNet.Numerics.LinearAlgebra.Complex.Solvers
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using Generic;
using Generic.Solvers;
using Generic.Solvers.Status;
using Generic.Solvers.StopCriterium;
using Properties;
using StopCriterium;
/// <summary>
/// An iterator that is used to check if an iterative calculation should continue or stop.
/// </summary>
public sealed class Iterator : IIterator<Complex>
{
/// <summary>
/// The default status for the iterator.
/// </summary>
private static readonly ICalculationStatus DefaultStatus = new CalculationIndetermined();
/// <summary>
/// Creates a default iterator with all the <see cref="IIterationStopCriterium{T}"/> objects.
/// </summary>
/// <returns>A new <see cref="IIterator{T}"/> object.</returns>
public static IIterator<Complex> CreateDefault()
{
var iterator = new Iterator();
iterator.Add(new FailureStopCriterium());
iterator.Add(new DivergenceStopCriterium());
iterator.Add(new IterationCountStopCriterium());
iterator.Add(new ResidualStopCriterium());
return iterator;
}
/// <summary>
/// The collection that holds all the stop criteria and the flag indicating if they should be added
/// to the child iterators.
/// </summary>
private readonly Dictionary<Type, IIterationStopCriterium<Complex>> _stopCriterias = new Dictionary<Type, IIterationStopCriterium<Complex>>();
/// <summary>
/// The status of the iterator.
/// </summary>
private ICalculationStatus _status = DefaultStatus;
/// <summary>
/// Indicates if the iteration was cancelled.
/// </summary>
private bool _wasIterationCancelled;
/// <summary>
/// Initializes a new instance of the <see cref="Iterator"/> class.
/// </summary>
public Iterator() : this(null)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Iterator"/> class with the specified stop criteria.
/// </summary>
/// <param name="stopCriteria">
/// The specified stop criteria. Only one stop criterium of each type can be passed in. None
/// of the stop criteria will be passed on to child iterators.
/// </param>
/// <exception cref="ArgumentException">Thrown if <paramref name="stopCriteria"/> contains multiple stop criteria of the same type.</exception>
public Iterator(IEnumerable<IIterationStopCriterium<Complex>> stopCriteria)
{
// Add the stop criteria
if (stopCriteria == null)
{
return;
}
foreach (var stopCriterium in stopCriteria.Where(stopCriterium => stopCriterium != null))
{
Add(stopCriterium);
}
}
/// <summary>
/// Adds an <see cref="IIterationStopCriterium{T}"/> to the internal collection of stop-criteria. Only a
/// single stop criterium of each type can be stored.
/// </summary>
/// <param name="stopCriterium">The stop criterium to add.</param>
/// <exception cref="ArgumentNullException">Thrown if <paramref name="stopCriterium"/> is <see langword="null" />.</exception>
/// <exception cref="ArgumentException">
/// Thrown if <paramref name="stopCriterium"/> is of the same type as an already
/// stored criterium.
/// </exception>
public void Add(IIterationStopCriterium<Complex> stopCriterium)
{
if (stopCriterium == null)
{
throw new ArgumentNullException("stopCriterium");
}
if (_stopCriterias.ContainsKey(stopCriterium.GetType()))
{
throw new ArgumentException(Resources.StopCriteriumDuplicate);
}
// Store the stop criterium.
_stopCriterias.Add(stopCriterium.GetType(), stopCriterium);
}
/// <summary>
/// Removes the <see cref="IIterationStopCriterium{T}"/> from the internal collection.
/// </summary>
/// <param name="stopCriterium">The stop criterium that must be removed.</param>
public void Remove(IIterationStopCriterium<Complex> stopCriterium)
{
if (stopCriterium == null)
{
throw new ArgumentNullException("stopCriterium");
}
if (!_stopCriterias.ContainsKey(stopCriterium.GetType()))
{
return;
}
// Remove from the collection
_stopCriterias.Remove(stopCriterium.GetType());
}
/// <summary>
/// Indicates if the specific stop criterium is stored by the <see cref="IIterator{T}"/>.
/// </summary>
/// <param name="stopCriterium">The stop criterium.</param>
/// <returns><c>true</c> if the <see cref="IIterator{T}"/> contains the stop criterium; otherwise <c>false</c>.</returns>
public bool Contains(IIterationStopCriterium<Complex> stopCriterium)
{
return stopCriterium != null && _stopCriterias.ContainsKey(stopCriterium.GetType());
}
/// <summary>
/// Gets the number of stored stop criteria.
/// </summary>
/// <remarks>Used for testing only.</remarks>
internal int NumberOfCriteria
{
get
{
return _stopCriterias.Count;
}
}
/// <summary>
/// Gets an <c>IEnumerator</c> that enumerates over all the stored stop criteria.
/// </summary>
/// <remarks>Used for testing only.</remarks>
internal IEnumerator<IIterationStopCriterium<Complex>> StoredStopCriteria
{
get
{
return _stopCriterias.Select(criterium => criterium.Value).GetEnumerator();
}
}
/// <summary>
/// Indicates to the iterator that the iterative process has been cancelled.
/// </summary>
/// <remarks>
/// Does not reset the stop-criteria.
/// </remarks>
public void IterationCancelled()
{
_wasIterationCancelled = true;
_status = new CalculationCancelled();
}
/// <summary>
/// Determines the status of the iterative calculation based on the stop criteria stored
/// by the current <c>IIterator</c>. Result is set into <c>Status</c> field.
/// </summary>
/// <param name="iterationNumber">The number of iterations that have passed so far.</param>
/// <param name="solutionVector">The vector containing the current solution values.</param>
/// <param name="sourceVector">The right hand side vector.</param>
/// <param name="residualVector">The vector containing the current residual vectors.</param>
/// <remarks>
/// The individual iterators may internally track the progress of the calculation based
/// on the invocation of this method. Therefore this method should only be called if the
/// calculation has moved forwards at least one step.
/// </remarks>
public void DetermineStatus(int iterationNumber, Vector<Complex> solutionVector, Vector<Complex> sourceVector, Vector<Complex> residualVector)
{
if (_stopCriterias.Count == 0)
{
throw new ArgumentException(Resources.StopCriteriumMissing);
}
if (iterationNumber < 0)
{
throw new ArgumentOutOfRangeException("iterationNumber");
}
if (solutionVector == null)
{
throw new ArgumentNullException("solutionVector");
}
if (sourceVector == null)
{
throw new ArgumentNullException("sourceVector");
}
if (residualVector == null)
{
throw new ArgumentNullException("residualVector");
}
// While we're cancelled we don't call on the stop-criteria.
if (_wasIterationCancelled)
{
return;
}
foreach (var stopCriterium in _stopCriterias.Select(pair => pair.Value))
{
stopCriterium.DetermineStatus(iterationNumber, solutionVector, sourceVector, residualVector);
var status = stopCriterium.Status;
// Check if the status is:
// - Running --> keep going
// - Indetermined --> keep going
// Anything else:
// Stop looping and set that status
if ((status is CalculationRunning) || (status is CalculationIndetermined))
{
continue;
}
_status = status;
return;
}
// Got all the way through
// So we're running because we had vectors passed to us.
if (!(_status is CalculationRunning))
{
_status = new CalculationRunning();
}
}
/// <summary>
/// Gets the current calculation status.
/// </summary>
public ICalculationStatus Status
{
get
{
return _status;
}
}
/// <summary>
/// Resets the <see cref="IIterator{T}"/> to the pre-calculation state.
/// </summary>
public void ResetToPrecalculationState()
{
// Indicate that we're no longer cancelled.
_wasIterationCancelled = false;
// Reset the status.
_status = DefaultStatus;
// Reset the stop-criteria
foreach (var stopCriterium in _stopCriterias.Select(pair => pair.Value))
{
stopCriterium.ResetToPrecalculationState();
}
}
/// <summary>
/// Creates a deep clone of the current iterator.
/// </summary>
/// <returns>The deep clone of the current iterator.</returns>
public IIterator<Complex> Clone()
{
var stopCriteria = _stopCriterias.Select(pair => pair.Value).Select(stopCriterium => (IIterationStopCriterium<Complex>)stopCriterium.Clone()).ToList();
return new Iterator(stopCriteria);
}
#if !SILVERLIGHT
/// <summary>
/// Creates a deep clone of the current iterator.
/// </summary>
/// <returns>The deep clone of the current iterator.</returns>
object ICloneable.Clone()
{
return Clone();
}
#endif
}
}

151
src/Numerics/LinearAlgebra/Complex/Solvers/Preconditioners/Diagonal.cs

@ -0,0 +1,151 @@
// <copyright file="Diagonal.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>
namespace MathNet.Numerics.LinearAlgebra.Complex.Solvers.Preconditioners
{
using System;
using System.Numerics;
using Generic;
using Generic.Solvers.Preconditioners;
using Properties;
/// <summary>
/// A diagonal preconditioner. The preconditioner uses the inverse
/// of the matrix diagonal as preconditioning values.
/// </summary>
public sealed class Diagonal : IPreConditioner<Complex>
{
/// <summary>
/// The inverse of the matrix diagonal.
/// </summary>
private Complex[] _inverseDiagonals;
/// <summary>
/// Returns the decomposed matrix diagonal.
/// </summary>
/// <returns>The matrix diagonal.</returns>
internal DiagonalMatrix DiagonalEntries()
{
var result = new DiagonalMatrix(_inverseDiagonals.Length);
for (var i = 0; i < _inverseDiagonals.Length; i++)
{
result[i, i] = 1 / _inverseDiagonals[i];
}
return result;
}
/// <summary>
/// Initializes the preconditioner and loads the internal data structures.
/// </summary>
/// <param name="matrix">
/// The <see cref="Matrix{T}"/> upon which this preconditioner is based.</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<Complex> matrix)
{
if (matrix == null)
{
throw new ArgumentNullException("matrix");
}
if (matrix.RowCount != matrix.ColumnCount)
{
throw new ArgumentException(Resources.ArgumentMatrixSquare, "matrix");
}
_inverseDiagonals = new Complex[matrix.RowCount];
for (var i = 0; i < matrix.RowCount; i++)
{
_inverseDiagonals[i] = 1 / matrix[i, i];
}
}
/// <summary>
/// Approximates the solution to the matrix equation <b>Ax = b</b>.
/// </summary>
/// <param name="rhs">The right hand side vector.</param>
/// <returns>The left hand side vector.</returns>
public Vector<Complex> Approximate(Vector<Complex> rhs)
{
if (rhs == null)
{
throw new ArgumentNullException("rhs");
}
if (_inverseDiagonals == null)
{
throw new ArgumentException(Resources.ArgumentMatrixDoesNotExist);
}
if (rhs.Count != _inverseDiagonals.Length)
{
throw new ArgumentException(Resources.ArgumentVectorsSameLength, "rhs");
}
Vector<Complex> result = new DenseVector(rhs.Count);
Approximate(rhs, result);
return result;
}
/// <summary>
/// Approximates the solution to the matrix equation <b>Ax = b</b>.
/// </summary>
/// <param name="rhs">The right hand side vector.</param>
/// <param name="lhs">The left hand side vector. Also known as the result vector.</param>
public void Approximate(Vector<Complex> rhs, Vector<Complex> lhs)
{
if (rhs == null)
{
throw new ArgumentNullException("rhs");
}
if (lhs == null)
{
throw new ArgumentNullException("lhs");
}
if (_inverseDiagonals == null)
{
throw new ArgumentException(Resources.ArgumentMatrixDoesNotExist);
}
if ((lhs.Count != rhs.Count) || (lhs.Count != _inverseDiagonals.Length))
{
throw new ArgumentException(Resources.ArgumentVectorsSameLength, "rhs");
}
for (var i = 0; i < _inverseDiagonals.Length; i++)
{
lhs[i] = rhs[i] * _inverseDiagonals[i];
}
}
}
}

730
src/Numerics/LinearAlgebra/Complex/Solvers/Preconditioners/Ilutp.cs

@ -0,0 +1,730 @@
// <copyright file="Ilutp.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>
namespace MathNet.Numerics.LinearAlgebra.Complex.Solvers.Preconditioners
{
using System;
using System.Collections.Generic;
using System.Numerics;
using Generic;
using Generic.Solvers.Preconditioners;
using Properties;
/// <summary>
/// This class performs an Incomplete LU factorization with drop tolerance
/// and partial pivoting. The drop tolerance indicates which additional entries
/// will be dropped from the factorized LU matrices.
/// </summary>
/// <remarks>
/// The ILUTP-Mem algorithm was taken from: <br/>
/// ILUTP_Mem: a Space-Efficient Incomplete LU Preconditioner
/// <br/>
/// Tzu-Yi Chen, Department of Mathematics and Computer Science, <br/>
/// Pomona College, Claremont CA 91711, USA <br/>
/// Published in: <br/>
/// Lecture Notes in Computer Science <br/>
/// Volume 3046 / 2004 <br/>
/// pp. 20 - 28 <br/>
/// Algorithm is described in Section 2, page 22
/// </remarks>
public sealed class Ilutp : IPreConditioner<Complex>
{
/// <summary>
/// The default fill level.
/// </summary>
public const double DefaultFillLevel = 200.0;
/// <summary>
/// The default drop tolerance.
/// </summary>
public const double DefaultDropTolerance = 0.0001;
/// <summary>
/// The decomposed upper triangular matrix.
/// </summary>
private SparseMatrix _upper;
/// <summary>
/// The decomposed lower triangular matrix.
/// </summary>
private SparseMatrix _lower;
/// <summary>
/// The array containing the pivot values.
/// </summary>
private int[] _pivots;
/// <summary>
/// The fill level.
/// </summary>
private double _fillLevel = DefaultFillLevel;
/// <summary>
/// The drop tolerance.
/// </summary>
private double _dropTolerance = DefaultDropTolerance;
/// <summary>
/// The pivot tolerance.
/// </summary>
private double _pivotTolerance;
/// <summary>
/// Initializes a new instance of the <see cref="Ilutp"/> class with the default settings.
/// </summary>
public Ilutp()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Ilutp"/> class with the specified settings.
/// </summary>
/// <param name="fillLevel">
/// The amount of fill that is allowed in the matrix. The value is a fraction of
/// the number of non-zero entries in the original matrix. Values should be positive.
/// </param>
/// <param name="dropTolerance">
/// The absolute drop tolerance which indicates below what absolute value an entry
/// will be dropped from the matrix. A drop tolerance of 0.0 means that no values
/// will be dropped. Values should always be positive.
/// </param>
/// <param name="pivotTolerance">
/// The pivot tolerance which indicates at what level pivoting will take place. A
/// value of 0.0 means that no pivoting will take place.
/// </param>
public Ilutp(double fillLevel, double dropTolerance, double pivotTolerance)
{
if (fillLevel < 0)
{
throw new ArgumentOutOfRangeException("fillLevel");
}
if (dropTolerance < 0)
{
throw new ArgumentOutOfRangeException("dropTolerance");
}
if (pivotTolerance < 0)
{
throw new ArgumentOutOfRangeException("pivotTolerance");
}
_fillLevel = fillLevel;
_dropTolerance = dropTolerance;
_pivotTolerance = pivotTolerance;
}
/// <summary>
/// Gets or sets the amount of fill that is allowed in the matrix. The
/// value is a fraction of the number of non-zero entries in the original
/// matrix. The standard value is 200.
/// </summary>
/// <remarks>
/// <para>
/// Values should always be positive and can be higher than 1.0. A value lower
/// than 1.0 means that the eventual preconditioner matrix will have fewer
/// non-zero entries as the original matrix. A value higher than 1.0 means that
/// the eventual preconditioner can have more non-zero values than the original
/// matrix.
/// </para>
/// <para>
/// Note that any changes to the <b>FillLevel</b> after creating the preconditioner
/// will invalidate the created preconditioner and will require a re-initialization of
/// the preconditioner.
/// </para>
/// </remarks>
/// <exception cref="ArgumentOutOfRangeException">Thrown if a negative value is provided.</exception>
public double FillLevel
{
get
{
return _fillLevel;
}
set
{
if (value < 0)
{
throw new ArgumentOutOfRangeException("Value");
}
_fillLevel = value;
}
}
/// <summary>
/// Gets or sets the absolute drop tolerance which indicates below what absolute value
/// an entry will be dropped from the matrix. The standard value is 0.0001.
/// </summary>
/// <remarks>
/// <para>
/// The values should always be positive and can be larger than 1.0. A low value will
/// keep more small numbers in the preconditioner matrix. A high value will remove
/// more small numbers from the preconditioner matrix.
/// </para>
/// <para>
/// Note that any changes to the <b>DropTolerance</b> after creating the preconditioner
/// will invalidate the created preconditioner and will require a re-initialization of
/// the preconditioner.
/// </para>
/// </remarks>
/// <exception cref="ArgumentOutOfRangeException">Thrown if a negative value is provided.</exception>
public double DropTolerance
{
get
{
return _dropTolerance;
}
set
{
if (value < 0)
{
throw new ArgumentOutOfRangeException("Value");
}
_dropTolerance = value;
}
}
/// <summary>
/// Gets or sets the pivot tolerance which indicates at what level pivoting will
/// take place. The standard value is 0.0 which means pivoting will never take place.
/// </summary>
/// <remarks>
/// <para>
/// The pivot tolerance is used to calculate if pivoting is necessary. Pivoting
/// will take place if any of the values in a row is bigger than the
/// diagonal value of that row divided by the pivot tolerance, i.e. pivoting
/// will take place if <b>row(i,j) > row(i,i) / PivotTolerance</b> for
/// any <b>j</b> that is not equal to <b>i</b>.
/// </para>
/// <para>
/// Note that any changes to the <b>PivotTolerance</b> after creating the preconditioner
/// will invalidate the created preconditioner and will require a re-initialization of
/// the preconditioner.
/// </para>
/// </remarks>
/// <exception cref="ArgumentOutOfRangeException">Thrown if a negative value is provided.</exception>
public double PivotTolerance
{
get
{
return _pivotTolerance;
}
set
{
if (value < 0)
{
throw new ArgumentOutOfRangeException("Value");
}
_pivotTolerance = value;
}
}
/// <summary>
/// Returns the upper triagonal matrix that was created during the LU decomposition.
/// </summary>
/// <remarks>
/// This method is used for debugging purposes only and should normally not be used.
/// </remarks>
/// <returns>A new matrix containing the upper triagonal elements.</returns>
internal Matrix<Complex> UpperTriangle()
{
return _upper.Clone();
}
/// <summary>
/// Returns the lower triagonal matrix that was created during the LU decomposition.
/// </summary>
/// <remarks>
/// This method is used for debugging purposes only and should normally not be used.
/// </remarks>
/// <returns>A new matrix containing the lower triagonal elements.</returns>
internal Matrix<Complex> LowerTriangle()
{
return _lower.Clone();
}
/// <summary>
/// Returns the pivot array. This array is not needed for normal use because
/// the preconditioner will return the solution vector values in the proper order.
/// </summary>
/// <remarks>
/// This method is used for debugging purposes only and should normally not be used.
/// </remarks>
/// <returns>The pivot array.</returns>
internal int[] Pivots()
{
var result = new int[_pivots.Length];
for (var i = 0; i < _pivots.Length; i++)
{
result[i] = _pivots[i];
}
return result;
}
/// <summary>
/// Initializes the preconditioner and loads the internal data structures.
/// </summary>
/// <param name="matrix">
/// The <see cref="Matrix{T}"/> 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<Complex> 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.ToArray());
// 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<Complex> workVector = new DenseVector(sparseMatrix.RowCount);
Vector<Complex> 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)
{
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>
/// Pivot elements in the <paramref name="row"/> according to internal pivot array
/// </summary>
/// <param name="row">Row <see cref="Vector{T}"/> to pivot in</param>
private void PivotRow(Vector<Complex> row)
{
var knownPivots = new Dictionary<int, int>();
// pivot the row
for (var i = 0; i < row.Count; i++)
{
if ((_pivots[i] != i) && (!PivotMapFound(knownPivots, i)))
{
// store the pivots in the hashtable
knownPivots.Add(_pivots[i], i);
var t = row[i];
row[i] = row[_pivots[i]];
row[_pivots[i]] = t;
}
}
}
/// <summary>
/// Was pivoting already performed
/// </summary>
/// <param name="knownPivots">Pivots already done</param>
/// <param name="currentItem">Current item to pivot</param>
/// <returns><c>true</c> if performed, otherwise <c>false</c></returns>
private bool PivotMapFound(Dictionary<int, int> knownPivots, int currentItem)
{
if (knownPivots.ContainsKey(_pivots[currentItem]))
{
if (knownPivots[_pivots[currentItem]].Equals(currentItem))
{
return true;
}
}
if (knownPivots.ContainsKey(currentItem))
{
if (knownPivots[currentItem].Equals(_pivots[currentItem]))
{
return true;
}
}
return false;
}
/// <summary>
/// Swap columns in the <see cref="Matrix{T}"/>
/// </summary>
/// <param name="matrix">Source <see cref="Matrix{T}"/>.</param>
/// <param name="firstColumn">First column index to swap</param>
/// <param name="secondColumn">Second column index to swap</param>
private static void SwapColumns(Matrix<Complex> matrix, int firstColumn, int secondColumn)
{
for (var i = 0; i < matrix.RowCount; i++)
{
var temp = matrix[i, firstColumn];
matrix[i, firstColumn] = matrix[i, secondColumn];
matrix[i, secondColumn] = temp;
}
}
/// <summary>
/// Sort vector descending, not changing vector but placing sorted indicies to <paramref name="sortedIndices"/>
/// </summary>
/// <param name="lowerBound">Start sort form</param>
/// <param name="upperBound">Sort till upper bound</param>
/// <param name="sortedIndices">Array with sorted vector indicies</param>
/// <param name="values">Source <see cref="Vector{T}"/></param>
private static void FindLargestItems(int lowerBound, int upperBound, int[] sortedIndices, Vector<Complex> values)
{
// Copy the indices for the values into the array
for (var i = 0; i < upperBound + 1 - lowerBound; i++)
{
sortedIndices[i] = lowerBound + i;
}
for (var i = upperBound + 1 - lowerBound; i < sortedIndices.Length; i++)
{
sortedIndices[i] = -1;
}
// Sort the first set of items.
// Sorting starts at index 0 because the index array
// starts at zero
// and ends at index upperBound - lowerBound
IlutpElementSorter.SortDoubleIndicesDecreasing(0, upperBound - lowerBound, sortedIndices, values);
}
/// <summary>
/// Approximates the solution to the matrix equation <b>Ax = b</b>.
/// </summary>
/// <param name="rhs">The right hand side vector.</param>
/// <returns>The left hand side vector.</returns>
public Vector<Complex> Approximate(Vector<Complex> rhs)
{
if (rhs == null)
{
throw new ArgumentNullException("rhs");
}
if (_upper == null)
{
throw new ArgumentException(Resources.ArgumentMatrixDoesNotExist);
}
if (rhs.Count != _upper.ColumnCount)
{
throw new ArgumentException(Resources.ArgumentVectorsSameLength, "rhs");
}
Vector<Complex> result = new DenseVector(rhs.Count);
Approximate(rhs, result);
return result;
}
/// <summary>
/// Approximates the solution to the matrix equation <b>Ax = b</b>.
/// </summary>
/// <param name="rhs">The right hand side vector.</param>
/// <param name="lhs">The left hand side vector. Also known as the result vector.</param>
public void Approximate(Vector<Complex> rhs, Vector<Complex> lhs)
{
if (rhs == null)
{
throw new ArgumentNullException("rhs");
}
if (lhs == null)
{
throw new ArgumentNullException("lhs");
}
if (_upper == null)
{
throw new ArgumentException(Resources.ArgumentMatrixDoesNotExist);
}
if ((lhs.Count != rhs.Count) || (lhs.Count != _upper.RowCount))
{
throw new ArgumentException(Resources.ArgumentVectorsSameLength, "rhs");
}
// Solve equation here
// Pivot(vector, result);
// Solve L*Y = B(piv,:)
Vector<Complex> rowValues = new DenseVector(_lower.RowCount);
for (var i = 0; i < _lower.RowCount; i++)
{
_lower.Row(i, rowValues);
var sum = Complex.Zero;
for (var j = 0; j < i; j++)
{
sum += rowValues[j] * lhs[j];
}
lhs[i] = rhs[i] - sum;
}
// Solve U*X = Y;
for (var i = _upper.RowCount - 1; i > -1; i--)
{
_upper.Row(i, rowValues);
var sum = Complex.Zero;
for (var j = _upper.RowCount - 1; j > i; j--)
{
sum += rowValues[j] * lhs[j];
}
lhs[i] = 1 / rowValues[i] * (lhs[i] - sum);
}
// We have a column pivot so we only need to pivot the
// end result not the incoming right hand side vector
var temp = lhs.Clone();
Pivot(temp, lhs);
}
/// <summary>
/// Pivot elements in <see cref="Vector{T}"/> accoring to internal pivot array
/// </summary>
/// <param name="vector">Source <see cref="Vector{T}"/>.</param>
/// <param name="result">Result <see cref="Vector{T}"/> after pivoting.</param>
private void Pivot(Vector<Complex> vector, Vector<Complex> result)
{
for (var i = 0; i < _pivots.Length; i++)
{
result[i] = vector[_pivots[i]];
}
}
}
}

228
src/Numerics/LinearAlgebra/Complex/Solvers/Preconditioners/IlutpElementSorter.cs

@ -0,0 +1,228 @@
// <copyright file="IlutpElementSorter.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>
namespace MathNet.Numerics.LinearAlgebra.Complex.Solvers.Preconditioners
{
using System.Numerics;
using Generic;
/// <summary>
/// An element sort algorithm for the <see cref="Ilutp"/> class.
/// </summary>
/// <remarks>
/// This sort algorithm is used to sort the columns in a sparse matrix based on
/// the value of the element on the diagonal of the matrix.
/// </remarks>
internal class IlutpElementSorter
{
/// <summary>
/// Sorts the elements of the <paramref name="values"/> vector in decreasing
/// fashion. The vector itself is not affected.
/// </summary>
/// <param name="lowerBound">The starting index.</param>
/// <param name="upperBound">The stopping index.</param>
/// <param name="sortedIndices">An array that will contain the sorted indices once the algorithm finishes.</param>
/// <param name="values">The <see cref="Vector{T}"/> that contains the values that need to be sorted.</param>
public static void SortDoubleIndicesDecreasing(int lowerBound, int upperBound, int[] sortedIndices, Vector<Complex> values)
{
// Move all the indices that we're interested in to the beginning of the
// array. Ignore the rest of the indices.
if (lowerBound > 0)
{
for (var i = 0; i < (upperBound - lowerBound + 1); i++)
{
Exchange(sortedIndices, i, i + lowerBound);
}
upperBound -= lowerBound;
lowerBound = 0;
}
HeapSortDoublesIndices(lowerBound, upperBound, sortedIndices, values);
}
/// <summary>
/// Sorts the elements of the <paramref name="values"/> vector in decreasing
/// fashion using heap sort algorithm. The vector itself is not affected.
/// </summary>
/// <param name="lowerBound">The starting index.</param>
/// <param name="upperBound">The stopping index.</param>
/// <param name="sortedIndices">An array that will contain the sorted indices once the algorithm finishes.</param>
/// <param name="values">The <see cref="Vector{T}"/> that contains the values that need to be sorted.</param>
private static void HeapSortDoublesIndices(int lowerBound, int upperBound, int[] sortedIndices, Vector<Complex> values)
{
var start = ((upperBound - lowerBound + 1) / 2) - 1 + lowerBound;
var end = (upperBound - lowerBound + 1) - 1 + lowerBound;
BuildDoubleIndexHeap(start, upperBound - lowerBound + 1, sortedIndices, values);
while (end >= lowerBound)
{
Exchange(sortedIndices, end, lowerBound);
SiftDoubleIndices(sortedIndices, values, lowerBound, end);
end -= 1;
}
}
/// <summary>
/// Build heap for double indicies
/// </summary>
/// <param name="start">Root position</param>
/// <param name="count">Lenght of <paramref name="values"/></param>
/// <param name="sortedIndices">Indicies of <paramref name="values"/></param>
/// <param name="values">Target <see cref="Vector{T}"/></param>
private static void BuildDoubleIndexHeap(int start, int count, int[] sortedIndices, Vector<Complex> values)
{
while (start >= 0)
{
SiftDoubleIndices(sortedIndices, values, start, count);
start -= 1;
}
}
/// <summary>
/// Sift double indicies
/// </summary>
/// <param name="sortedIndices">Indicies of <paramref name="values"/></param>
/// <param name="values">Target <see cref="Vector{T}"/></param>
/// <param name="begin">Root position</param>
/// <param name="count">Lenght of <paramref name="values"/></param>
private static void SiftDoubleIndices(int[] sortedIndices, Vector<Complex> values, int begin, int count)
{
var root = begin;
int child;
while (root * 2 < count)
{
child = root * 2;
if ((child < count - 1) && (values[sortedIndices[child]].Magnitude > values[sortedIndices[child + 1]].Magnitude))
{
child += 1;
}
if (values[sortedIndices[root]].Magnitude <= values[sortedIndices[child]].Magnitude)
{
return;
}
Exchange(sortedIndices, root, child);
root = child;
}
}
/// <summary>
/// Sorts the given integers in a decreasing fashion.
/// </summary>
/// <param name="values">The values.</param>
public static void SortIntegersDecreasing(int[] values)
{
HeapSortIntegers(values, values.Length);
}
/// <summary>
/// Sort the given integers in a decreasing fashion using heapsort algorithm
/// </summary>
/// <param name="values">Array of values to sort</param>
/// <param name="count">Length of <paramref name="values"/></param>
private static void HeapSortIntegers(int[] values, int count)
{
var start = (count / 2) - 1;
var end = count - 1;
BuildHeap(values, start, count);
while (end >= 0)
{
Exchange(values, end, 0);
Sift(values, 0, end);
end -= 1;
}
}
/// <summary>
/// Build heap
/// </summary>
/// <param name="values">Target values array</param>
/// <param name="start">Root position</param>
/// <param name="count">Lenght of <paramref name="values"/></param>
private static void BuildHeap(int[] values, int start, int count)
{
while (start >= 0)
{
Sift(values, start, count);
start -= 1;
}
}
/// <summary>
/// Sift values
/// </summary>
/// <param name="values">Target value array</param>
/// <param name="start">Root position</param>
/// <param name="count">Lenght of <paramref name="values"/></param>
private static void Sift(int[] values, int start, int count)
{
var root = start;
int child;
while (root * 2 < count)
{
child = root * 2;
if ((child < count - 1) && (values[child] > values[child + 1]))
{
child += 1;
}
if (values[root] > values[child])
{
Exchange(values, root, child);
root = child;
}
else
{
return;
}
}
}
/// <summary>
/// Exchange values in array
/// </summary>
/// <param name="values">Target values array</param>
/// <param name="first">First value to exchanghe</param>
/// <param name="second">Second value to exchanghe</param>
private static void Exchange(int[] values, int first, int second)
{
var t = values[first];
values[first] = values[second];
values[second] = t;
}
}
}

261
src/Numerics/LinearAlgebra/Complex/Solvers/Preconditioners/IncompleteLU.cs

@ -0,0 +1,261 @@
// <copyright file="IncompleteLU.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>
namespace MathNet.Numerics.LinearAlgebra.Complex.Solvers.Preconditioners
{
using System;
using System.Numerics;
using Generic;
using Generic.Solvers.Preconditioners;
using Properties;
/// <summary>
/// An incomplete, level 0, LU factorization preconditioner.
/// </summary>
/// <remarks>
/// The ILU(0) algorithm was taken from: <br/>
/// Iterative methods for sparse linear systems <br/>
/// Yousef Saad <br/>
/// Algorithm is described in Chapter 10, section 10.3.2, page 275 <br/>
/// </remarks>
public sealed class IncompleteLU : IPreConditioner<Complex>
{
/// <summary>
/// The matrix holding the lower (L) and upper (U) matrices. The
/// decomposition matrices are combined to reduce storage.
/// </summary>
private SparseMatrix _decompositionLU;
/// <summary>
/// Returns the upper triagonal matrix that was created during the LU decomposition.
/// </summary>
/// <returns>A new matrix containing the upper triagonal elements.</returns>
internal Matrix<Complex> UpperTriangle()
{
var result = new SparseMatrix(_decompositionLU.RowCount);
for (var i = 0; i < _decompositionLU.RowCount; i++)
{
for (var j = i; j < _decompositionLU.ColumnCount; j++)
{
result[i, j] = _decompositionLU[i, j];
}
}
return result;
}
/// <summary>
/// Returns the lower triagonal matrix that was created during the LU decomposition.
/// </summary>
/// <returns>A new matrix containing the lower triagonal elements.</returns>
internal Matrix<Complex> LowerTriangle()
{
var result = new SparseMatrix(_decompositionLU.RowCount);
for (var i = 0; i < _decompositionLU.RowCount; i++)
{
for (var j = 0; j <= i; j++)
{
if (i == j)
{
result[i, j] = 1.0;
}
else
{
result[i, j] = _decompositionLU[i, j];
}
}
}
return result;
}
/// <summary>
/// Initializes the preconditioner and loads the internal data structures.
/// </summary>
/// <param name="matrix">The matrix upon which the preconditioner is based. </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<Complex> matrix)
{
if (matrix == null)
{
throw new ArgumentNullException("matrix");
}
if (matrix.RowCount != matrix.ColumnCount)
{
throw new ArgumentException(Resources.ArgumentMatrixSquare, "matrix");
}
_decompositionLU = new SparseMatrix(matrix.ToArray());
// M == A
// for i = 2, ... , n do
// for k = 1, .... , i - 1 do
// if (i,k) == NZ(Z) then
// compute z(i,k) = z(i,k) / z(k,k);
// for j = k + 1, ...., n do
// if (i,j) == NZ(Z) then
// compute z(i,j) = z(i,j) - z(i,k) * z(k,j)
// end
// end
// end
// end
// end
for (var i = 0; i < _decompositionLU.RowCount; i++)
{
for (var k = 0; k < i; k++)
{
if (_decompositionLU[i, k] != 0.0)
{
var t = _decompositionLU[i, k] / _decompositionLU[k, k];
_decompositionLU[i, k] = t;
if (_decompositionLU[k, i] != 0.0)
{
_decompositionLU[i, i] = _decompositionLU[i, i] - (t * _decompositionLU[k, i]);
}
for (var j = k + 1; j < _decompositionLU.RowCount; j++)
{
if (j == i)
{
continue;
}
if (_decompositionLU[i, j] != 0.0)
{
_decompositionLU[i, j] = _decompositionLU[i, j] - (t * _decompositionLU[k, j]);
}
}
}
}
}
}
/// <summary>
/// Approximates the solution to the matrix equation <b>Ax = b</b>.
/// </summary>
/// <param name="rhs">The right hand side vector.</param>
/// <returns>The left hand side vector.</returns>
public Vector<Complex> Approximate(Vector<Complex> rhs)
{
if (rhs == null)
{
throw new ArgumentNullException("rhs");
}
if (_decompositionLU == null)
{
throw new ArgumentException(Resources.ArgumentMatrixDoesNotExist);
}
if (rhs.Count != _decompositionLU.ColumnCount)
{
throw new ArgumentException(Resources.ArgumentVectorsSameLength, "rhs");
}
Vector<Complex> result = new DenseVector(rhs.Count);
Approximate(rhs, result);
return result;
}
/// <summary>
/// Approximates the solution to the matrix equation <b>Ax = b</b>.
/// </summary>
/// <param name="rhs">The right hand side vector.</param>
/// <param name="lhs">The left hand side vector. Also known as the result vector.</param>
public void Approximate(Vector<Complex> rhs, Vector<Complex> lhs)
{
if (rhs == null)
{
throw new ArgumentNullException("rhs");
}
if (lhs == null)
{
throw new ArgumentNullException("lhs");
}
if (_decompositionLU == null)
{
throw new ArgumentException(Resources.ArgumentMatrixDoesNotExist);
}
if ((lhs.Count != rhs.Count) || (lhs.Count != _decompositionLU.RowCount))
{
throw new ArgumentException(Resources.ArgumentVectorsSameLength);
}
// Solve:
// Lz = y
// Which gives
// for (int i = 1; i < matrix.RowLength; i++)
// {
// z_i = l_ii^-1 * (y_i - SUM_(j<i) l_ij * z_j)
// }
// NOTE: l_ii should be 1 because u_ii has to be the value
Vector<Complex> rowValues = new DenseVector(_decompositionLU.RowCount);
for (var i = 0; i < _decompositionLU.RowCount; i++)
{
// Clear the rowValues
rowValues.Clear();
_decompositionLU.Row(i, rowValues);
var sum = Complex.Zero;
for (var j = 0; j < i; j++)
{
sum += rowValues[j] * lhs[j];
}
lhs[i] = rhs[i] - sum;
}
// Solve:
// Ux = z
// Which gives
// for (int i = matrix.RowLength - 1; i > -1; i--)
// {
// x_i = u_ii^-1 * (z_i - SUM_(j > i) u_ij * x_j)
// }
for (var i = _decompositionLU.RowCount - 1; i > -1; i--)
{
_decompositionLU.Row(i, rowValues);
var sum = Complex.Zero;
for (var j = _decompositionLU.RowCount - 1; j > i; j--)
{
sum += rowValues[j] * lhs[j];
}
lhs[i] = 1 / rowValues[i] * (lhs[i] - sum);
}
}
}
}

140
src/Numerics/LinearAlgebra/Complex/Solvers/Preconditioners/UnitPreconditioner.cs

@ -0,0 +1,140 @@
// <copyright file="UnitPreconditioner.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>
namespace MathNet.Numerics.LinearAlgebra.Complex.Solvers.Preconditioners
{
using System;
using System.Numerics;
using Generic;
using Generic.Solvers;
using Generic.Solvers.Preconditioners;
using Properties;
/// <summary>
/// A unit preconditioner. This preconditioner does not actually do anything
/// it is only used when running an <see cref="IIterativeSolver{T}"/> without
/// a preconditioner.
/// </summary>
internal sealed class UnitPreconditioner : IPreConditioner<Complex>
{
/// <summary>
/// The coefficient matrix on which this preconditioner operates.
/// Is used to check dimensions on the different vectors that are processed.
/// </summary>
private int _size;
/// <summary>
/// Initializes the preconditioner and loads the internal data structures.
/// </summary>
/// <param name="matrix">
/// The matrix upon which the preconditioner is based.
/// </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<Complex> matrix)
{
if (matrix == null)
{
throw new ArgumentNullException("matrix");
}
if (matrix.RowCount != matrix.ColumnCount)
{
throw new ArgumentException(Resources.ArgumentMatrixSquare, "matrix");
}
_size = matrix.RowCount;
}
/// <summary>
/// Approximates the solution to the matrix equation <b>Ax = b</b>.
/// </summary>
/// <param name="rhs">The right hand side vector.</param>
/// <param name="lhs">The left hand side vector. Also known as the result vector.</param>
/// <exception cref="ArgumentNullException">If <paramref name="rhs"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentNullException">If <paramref name="lhs"/> is <see langword="null"/>. </exception>
/// <exception cref="ArgumentException">
/// <para>
/// If <paramref name="rhs"/> and <paramref name="lhs"/> do not have the same size.
/// </para>
/// <para>
/// - or -
/// </para>
/// <para>
/// If the size of <paramref name="rhs"/> is different the number of rows of the coefficient matrix.
/// </para>
/// </exception>
public void Approximate(Vector<Complex> rhs, Vector<Complex> lhs)
{
if (rhs == null)
{
throw new ArgumentNullException("rhs");
}
if (lhs == null)
{
throw new ArgumentNullException("lhs");
}
if ((lhs.Count != rhs.Count) || (lhs.Count != _size))
{
throw new ArgumentException(Resources.ArgumentVectorsSameLength);
}
rhs.CopyTo(lhs);
}
/// <summary>
/// Approximates the solution to the matrix equation <b>Ax = b</b>.
/// </summary>
/// <param name="rhs">The right hand side vector.</param>
/// <returns>The left hand side vector.</returns>
/// <exception cref="ArgumentNullException">If <paramref name="rhs"/> is <see langword="null"/>. </exception>
/// <exception cref="ArgumentException">
/// If the size of <paramref name="rhs"/> is different the number of rows of the coefficient matrix.
/// </exception>
public Vector<Complex> Approximate(Vector<Complex> rhs)
{
if (rhs == null)
{
throw new ArgumentNullException("rhs");
}
if (rhs.Count != _size)
{
throw new ArgumentException(Resources.ArgumentMatrixDimensions);
}
Vector<Complex> result = new DenseVector(rhs.Count);
Approximate(rhs, result);
return result;
}
}
}

394
src/Numerics/LinearAlgebra/Complex/Solvers/StopCriterium/DivergenceStopCriterium.cs

@ -0,0 +1,394 @@
// <copyright file="DivergenceStopCriterium.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>
namespace MathNet.Numerics.LinearAlgebra.Complex.Solvers.StopCriterium
{
using System;
using System.Diagnostics;
using System.Numerics;
using Generic;
using Generic.Solvers.Status;
using Generic.Solvers.StopCriterium;
/// <summary>
/// Monitors an iterative calculation for signs of divergence.
/// </summary>
public sealed class DivergenceStopCriterium : IIterationStopCriterium<Complex>
{
/// <summary>
/// Default value for the maximum relative increase that the
/// residual may experience before a divergence warning is issued.
/// </summary>
public const double DefaultMaximumRelativeIncrease = 0.08;
/// <summary>
/// Default value for the minimum number of iterations over which
/// the residual must grow before a divergence warning is issued.
/// </summary>
public const int DefaultMinimumNumberOfIterations = 10;
/// <summary>
/// Defines the default last iteration number. Set to -1 because iterations normally
/// start at 0.
/// </summary>
private const int DefaultLastIterationNumber = -1;
/// <summary>
/// The default status.
/// </summary>
private static readonly ICalculationStatus DefaultStatus = new CalculationIndetermined();
/// <summary>
/// The maximum relative increase the residual may experience without triggering a divergence warning.
/// </summary>
private double _maximumRelativeIncrease;
/// <summary>
/// The number of iterations over which a residual increase should be tracked before issuing a divergence warning.
/// </summary>
private int _minimumNumberOfIterations;
/// <summary>
/// The status of the calculation
/// </summary>
private ICalculationStatus _status = DefaultStatus;
/// <summary>
/// The array that holds the tracking information.
/// </summary>
private double[] _residualHistory;
/// <summary>
/// The iteration number of the last iteration.
/// </summary>
private int _lastIteration = DefaultLastIterationNumber;
/// <summary>
/// Initializes a new instance of the <see cref="DivergenceStopCriterium"/> class with the default maximum
/// relative increase and the default minimum number of tracking iterations.
/// </summary>
public DivergenceStopCriterium() : this(DefaultMaximumRelativeIncrease, DefaultMinimumNumberOfIterations)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="DivergenceStopCriterium"/> class with the specified maximum
/// relative increase and the default minimum number of tracking iterations.
/// </summary>
/// <param name="maximumRelativeIncrease">The maximum relative increase that the residual may experience before a divergence warning is issued.</param>
public DivergenceStopCriterium(double maximumRelativeIncrease) : this(maximumRelativeIncrease, DefaultMinimumNumberOfIterations)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="DivergenceStopCriterium"/> class with the default maximum
/// relative increase and the specified minimum number of tracking iterations.
/// </summary>
/// <param name="minimumIterations">The minimum number of iterations over which the residual must grow before a divergence warning is issued. </param>
public DivergenceStopCriterium(int minimumIterations) : this(DefaultMinimumNumberOfIterations, minimumIterations)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="DivergenceStopCriterium"/> class with the specified maximum
/// relative increase and the specified minimum number of tracking iterations.
/// </summary>
/// <param name="maximumRelativeIncrease">The maximum relative increase that the residual may experience before a divergence warning is issued. </param>
/// <param name="minimumIterations">The minimum number of iterations over which the residual must grow before a divergence warning is issued.</param>
public DivergenceStopCriterium(double maximumRelativeIncrease, int minimumIterations)
{
if (maximumRelativeIncrease <= 0)
{
throw new ArgumentOutOfRangeException("maximumRelativeIncrease");
}
// There must be at least three iterations otherwise we can't calculate the relative increase
if (minimumIterations < 3)
{
throw new ArgumentOutOfRangeException("minimumIterations");
}
_maximumRelativeIncrease = maximumRelativeIncrease;
_minimumNumberOfIterations = minimumIterations;
}
/// <summary>
/// Gets or sets the maximum relative increase that the residual may experience before a divergence warning is issued.
/// </summary>
/// <exception cref="ArgumentOutOfRangeException">Thrown if the <c>Maximum</c> is set to zero or below.</exception>
public double MaximumRelativeIncrease
{
[DebuggerStepThrough]
get
{
return _maximumRelativeIncrease;
}
[DebuggerStepThrough]
set
{
if (value <= 0)
{
throw new ArgumentOutOfRangeException("value");
}
_maximumRelativeIncrease = value;
}
}
/// <summary>
/// Returns the maximum relative increase to the default.
/// </summary>
public void ResetMaximumRelativeIncreaseToDefault()
{
_maximumRelativeIncrease = DefaultMaximumRelativeIncrease;
}
/// <summary>
/// Gets or sets the minimum number of iterations over which the residual must grow before
/// issuing a divergence warning.
/// </summary>
/// <exception cref="ArgumentOutOfRangeException">Thrown if the <c>value</c> is set to less than one.</exception>
public int MinimumNumberOfIterations
{
[DebuggerStepThrough]
get
{
return _minimumNumberOfIterations;
}
[DebuggerStepThrough]
set
{
// There must be at least three iterations otherwise we can't calculate
// the relative increase
if (value < 3)
{
throw new ArgumentOutOfRangeException("value");
}
_minimumNumberOfIterations = value;
}
}
/// <summary>
/// Returns the minimum number of iterations to the default.
/// </summary>
public void ResetNumberOfIterationsToDefault()
{
_minimumNumberOfIterations = DefaultMinimumNumberOfIterations;
}
/// <summary>
/// Determines the status of the iterative calculation based on the stop criteria stored
/// by the current <see cref="IIterationStopCriterium{T}"/>. Result is set into <c>Status</c> field.
/// </summary>
/// <param name="iterationNumber">The number of iterations that have passed so far.</param>
/// <param name="solutionVector">The vector containing the current solution values.</param>
/// <param name="sourceVector">The right hand side vector.</param>
/// <param name="residualVector">The vector containing the current residual vectors.</param>
/// <remarks>
/// The individual stop criteria may internally track the progress of the calculation based
/// on the invocation of this method. Therefore this method should only be called if the
/// calculation has moved forwards at least one step.
/// </remarks>
public void DetermineStatus(int iterationNumber, Vector<Complex> solutionVector, Vector<Complex> sourceVector, Vector<Complex> residualVector)
{
if (iterationNumber < 0)
{
throw new ArgumentOutOfRangeException("iterationNumber");
}
if (residualVector == null)
{
throw new ArgumentNullException("residualVector");
}
if (_lastIteration >= iterationNumber)
{
// We have already stored the actual last iteration number
// For now do nothing. We only care about the next step.
return;
}
if ((_residualHistory == null) || (_residualHistory.Length != RequiredHistoryLength))
{
_residualHistory = new double[RequiredHistoryLength];
}
// We always track the residual.
// Move the old versions one element up in the array.
for (var i = 1; i < _residualHistory.Length; i++)
{
_residualHistory[i - 1] = _residualHistory[i];
}
// Store the infinity norms of both the solution and residual vectors
// These values will be used to calculate the relative drop in residuals later on.
_residualHistory[_residualHistory.Length - 1] = residualVector.Norm(Double.PositiveInfinity);
// Check if we have NaN's. If so we've gone way beyond normal divergence.
// Stop the iteration.
if (double.IsNaN(_residualHistory[_residualHistory.Length - 1]))
{
SetStatusToDiverged();
return;
}
// Check if we are diverging and if so set the status
if (IsDiverging())
{
SetStatusToDiverged();
}
else
{
SetStatusToRunning();
}
_lastIteration = iterationNumber;
}
/// <summary>
/// Detect if solution is diverging
/// </summary>
/// <returns><c>true</c> if diverging, otherwise <c>false</c></returns>
private bool IsDiverging()
{
// Run for each variable
for (var i = 1; i < _residualHistory.Length; i++)
{
var difference = _residualHistory[i] - _residualHistory[i - 1];
// Divergence is occurring if:
// - the last residual is larger than the previous one
// - the relative increase of the residual is larger than the setting allows
if ((difference < 0) || (_residualHistory[i - 1] * (1 + _maximumRelativeIncrease) >= _residualHistory[i]))
{
// No divergence taking place within the required number of iterations
// So reset and stop the iteration. There is no way we can get to the
// required number of iterations anymore.
return false;
}
}
return true;
}
/// <summary>
/// Gets required history lenght
/// </summary>
private int RequiredHistoryLength
{
[DebuggerStepThrough]
get
{
return _minimumNumberOfIterations + 1;
}
}
/// <summary>
/// Set status to <see cref="CalculationDiverged"/>
/// </summary>
private void SetStatusToDiverged()
{
if (!(_status is CalculationDiverged))
{
_status = new CalculationDiverged();
}
}
/// <summary>
/// Set status to <see cref="CalculationRunning"/>
/// </summary>
private void SetStatusToRunning()
{
if (!(_status is CalculationRunning))
{
_status = new CalculationRunning();
}
}
/// <summary>
/// Gets the current calculation status.
/// </summary>
public ICalculationStatus Status
{
[DebuggerStepThrough]
get
{
return _status;
}
}
/// <summary>
/// Resets the <see cref="IIterationStopCriterium{T}"/> to the pre-calculation state.
/// </summary>
public void ResetToPrecalculationState()
{
_status = DefaultStatus;
_lastIteration = DefaultLastIterationNumber;
_residualHistory = null;
}
/// <summary>
/// Gets the <see cref="StopLevel"/> which indicates what sort of stop criterium this
/// <see cref="IIterationStopCriterium{T}"/> monitors.
/// </summary>
/// <value>Returns <see cref="Generic.Solvers.StopCriterium.StopLevel.Divergence"/>.</value>
public StopLevel StopLevel
{
[DebuggerStepThrough]
get
{
return StopLevel.Divergence;
}
}
/// <summary>
/// Clones the current <see cref="DivergenceStopCriterium"/> and its settings.
/// </summary>
/// <returns>A new instance of the <see cref="DivergenceStopCriterium"/> class.</returns>
public IIterationStopCriterium<Complex> Clone()
{
return new DivergenceStopCriterium(_maximumRelativeIncrease, _minimumNumberOfIterations);
}
#if !SILVERLIGHT
/// <summary>
/// Clone this object
/// </summary>
/// <returns>Object clone</returns>
object ICloneable.Clone()
{
return Clone();
}
#endif
}
}

202
src/Numerics/LinearAlgebra/Complex/Solvers/StopCriterium/FailureStopCriterium.cs

@ -0,0 +1,202 @@
// <copyright file="FailureStopCriterium.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>
namespace MathNet.Numerics.LinearAlgebra.Complex.Solvers.StopCriterium
{
using System;
using System.Diagnostics;
using System.Numerics;
using Generic;
using Generic.Solvers.Status;
using Generic.Solvers.StopCriterium;
using Properties;
/// <summary>
/// Defines an <see cref="Generic.Solvers.StopCriterium.IIterationStopCriterium{T}"/> that monitors residuals for NaN's.
/// </summary>
public sealed class FailureStopCriterium : IIterationStopCriterium<Complex>
{
/// <summary>
/// Defines the default last iteration number. Set to -1 because iterations normally
/// start at 0.
/// </summary>
private const int DefaultLastIterationNumber = -1;
/// <summary>
/// The default status.
/// </summary>
private static readonly ICalculationStatus DefaultStatus = new CalculationIndetermined();
/// <summary>
/// The status of the calculation
/// </summary>
private ICalculationStatus _status = DefaultStatus;
/// <summary>
/// The iteration number of the last iteration.
/// </summary>
private int _lastIteration = DefaultLastIterationNumber;
/// <summary>
/// Determines the status of the iterative calculation based on the stop criteria stored
/// by the current <see cref="IIterationStopCriterium{T}"/>. Result is set into <c>Status</c> field.
/// </summary>
/// <param name="iterationNumber">The number of iterations that have passed so far.</param>
/// <param name="solutionVector">The vector containing the current solution values.</param>
/// <param name="sourceVector">The right hand side vector.</param>
/// <param name="residualVector">The vector containing the current residual vectors.</param>
/// <remarks>
/// The individual stop criteria may internally track the progress of the calculation based
/// on the invocation of this method. Therefore this method should only be called if the
/// calculation has moved forwards at least one step.
/// </remarks>
public void DetermineStatus(int iterationNumber, Vector<Complex> solutionVector, Vector<Complex> sourceVector, Vector<Complex> residualVector)
{
if (iterationNumber < 0)
{
throw new ArgumentOutOfRangeException("iterationNumber");
}
if (solutionVector == null)
{
throw new ArgumentNullException("solutionVector");
}
if (residualVector == null)
{
throw new ArgumentNullException("residualVector");
}
if (solutionVector.Count != residualVector.Count)
{
throw new ArgumentException(Resources.ArgumentArraysSameLength);
}
if (_lastIteration >= iterationNumber)
{
// We have already stored the actual last iteration number
// For now do nothing. We only care about the next step.
return;
}
// Store the infinity norms of both the solution and residual vectors
var residualNorm = residualVector.Norm(Double.PositiveInfinity);
var solutionNorm = solutionVector.Norm(Double.PositiveInfinity);
if (Double.IsNaN(solutionNorm) || Double.IsNaN(residualNorm))
{
SetStatusToFailed();
}
else
{
SetStatusToRunning();
}
_lastIteration = iterationNumber;
}
/// <summary>
/// Set status to <see cref="CalculationFailure"/>
/// </summary>
private void SetStatusToFailed()
{
if (!(_status is CalculationFailure))
{
_status = new CalculationFailure();
}
}
/// <summary>
/// Set status to <see cref="CalculationRunning"/>
/// </summary>
private void SetStatusToRunning()
{
if (!(_status is CalculationRunning))
{
_status = new CalculationRunning();
}
}
/// <summary>
/// Gets the current calculation status.
/// </summary>
public ICalculationStatus Status
{
[DebuggerStepThrough]
get
{
return _status;
}
}
/// <summary>
/// Resets the <see cref="IIterationStopCriterium{T}"/> to the pre-calculation state.
/// </summary>
public void ResetToPrecalculationState()
{
_status = DefaultStatus;
_lastIteration = DefaultLastIterationNumber;
}
/// <summary>
/// Gets the <see cref="StopLevel"/>which indicates what sort of stop criterium this
/// <see cref="IIterationStopCriterium{T}"/> monitors.
/// </summary>
/// <value>Returns <see cref="CalculationFailure"/>.</value>
public StopLevel StopLevel
{
[DebuggerStepThrough]
get
{
return StopLevel.CalculationFailure;
}
}
/// <summary>
/// Clones the current <see cref="FailureStopCriterium"/> and its settings.
/// </summary>
/// <returns>A new instance of the <see cref="FailureStopCriterium"/> class.</returns>
public IIterationStopCriterium<Complex> Clone()
{
return new FailureStopCriterium();
}
#if !SILVERLIGHT
/// <summary>
/// Clones the current <see cref="FailureStopCriterium"/> and its settings.
/// </summary>
/// <returns>A new instance of the <see cref="FailureStopCriterium"/> class.</returns>
object ICloneable.Clone()
{
return Clone();
}
#endif
}
}

228
src/Numerics/LinearAlgebra/Complex/Solvers/StopCriterium/IterationCountStopCriterium.cs

@ -0,0 +1,228 @@
// <copyright file="IterationCountStopCriterium.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>
namespace MathNet.Numerics.LinearAlgebra.Complex.Solvers.StopCriterium
{
using System;
using System.Diagnostics;
using System.Numerics;
using Generic;
using Generic.Solvers.Status;
using Generic.Solvers.StopCriterium;
/// <summary>
/// Defines an <see cref="IIterationStopCriterium{T}"/> that monitors the numbers of iteration
/// steps as stop criterium.
/// </summary>
public sealed class IterationCountStopCriterium : IIterationStopCriterium<Complex>
{
/// <summary>
/// The default value for the maximum number of iterations the process is allowed
/// to perform.
/// </summary>
public const int DefaultMaximumNumberOfIterations = 1000;
/// <summary>
/// The default status.
/// </summary>
private static readonly ICalculationStatus DefaultStatus = new CalculationIndetermined();
/// <summary>
/// The maximum number of iterations the calculation is allowed to perform.
/// </summary>
private int _maximumNumberOfIterations;
/// <summary>
/// The status of the calculation
/// </summary>
private ICalculationStatus _status = DefaultStatus;
/// <summary>
/// Initializes a new instance of the <see cref="IterationCountStopCriterium"/> class with the default maximum
/// number of iterations.
/// </summary>
public IterationCountStopCriterium() : this(DefaultMaximumNumberOfIterations)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="IterationCountStopCriterium"/> class with the specified maximum
/// number of iterations.
/// </summary>
/// <param name="maximumNumberOfIterations">The maximum number of iterations the calculation is allowed to perform.</param>
public IterationCountStopCriterium(int maximumNumberOfIterations)
{
if (maximumNumberOfIterations < 1)
{
throw new ArgumentOutOfRangeException("maximumNumberOfIterations");
}
_maximumNumberOfIterations = maximumNumberOfIterations;
}
/// <summary>
/// Gets or sets the maximum number of iterations the calculation is allowed to perform.
/// </summary>
/// <exception cref="ArgumentOutOfRangeException">Thrown if the <c>Maximum</c> is set to a negative value.</exception>
public int MaximumNumberOfIterations
{
[DebuggerStepThrough]
get
{
return _maximumNumberOfIterations;
}
[DebuggerStepThrough]
set
{
if (value < 1)
{
throw new ArgumentOutOfRangeException("value");
}
_maximumNumberOfIterations = value;
}
}
/// <summary>
/// Returns the maximum number of iterations to the default.
/// </summary>
public void ResetMaximumNumberOfIterationsToDefault()
{
_maximumNumberOfIterations = DefaultMaximumNumberOfIterations;
}
/// <summary>
/// Determines the status of the iterative calculation based on the stop criteria stored
/// by the current <see cref="IterationCountStopCriterium"/>. Result is set into <c>Status</c> field.
/// </summary>
/// <param name="iterationNumber">The number of iterations that have passed so far.</param>
/// <param name="solutionVector">The vector containing the current solution values.</param>
/// <param name="sourceVector">The right hand side vector.</param>
/// <param name="residualVector">The vector containing the current residual vectors.</param>
/// <remarks>
/// The individual stop criteria may internally track the progress of the calculation based
/// on the invocation of this method. Therefore this method should only be called if the
/// calculation has moved forwards at least one step.
/// </remarks>
public void DetermineStatus(int iterationNumber, Vector<Complex> solutionVector, Vector<Complex> sourceVector, Vector<Complex> residualVector)
{
if (iterationNumber < 0)
{
throw new ArgumentOutOfRangeException("iterationNumber");
}
if (iterationNumber >= _maximumNumberOfIterations)
{
SetStatusToFinished();
}
else
{
SetStatusToRunning();
}
}
/// <summary>
/// Set status to <see cref="CalculationFailure"/>
/// </summary>
private void SetStatusToFinished()
{
if (!(_status is CalculationStoppedWithoutConvergence))
{
_status = new CalculationStoppedWithoutConvergence();
}
}
/// <summary>
/// Set status to <see cref="CalculationRunning"/>
/// </summary>
private void SetStatusToRunning()
{
if (!(_status is CalculationRunning))
{
_status = new CalculationRunning();
}
}
/// <summary>
/// Gets the current calculation status.
/// </summary>
public ICalculationStatus Status
{
[DebuggerStepThrough]
get
{
return _status;
}
}
/// <summary>
/// Resets the <see cref="IterationCountStopCriterium"/> to the pre-calculation state.
/// </summary>
public void ResetToPrecalculationState()
{
_status = DefaultStatus;
}
/// <summary>
/// Gets the <see cref="StopLevel"/> which indicates what sort of stop criterium this
/// <see cref="IterationCountStopCriterium"/> monitors.
/// </summary>
/// <value>Returns <see cref="StopLevel"/>.</value>
public StopLevel StopLevel
{
[DebuggerStepThrough]
get
{
return StopLevel.StoppedWithoutConvergence;
}
}
/// <summary>
/// Clones the current <see cref="IterationCountStopCriterium"/> and its settings.
/// </summary>
/// <returns>A new instance of the <see cref="IterationCountStopCriterium"/> class.</returns>
public IIterationStopCriterium<Complex> Clone()
{
return new IterationCountStopCriterium(_maximumNumberOfIterations);
}
#if !SILVERLIGHT
/// <summary>
/// Clones the current <see cref="IterationCountStopCriterium"/> and its settings.
/// </summary>
/// <returns>A new instance of the <see cref="IterationCountStopCriterium"/> object.</returns>
object ICloneable.Clone()
{
return Clone();
}
#endif
}
}

410
src/Numerics/LinearAlgebra/Complex/Solvers/StopCriterium/ResidualStopCriterium.cs

@ -0,0 +1,410 @@
// <copyright file="ResidualStopCriterium.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>
namespace MathNet.Numerics.LinearAlgebra.Complex.Solvers.StopCriterium
{
using System;
using System.Diagnostics;
using System.Numerics;
using Generic;
using Generic.Solvers.Status;
using Generic.Solvers.StopCriterium;
using Properties;
/// <summary>
/// Defines an <see cref="IIterationStopCriterium{T}"/> that monitors residuals as stop criterium.
/// </summary>
public sealed class ResidualStopCriterium : IIterationStopCriterium<Complex>
{
/// <summary>
/// The default value for the maximum value of the residual.
/// </summary>
public const double DefaultMaximumResidual = 1e-12;
/// <summary>
/// The default value for the minimum number of iterations.
/// </summary>
public const int DefaultMinimumIterationsBelowMaximum = 0;
/// <summary>
/// Defines the default last iteration number. Set to -1 because iterations normally start at 0.
/// </summary>
private const int DefaultLastIterationNumber = -1;
/// <summary>
/// The default status.
/// </summary>
private static readonly ICalculationStatus DefaultStatus = new CalculationIndetermined();
/// <summary>
/// The maximum value for the residual below which the calculation is considered converged.
/// </summary>
private double _maximum;
/// <summary>
/// The minimum number of iterations for which the residual has to be below the maximum before
/// the calculation is considered converged.
/// </summary>
private int _minimumIterationsBelowMaximum;
/// <summary>
/// The status of the calculation
/// </summary>
private ICalculationStatus _status = DefaultStatus;
/// <summary>
/// The number of iterations since the residuals got below the maximum.
/// </summary>
private int _iterationCount;
/// <summary>
/// The iteration number of the last iteration.
/// </summary>
private int _lastIteration = DefaultLastIterationNumber;
/// <summary>
/// Initializes a new instance of the <see cref="ResidualStopCriterium"/> class with the default maximum
/// residual and the default minimum number of iterations.
/// </summary>
public ResidualStopCriterium() : this(DefaultMaximumResidual, DefaultMinimumIterationsBelowMaximum)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ResidualStopCriterium"/> class with the specified
/// maximum residual and the default minimum number of iterations.
/// </summary>
/// <param name="maximum">The maximum value for the residual below which the calculation is considered converged.</param>
public ResidualStopCriterium(double maximum) : this(maximum, DefaultMinimumIterationsBelowMaximum)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ResidualStopCriterium"/> class with the default maximum residual
/// and specified minimum number of iterations.
/// </summary>
/// <param name="minimumIterationsBelowMaximum">
/// The minimum number of iterations for which the residual has to be below the maximum before
/// the calculation is considered converged.
/// </param>
public ResidualStopCriterium(int minimumIterationsBelowMaximum) : this(DefaultMaximumResidual, minimumIterationsBelowMaximum)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ResidualStopCriterium"/> class with the specified
/// maximum residual and minimum number of iterations.
/// </summary>
/// <param name="maximum">
/// The maximum value for the residual below which the calculation is considered converged.
/// </param>
/// <param name="minimumIterationsBelowMaximum">
/// The minimum number of iterations for which the residual has to be below the maximum before
/// the calculation is considered converged.
/// </param>
public ResidualStopCriterium(double maximum, int minimumIterationsBelowMaximum)
{
if (maximum < 0)
{
throw new ArgumentOutOfRangeException("maximum");
}
if (minimumIterationsBelowMaximum < 0)
{
throw new ArgumentOutOfRangeException("minimumIterationsBelowMaximum");
}
_maximum = maximum;
_minimumIterationsBelowMaximum = minimumIterationsBelowMaximum;
}
/// <summary>
/// Gets or sets the maximum value for the residual below which the calculation is considered
/// converged.
/// </summary>
/// <exception cref="ArgumentOutOfRangeException">Thrown if the <c>Maximum</c> is set to a negative value.</exception>
public double Maximum
{
[DebuggerStepThrough]
get
{
return _maximum;
}
[DebuggerStepThrough]
set
{
if (value < 0)
{
throw new ArgumentOutOfRangeException("value");
}
_maximum = value;
}
}
/// <summary>
/// Returns the maximum residual to the default.
/// </summary>
public void ResetMaximumResidualToDefault()
{
_maximum = DefaultMaximumResidual;
}
/// <summary>
/// Gets or sets the minimum number of iterations for which the residual has to be
/// below the maximum before the calculation is considered converged.
/// </summary>
/// <exception cref="ArgumentOutOfRangeException">Thrown if the <c>BelowMaximumFor</c> is set to a value less than 1.</exception>
public int MinimumIterationsBelowMaximum
{
[DebuggerStepThrough]
get
{
return _minimumIterationsBelowMaximum;
}
[DebuggerStepThrough]
set
{
if (value < 0)
{
throw new ArgumentOutOfRangeException("value");
}
_minimumIterationsBelowMaximum = value;
}
}
/// <summary>
/// Returns the minimum number of iterations to the default.
/// </summary>
public void ResetMinimumIterationsBelowMaximumToDefault()
{
_minimumIterationsBelowMaximum = DefaultMinimumIterationsBelowMaximum;
}
/// <summary>
/// Determines the status of the iterative calculation based on the stop criteria stored
/// by the current <see cref="IIterationStopCriterium{T}"/>. Result is set into <c>Status</c> field.
/// </summary>
/// <param name="iterationNumber">The number of iterations that have passed so far.</param>
/// <param name="solutionVector">The vector containing the current solution values.</param>
/// <param name="sourceVector">The right hand side vector.</param>
/// <param name="residualVector">The vector containing the current residual vectors.</param>
/// <remarks>
/// The individual stop criteria may internally track the progress of the calculation based
/// on the invocation of this method. Therefore this method should only be called if the
/// calculation has moved forwards at least one step.
/// </remarks>
public void DetermineStatus(int iterationNumber, Vector<Complex> solutionVector, Vector<Complex> sourceVector, Vector<Complex> residualVector)
{
if (iterationNumber < 0)
{
throw new ArgumentOutOfRangeException("iterationNumber");
}
if (solutionVector == null)
{
throw new ArgumentNullException("solutionVector");
}
if (sourceVector == null)
{
throw new ArgumentNullException("sourceVector");
}
if (residualVector == null)
{
throw new ArgumentNullException("residualVector");
}
if (solutionVector.Count != sourceVector.Count)
{
throw new ArgumentException(Resources.ArgumentVectorsSameLength, "sourceVector");
}
if (solutionVector.Count != residualVector.Count)
{
throw new ArgumentException(Resources.ArgumentVectorsSameLength, "residualVector");
}
// Store the infinity norms of both the solution and residual vectors
// These values will be used to calculate the relative drop in residuals
// later on.
var residualNorm = residualVector.Norm(Double.PositiveInfinity);
// Check the residuals by calculating:
// ||r_i|| <= stop_tol * ||b||
var stopCriterium = ComputeStopCriterium(sourceVector.Norm(Double.PositiveInfinity));
// First check that we have real numbers not NaN's.
// NaN's can occur when the iterative process diverges so we
// stop if that is the case.
if (double.IsNaN(stopCriterium) || double.IsNaN(residualNorm))
{
_iterationCount = 0;
SetStatusToDiverged();
return;
}
// ||r_i|| <= stop_tol * ||b||
// Stop the calculation if it's clearly smaller than the tolerance
var decimalMagnitude = Math.Abs(stopCriterium.Magnitude()) + 1;
if (residualNorm.IsSmallerWithDecimalPlaces(stopCriterium, decimalMagnitude))
{
if (_lastIteration <= iterationNumber)
{
_iterationCount = iterationNumber - _lastIteration;
if (_iterationCount >= _minimumIterationsBelowMaximum)
{
SetStatusToConverged();
}
else
{
SetStatusToRunning();
}
}
}
else
{
_iterationCount = 0;
SetStatusToRunning();
}
_lastIteration = iterationNumber;
}
/// <summary>
/// Calculate stop criterium
/// </summary>
/// <param name="solutionNorm">Solution vector norm</param>
/// <returns>Criterium value</returns>
private double ComputeStopCriterium(double solutionNorm)
{
// This is criterium 1 from Templates for the solution of linear systems.
// The problem with this criterium is that it's not limiting enough. For now
// we won't use it. Later on we might get back to it.
// return mMaximumResidual * (System.Math.Abs(mMatrixNorm) * System.Math.Abs(solutionNorm) + System.Math.Abs(mVectorNorm));
// For now use criterium 2 from Templates for the solution of linear systems. See page 60.
return _maximum * Math.Abs(solutionNorm);
}
/// <summary>
/// Set status to <see cref="CalculationDiverged"/>
/// </summary>
private void SetStatusToDiverged()
{
if (!(_status is CalculationDiverged))
{
_status = new CalculationDiverged();
}
}
/// <summary>
/// Set status to <see cref="CalculationConverged"/>
/// </summary>
private void SetStatusToConverged()
{
if (!(_status is CalculationConverged))
{
_status = new CalculationConverged();
}
}
/// <summary>
/// Set status to <see cref="CalculationRunning"/>
/// </summary>
private void SetStatusToRunning()
{
if (!(_status is CalculationRunning))
{
_status = new CalculationRunning();
}
}
/// <summary>
/// Gets the current calculation status.
/// </summary>
public ICalculationStatus Status
{
[DebuggerStepThrough]
get
{
return _status;
}
}
/// <summary>
/// Resets the <see cref="IIterationStopCriterium{T}"/> to the pre-calculation state.
/// </summary>
public void ResetToPrecalculationState()
{
_status = DefaultStatus;
_iterationCount = 0;
_lastIteration = DefaultLastIterationNumber;
}
/// <summary>
/// Gets the <see cref="StopLevel"/> which indicates what sort of stop criterium this
/// <see cref="IIterationStopCriterium{T}"/> monitors.
/// </summary>
/// <value>Returns <see cref="StopLevel"/>.</value>
public StopLevel StopLevel
{
[DebuggerStepThrough]
get
{
return StopLevel.Convergence;
}
}
/// <summary>
/// Clones the current <see cref="ResidualStopCriterium"/> and its settings.
/// </summary>
/// <returns>A new instance of the <see cref="ResidualStopCriterium"/> class.</returns>
public IIterationStopCriterium<Complex> Clone()
{
return new ResidualStopCriterium(_maximum, _minimumIterationsBelowMaximum);
}
#if !SILVERLIGHT
/// <summary>
/// Clones the current <see cref="ResidualStopCriterium"/> and its settings.
/// </summary>
/// <returns>A new instance of the <see cref="ResidualStopCriterium"/> object.</returns>
object ICloneable.Clone()
{
return Clone();
}
#endif
}
}

1710
src/Numerics/LinearAlgebra/Complex/SparseMatrix.cs

File diff suppressed because it is too large

1695
src/Numerics/LinearAlgebra/Complex/SparseVector.cs

File diff suppressed because it is too large

13
src/Numerics/LinearAlgebra/Double/DenseMatrix.cs

@ -27,11 +27,8 @@
namespace MathNet.Numerics.LinearAlgebra.Double
{
using System;
using System.Text;
using Distributions;
using Factorization;
using Generic;
using Generic.Factorization;
using Properties;
using Threading;
@ -654,6 +651,15 @@ namespace MathNet.Numerics.LinearAlgebra.Double
return matrix;
}
/// <summary>
/// Returns the conjugate transpose of this matrix.
/// </summary>
/// <returns>The conjugate transpose of this matrix.</returns>
public override Matrix<double> ConjugateTranspose()
{
throw new NotSupportedException("ConjugateTranspose is not supported for real matricies");
}
#region Simple arithmetic of type T
/// <summary>
/// Add two values T+T
@ -719,5 +725,6 @@ namespace MathNet.Numerics.LinearAlgebra.Double
return Math.Abs(val1);
}
#endregion
}
}

10
src/Numerics/LinearAlgebra/Double/DenseVector.cs

@ -30,7 +30,6 @@ namespace MathNet.Numerics.LinearAlgebra.Double
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using Distributions;
using Generic;
using NumberTheory;
@ -1499,6 +1498,15 @@ namespace MathNet.Numerics.LinearAlgebra.Double
Array.Clear(Data, 0, Data.Length);
}
/// <summary>
/// Conjugates vector and save result to <paramref name="target"/>
/// </summary>
/// <param name="target">Target vector</param>
public override void Conjugate(Vector<double> target)
{
throw new NotSupportedException("Conjugate is not supported for real vectors");
}
#region Simple arithmetic of type T
/// <summary>
/// Add two values T+T

9
src/Numerics/LinearAlgebra/Double/DiagonalMatrix.cs

@ -1671,6 +1671,15 @@ namespace MathNet.Numerics.LinearAlgebra.Double
return stringBuilder.ToString();
}
/// <summary>
/// Returns the conjugate transpose of this matrix.
/// </summary>
/// <returns>The conjugate transpose of this matrix.</returns>
public override Matrix<double> ConjugateTranspose()
{
throw new NotSupportedException("ConjugateTranspose is not supported for real matricies");
}
#region Simple arithmetic of type T
/// <summary>
/// Add two values T+T

10
src/Numerics/LinearAlgebra/Double/Factorization/DenseCholesky.cs

@ -176,6 +176,16 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Factorization
}
#region Simple arithmetic of type T
/// <summary>
/// Add two values T+T
/// </summary>
/// <param name="val1">Left operand value</param>
/// <param name="val2">Right operand value</param>
/// <returns>Result of addition</returns>
protected sealed override double AddT(double val1, double val2)
{
return val1 + val2;
}
/// <summary>
/// Multiply two values T*T

10
src/Numerics/LinearAlgebra/Double/Factorization/SparseCholesky.cs

@ -222,6 +222,16 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Factorization
}
#region Simple arithmetic of type T
/// <summary>
/// Add two values T+T
/// </summary>
/// <param name="val1">Left operand value</param>
/// <param name="val2">Right operand value</param>
/// <returns>Result of addition</returns>
protected sealed override double AddT(double val1, double val2)
{
return val1 + val2;
}
/// <summary>
/// Multiply two values T*T

10
src/Numerics/LinearAlgebra/Double/Factorization/UserCholesky.cs

@ -256,6 +256,16 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Factorization
}
#region Simple T Mathematics
/// <summary>
/// Add two values T+T
/// </summary>
/// <param name="val1">Left operand value</param>
/// <param name="val2">Right operand value</param>
/// <returns>Result of addition</returns>
protected sealed override double AddT(double val1, double val2)
{
return val1 + val2;
}
/// <summary>
/// Multiply two values T*T

9
src/Numerics/LinearAlgebra/Double/SparseMatrix.cs

@ -1648,6 +1648,15 @@ namespace MathNet.Numerics.LinearAlgebra.Double
return stringBuilder.ToString();
}
/// <summary>
/// Returns the conjugate transpose of this matrix.
/// </summary>
/// <returns>The conjugate transpose of this matrix.</returns>
public override Matrix<double> ConjugateTranspose()
{
throw new NotSupportedException("ConjugateTranspose is not supported for real matricies");
}
#region Simple arithmetic of type T
/// <summary>
/// Add two values T+T

11
src/Numerics/LinearAlgebra/Double/SparseVector.cs

@ -1574,7 +1574,7 @@ namespace MathNet.Numerics.LinearAlgebra.Double
/// </param>
/// <returns>
/// <c>true</c> if the current object is equal to the <paramref name="other"/> parameter; otherwise, <c>false</c>.
/// </returns>
/// </returns>
public override bool Equals(Vector<double> other)
{
// Reject equality when the argument is null or has a different length.
@ -1618,6 +1618,15 @@ namespace MathNet.Numerics.LinearAlgebra.Double
return true;
}
/// <summary>
/// Conjugates vector and save result to <paramref name="target"/>
/// </summary>
/// <param name="target">Target vector</param>
public override void Conjugate(Vector<double> target)
{
throw new NotSupportedException("Conjugate is not supported for real vectors");
}
#region Simple arithmetic of type T
/// <summary>
/// Add two values T+T

28
src/Numerics/LinearAlgebra/Generic/Factorization/Cholesky.cs

@ -65,6 +65,17 @@ namespace MathNet.Numerics.LinearAlgebra.Generic.Factorization
return new LinearAlgebra.Double.Factorization.UserCholesky(matrix as Matrix<double>) as Cholesky<T>;
}
if (typeof(T) == typeof(Complex))
{
var dense = matrix as LinearAlgebra.Complex.DenseMatrix;
if (dense != null)
{
return new LinearAlgebra.Complex.Factorization.DenseCholesky(dense) as Cholesky<T>;
}
return new LinearAlgebra.Complex.Factorization.UserCholesky(matrix as Matrix<Complex>) as Cholesky<T>;
}
throw new NotImplementedException();
}
@ -108,14 +119,15 @@ namespace MathNet.Numerics.LinearAlgebra.Generic.Factorization
/// <summary>
/// Gets the log determinant of the matrix for which the Cholesky matrix was computed.
/// </summary>
public virtual double DeterminantLn
public virtual T DeterminantLn
{
get
{
var det = 0.0;
var det = default(T);
for (var j = 0; j < CholeskyFactor.RowCount; j++)
{
det += 2.0 * LogT(CholeskyFactor[j, j]);
// det += 2.0 * CholeskyFactor[j, j].NaturalLogarithm();
det = AddT(det, MultiplyT(AddT(OneValueT, OneValueT), LogT(CholeskyFactor[j, j])));
}
return det;
@ -174,6 +186,14 @@ namespace MathNet.Numerics.LinearAlgebra.Generic.Factorization
#region Simple arithmetic of type T
/// <summary>
/// Add two values T+T
/// </summary>
/// <param name="val1">Left operand value</param>
/// <param name="val2">Right operand value</param>
/// <returns>Result of addition</returns>
protected abstract T AddT(T val1, T val2);
/// <summary>
/// Multiply two values T*T
/// </summary>
@ -187,7 +207,7 @@ namespace MathNet.Numerics.LinearAlgebra.Generic.Factorization
/// </summary>
/// <param name="val1"> A number whose logarithm is to be found</param>
/// <returns>Natural (base e) logarithm </returns>
protected abstract double LogT(T val1);
protected abstract T LogT(T val1);
/// <summary>
/// Gets value of type T equal to one

41
src/Numerics/LinearAlgebra/Double/Factorization/ExtensionMethods.cs → src/Numerics/LinearAlgebra/Generic/Factorization/ExtensionMethods.cs

@ -28,10 +28,11 @@
// OTHER DEALINGS IN THE SOFTWARE.
// </copyright>
namespace MathNet.Numerics.LinearAlgebra.Double.Factorization
namespace MathNet.Numerics.LinearAlgebra.Generic.Factorization
{
using System;
using System.Numerics;
using Generic;
using Generic.Factorization;
/// <summary>
/// Extension methods which return factorizations for the various matrix classes.
@ -43,9 +44,10 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Factorization
/// </summary>
/// <param name="matrix">The matrix to factor.</param>
/// <returns>The Cholesky decomposition object.</returns>
public static Cholesky<double> Cholesky(this Matrix<double> matrix)
/// <typeparam name="T">Supported data types are double, single, <see cref="Complex"/>, and <see cref="Complex32"/>.</typeparam>
public static Cholesky<T> Cholesky<T>(this Matrix<T> matrix) where T : struct, IEquatable<T>, IFormattable
{
return Cholesky<double>.Create(matrix);
return Factorization.Cholesky<T>.Create(matrix);
}
/// <summary>
@ -53,9 +55,10 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Factorization
/// </summary>
/// <param name="matrix">The matrix to factor.</param>
/// <returns>The LU decomposition object.</returns>
public static LU<double> LU(this Matrix<double> matrix)
/// <typeparam name="T">Supported data types are double, single, <see cref="Complex"/>, and <see cref="Complex32"/>.</typeparam>
public static LU<T> LU<T>(this Matrix<T> matrix) where T : struct, IEquatable<T>, IFormattable
{
return LU<double>.Create(matrix);
return Factorization.LU<T>.Create(matrix);
}
/// <summary>
@ -63,9 +66,10 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Factorization
/// </summary>
/// <param name="matrix">The matrix to factor.</param>
/// <returns>The QR decomposition object.</returns>
public static QR<double> QR(this Matrix<double> matrix)
/// <typeparam name="T">Supported data types are double, single, <see cref="Complex"/>, and <see cref="Complex32"/>.</typeparam>
public static QR<T> QR<T>(this Matrix<T> matrix) where T : struct, IEquatable<T>, IFormattable
{
return QR<double>.Create(matrix);
return Factorization.QR<T>.Create(matrix);
}
/// <summary>
@ -73,10 +77,20 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Factorization
/// </summary>
/// <param name="matrix">The matrix to factor.</param>
/// <returns>The QR decomposition object.</returns>
public static GramSchmidt GramSchmidt(this Matrix<double> matrix)
/// <typeparam name="T">Supported data types are double, single, <see cref="Complex"/>, and <see cref="Complex32"/>.</typeparam>
public static QR<T> GramSchmidt<T>(this Matrix<T> matrix) where T : struct, IEquatable<T>, IFormattable
{
// NOTE: There is no factory for GramSchmidt. Use constructor of GramSchmidt class.
return new GramSchmidt(matrix);
if (typeof(T) == typeof(double))
{
return new LinearAlgebra.Double.Factorization.GramSchmidt(matrix as Matrix<double>) as QR<T>;
}
if (typeof(T) == typeof(Complex))
{
return new LinearAlgebra.Complex.Factorization.GramSchmidt(matrix as Matrix<Complex>) as QR<T>;
}
throw new NotImplementedException();
}
/// <summary>
@ -85,9 +99,10 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Factorization
/// <param name="matrix">The matrix to factor.</param>
/// <param name="computeVectors">Compute the singular U and VT vectors or not.</param>
/// <returns>The QR decomposition object.</returns>
public static Svd<double> Svd(this Matrix<double> matrix, bool computeVectors)
/// <typeparam name="T">Supported data types are double, single, <see cref="Complex"/>, and <see cref="Complex32"/>.</typeparam>
public static Svd<T> Svd<T>(this Matrix<T> matrix, bool computeVectors) where T : struct, IEquatable<T>, IFormattable
{
return Svd<double>.Create(matrix, computeVectors);
return Factorization.Svd<T>.Create(matrix, computeVectors);
}
}
}

11
src/Numerics/LinearAlgebra/Generic/Factorization/LU.cs

@ -80,6 +80,17 @@ namespace MathNet.Numerics.LinearAlgebra.Generic.Factorization
return new LinearAlgebra.Double.Factorization.UserLU(matrix as Matrix<double>) as LU<T>;
}
if (typeof(T) == typeof(Complex))
{
var dense = matrix as LinearAlgebra.Complex.DenseMatrix;
if (dense != null)
{
return new LinearAlgebra.Complex.Factorization.DenseLU(dense) as LU<T>;
}
return new LinearAlgebra.Complex.Factorization.UserLU(matrix as Matrix<Complex>) as LU<T>;
}
throw new NotImplementedException();
}

11
src/Numerics/LinearAlgebra/Generic/Factorization/QR.cs

@ -80,6 +80,17 @@ namespace MathNet.Numerics.LinearAlgebra.Generic.Factorization
return new LinearAlgebra.Double.Factorization.UserQR(matrix as Matrix<double>) as QR<T>;
}
if (typeof(T) == typeof(Complex))
{
var dense = matrix as LinearAlgebra.Complex.DenseMatrix;
if (dense != null)
{
return new LinearAlgebra.Complex.Factorization.DenseQR(dense) as QR<T>;
}
return new LinearAlgebra.Complex.Factorization.UserQR(matrix as Matrix<Complex>) as QR<T>;
}
throw new NotImplementedException();
}

11
src/Numerics/LinearAlgebra/Generic/Factorization/Svd.cs

@ -132,6 +132,17 @@ namespace MathNet.Numerics.LinearAlgebra.Generic.Factorization
return new LinearAlgebra.Double.Factorization.UserSvd(matrix as Matrix<double>, computeVectors) as Svd<T>;
}
if (typeof(T) == typeof(Complex))
{
var dense = matrix as LinearAlgebra.Complex.DenseMatrix;
if (dense != null)
{
return new LinearAlgebra.Complex.Factorization.DenseSvd(dense, computeVectors) as Svd<T>;
}
return new LinearAlgebra.Complex.Factorization.UserSvd(matrix as Matrix<Complex>, computeVectors) as Svd<T>;
}
throw new NotImplementedException();
}

9
src/Numerics/LinearAlgebra/Generic/Matrix.cs

@ -1487,6 +1487,12 @@ namespace MathNet.Numerics.LinearAlgebra.Generic
return ret;
}
/// <summary>
/// Returns the conjugate transpose of this matrix.
/// </summary>
/// <returns>The conjugate transpose of this matrix.</returns>
public abstract Matrix<T> ConjugateTranspose();
/// <summary>
/// Permute the rows of a matrix according to a permutation.
/// </summary>
@ -1796,9 +1802,6 @@ namespace MathNet.Numerics.LinearAlgebra.Generic
var transpose = Transpose();
var aat = this * transpose;
// TODO: Replace with multiple Transpose
//// aat.Gemm(1.0, 0.0, false, true, this, this);
var norm = 0.0;
for (var i = 0; i < RowCount; i++)
{

2
src/Numerics/LinearAlgebra/Generic/Solvers/IIterator.cs

@ -105,7 +105,7 @@ namespace MathNet.Numerics.LinearAlgebra.Generic.Solvers
void ResetToPrecalculationState();
#if SILVERLIGHT
IIterator<double> Clone();
IIterator<T> Clone();
#endif
}
}

2
src/Numerics/LinearAlgebra/Generic/Solvers/Preconditioners/IPreConditioner.cs

@ -65,7 +65,7 @@ namespace MathNet.Numerics.LinearAlgebra.Generic.Solvers.Preconditioners
/// </summary>
/// <param name="rhs">The right hand side vector.</param>
/// <returns>The left hand side vector.</returns>
Vector<double> Approximate(Vector<T> rhs);
Vector<T> Approximate(Vector<T> rhs);
/// <summary>
/// Approximates the solution to the matrix equation <b>Mx = b</b>.

2
src/Numerics/LinearAlgebra/Generic/Solvers/StopCriterium/IIterationStopCriterium.cs

@ -76,7 +76,7 @@ namespace MathNet.Numerics.LinearAlgebra.Generic.Solvers.StopCriterium
StopLevel StopLevel { get; }
#if SILVERLIGHT
IIterationStopCriterium<double> Clone();
IIterationStopCriterium<T> Clone();
#endif
}
}

17
src/Numerics/LinearAlgebra/Generic/Vector.cs

@ -1106,6 +1106,23 @@ namespace MathNet.Numerics.LinearAlgebra.Generic
#endregion
/// <summary>
/// Return vector with conjugate values of the source vector
/// </summary>
/// <returns>Conjugated vector</returns>
public Vector<T> Conjugate()
{
var retrunVector = CreateVector(Count);
Conjugate(retrunVector);
return retrunVector;
}
/// <summary>
/// Conjugates vector and save result to <paramref name="target"/>
/// </summary>
/// <param name="target">Target vector</param>
public abstract void Conjugate(Vector<T> target);
#region Copying and Conversion
/// <summary>

31
src/Numerics/Numerics.csproj

@ -107,6 +107,35 @@
<Compile Include="LinearAlgebra\Common\IO\Matlab\ArrayClass.cs" />
<Compile Include="LinearAlgebra\Common\IO\Matlab\ArrayFlags.cs" />
<Compile Include="LinearAlgebra\Common\IO\Matlab\DataType.cs" />
<Compile Include="LinearAlgebra\Complex\DenseMatrix.cs" />
<Compile Include="LinearAlgebra\Complex\DenseVector.cs" />
<Compile Include="LinearAlgebra\Complex\DiagonalMatrix.cs" />
<Compile Include="LinearAlgebra\Complex\Factorization\DenseCholesky.cs" />
<Compile Include="LinearAlgebra\Complex\Factorization\DenseLU.cs" />
<Compile Include="LinearAlgebra\Complex\Factorization\DenseQR.cs" />
<Compile Include="LinearAlgebra\Complex\Factorization\DenseSvd.cs" />
<Compile Include="LinearAlgebra\Complex\Factorization\GramSchmidt.cs" />
<Compile Include="LinearAlgebra\Complex\Factorization\UserCholesky.cs" />
<Compile Include="LinearAlgebra\Complex\Factorization\UserLU.cs" />
<Compile Include="LinearAlgebra\Complex\Factorization\UserQR.cs" />
<Compile Include="LinearAlgebra\Complex\Factorization\UserSvd.cs" />
<Compile Include="LinearAlgebra\Complex\Solvers\Iterative\BiCgStab.cs" />
<Compile Include="LinearAlgebra\Complex\Solvers\Iterative\CompositeSolver.cs" />
<Compile Include="LinearAlgebra\Complex\Solvers\Iterative\GpBiCg.cs" />
<Compile Include="LinearAlgebra\Complex\Solvers\Iterative\MlkBiCgStab.cs" />
<Compile Include="LinearAlgebra\Complex\Solvers\Iterative\TFQMR.cs" />
<Compile Include="LinearAlgebra\Complex\Solvers\Iterator.cs" />
<Compile Include="LinearAlgebra\Complex\Solvers\Preconditioners\Diagonal.cs" />
<Compile Include="LinearAlgebra\Complex\Solvers\Preconditioners\Ilutp.cs" />
<Compile Include="LinearAlgebra\Complex\Solvers\Preconditioners\IlutpElementSorter.cs" />
<Compile Include="LinearAlgebra\Complex\Solvers\Preconditioners\IncompleteLU.cs" />
<Compile Include="LinearAlgebra\Complex\Solvers\Preconditioners\UnitPreconditioner.cs" />
<Compile Include="LinearAlgebra\Complex\Solvers\StopCriterium\DivergenceStopCriterium.cs" />
<Compile Include="LinearAlgebra\Complex\Solvers\StopCriterium\FailureStopCriterium.cs" />
<Compile Include="LinearAlgebra\Complex\Solvers\StopCriterium\IterationCountStopCriterium.cs" />
<Compile Include="LinearAlgebra\Complex\Solvers\StopCriterium\ResidualStopCriterium.cs" />
<Compile Include="LinearAlgebra\Complex\SparseMatrix.cs" />
<Compile Include="LinearAlgebra\Complex\SparseVector.cs" />
<Compile Include="LinearAlgebra\Double\DenseMatrix.cs" />
<Compile Include="LinearAlgebra\Double\DenseVector.cs" />
<Compile Include="LinearAlgebra\Double\DiagonalMatrix.cs" />
@ -115,8 +144,8 @@
<Compile Include="LinearAlgebra\Double\Factorization\DenseLU.cs" />
<Compile Include="LinearAlgebra\Double\Factorization\DenseQR.cs" />
<Compile Include="LinearAlgebra\Double\Factorization\DenseSvd.cs" />
<Compile Include="LinearAlgebra\Double\Factorization\ExtensionMethods.cs" />
<Compile Include="LinearAlgebra\Double\Factorization\GramSchmidt.cs" />
<Compile Include="LinearAlgebra\Generic\Factorization\ExtensionMethods.cs" />
<Compile Include="LinearAlgebra\Generic\Factorization\LU.cs" />
<Compile Include="LinearAlgebra\Generic\Factorization\QR.cs" />
<Compile Include="LinearAlgebra\Double\Factorization\SparseCholesky.cs" />

14
src/Numerics/Precision.cs

@ -764,18 +764,8 @@ namespace MathNet.Numerics
/// <returns>true if the two values differ by no more than 10 * 2^(-52); false otherwise.</returns>
public static bool AlmostEqual(this Complex a, Complex b)
{
// TODO - I think that it should be changed to "if (a.IsNaN() && b.IsNaN()) { return true; }"
if (a.IsNaN() || b.IsNaN())
{
return false;
}
if (a.IsInfinity() && b.IsInfinity())
{
return true;
}
return a.Real.AlmostEqual(b.Real) && a.Imaginary.AlmostEqual(b.Imaginary);
double diff = a.NormOfDifference(b);
return AlmostEqualWithError(a.Norm(), b.Norm(), diff, _defaultDoubleRelativeAccuracy);
}
/// <summary>

93
src/Silverlight/Silverlight.csproj

@ -215,6 +215,93 @@
<Compile Include="..\Numerics\IPrecisionSupport.cs">
<Link>IPrecisionSupport.cs</Link>
</Compile>
<Compile Include="..\Numerics\LinearAlgebra\Complex\DenseMatrix.cs">
<Link>LinearAlgebra\Complex\DenseMatrix.cs</Link>
</Compile>
<Compile Include="..\Numerics\LinearAlgebra\Complex\DenseVector.cs">
<Link>LinearAlgebra\Complex\DenseVector.cs</Link>
</Compile>
<Compile Include="..\Numerics\LinearAlgebra\Complex\DiagonalMatrix.cs">
<Link>LinearAlgebra\Complex\DiagonalMatrix.cs</Link>
</Compile>
<Compile Include="..\Numerics\LinearAlgebra\Complex\Factorization\DenseCholesky.cs">
<Link>LinearAlgebra\Complex\Factorization\DenseCholesky.cs</Link>
</Compile>
<Compile Include="..\Numerics\LinearAlgebra\Complex\Factorization\DenseLU.cs">
<Link>LinearAlgebra\Complex\Factorization\DenseLU.cs</Link>
</Compile>
<Compile Include="..\Numerics\LinearAlgebra\Complex\Factorization\DenseQR.cs">
<Link>LinearAlgebra\Complex\Factorization\DenseQR.cs</Link>
</Compile>
<Compile Include="..\Numerics\LinearAlgebra\Complex\Factorization\DenseSvd.cs">
<Link>LinearAlgebra\Complex\Factorization\DenseSvd.cs</Link>
</Compile>
<Compile Include="..\Numerics\LinearAlgebra\Complex\Factorization\GramSchmidt.cs">
<Link>LinearAlgebra\Complex\Factorization\GramSchmidt.cs</Link>
</Compile>
<Compile Include="..\Numerics\LinearAlgebra\Complex\Factorization\UserCholesky.cs">
<Link>LinearAlgebra\Complex\Factorization\UserCholesky.cs</Link>
</Compile>
<Compile Include="..\Numerics\LinearAlgebra\Complex\Factorization\UserLU.cs">
<Link>LinearAlgebra\Complex\Factorization\UserLU.cs</Link>
</Compile>
<Compile Include="..\Numerics\LinearAlgebra\Complex\Factorization\UserQR.cs">
<Link>LinearAlgebra\Complex\Factorization\UserQR.cs</Link>
</Compile>
<Compile Include="..\Numerics\LinearAlgebra\Complex\Factorization\UserSvd.cs">
<Link>LinearAlgebra\Complex\Factorization\UserSvd.cs</Link>
</Compile>
<Compile Include="..\Numerics\LinearAlgebra\Complex\Solvers\Iterative\BiCgStab.cs">
<Link>LinearAlgebra\Complex\Solvers\Iterative\BiCgStab.cs</Link>
</Compile>
<Compile Include="..\Numerics\LinearAlgebra\Complex\Solvers\Iterative\CompositeSolver.cs">
<Link>LinearAlgebra\Complex\Solvers\Iterative\CompositeSolver.cs</Link>
</Compile>
<Compile Include="..\Numerics\LinearAlgebra\Complex\Solvers\Iterative\GpBiCg.cs">
<Link>LinearAlgebra\Complex\Solvers\Iterative\GpBiCg.cs</Link>
</Compile>
<Compile Include="..\Numerics\LinearAlgebra\Complex\Solvers\Iterative\MlkBiCgStab.cs">
<Link>LinearAlgebra\Complex\Solvers\Iterative\MlkBiCgStab.cs</Link>
</Compile>
<Compile Include="..\Numerics\LinearAlgebra\Complex\Solvers\Iterative\TFQMR.cs">
<Link>LinearAlgebra\Complex\Solvers\Iterative\TFQMR.cs</Link>
</Compile>
<Compile Include="..\Numerics\LinearAlgebra\Complex\Solvers\Iterator.cs">
<Link>LinearAlgebra\Complex\Solvers\Iterator.cs</Link>
</Compile>
<Compile Include="..\Numerics\LinearAlgebra\Complex\Solvers\Preconditioners\Diagonal.cs">
<Link>LinearAlgebra\Complex\Solvers\Preconditioners\Diagonal.cs</Link>
</Compile>
<Compile Include="..\Numerics\LinearAlgebra\Complex\Solvers\Preconditioners\Ilutp.cs">
<Link>LinearAlgebra\Complex\Solvers\Preconditioners\Ilutp.cs</Link>
</Compile>
<Compile Include="..\Numerics\LinearAlgebra\Complex\Solvers\Preconditioners\IlutpElementSorter.cs">
<Link>LinearAlgebra\Complex\Solvers\Preconditioners\IlutpElementSorter.cs</Link>
</Compile>
<Compile Include="..\Numerics\LinearAlgebra\Complex\Solvers\Preconditioners\IncompleteLU.cs">
<Link>LinearAlgebra\Complex\Solvers\Preconditioners\IncompleteLU.cs</Link>
</Compile>
<Compile Include="..\Numerics\LinearAlgebra\Complex\Solvers\Preconditioners\UnitPreconditioner.cs">
<Link>LinearAlgebra\Complex\Solvers\Preconditioners\UnitPreconditioner.cs</Link>
</Compile>
<Compile Include="..\Numerics\LinearAlgebra\Complex\Solvers\StopCriterium\DivergenceStopCriterium.cs">
<Link>LinearAlgebra\Complex\Solvers\StopCriterium\DivergenceStopCriterium.cs</Link>
</Compile>
<Compile Include="..\Numerics\LinearAlgebra\Complex\Solvers\StopCriterium\FailureStopCriterium.cs">
<Link>LinearAlgebra\Complex\Solvers\StopCriterium\FailureStopCriterium.cs</Link>
</Compile>
<Compile Include="..\Numerics\LinearAlgebra\Complex\Solvers\StopCriterium\IterationCountStopCriterium.cs">
<Link>LinearAlgebra\Complex\Solvers\StopCriterium\IterationCountStopCriterium.cs</Link>
</Compile>
<Compile Include="..\Numerics\LinearAlgebra\Complex\Solvers\StopCriterium\ResidualStopCriterium.cs">
<Link>LinearAlgebra\Complex\Solvers\StopCriterium\ResidualStopCriterium.cs</Link>
</Compile>
<Compile Include="..\Numerics\LinearAlgebra\Complex\SparseMatrix.cs">
<Link>LinearAlgebra\Complex\SparseMatrix.cs</Link>
</Compile>
<Compile Include="..\Numerics\LinearAlgebra\Complex\SparseVector.cs">
<Link>LinearAlgebra\Complex\SparseVector.cs</Link>
</Compile>
<Compile Include="..\Numerics\LinearAlgebra\Double\DenseMatrix.cs">
<Link>LinearAlgebra\Double\DenseMatrix.cs</Link>
</Compile>
@ -236,9 +323,6 @@
<Compile Include="..\Numerics\LinearAlgebra\Double\Factorization\DenseSvd.cs">
<Link>LinearAlgebra\Double\Factorization\DenseSvd.cs</Link>
</Compile>
<Compile Include="..\Numerics\LinearAlgebra\Double\Factorization\ExtensionMethods.cs">
<Link>LinearAlgebra\Double\Factorization\ExtensionMethods.cs</Link>
</Compile>
<Compile Include="..\Numerics\LinearAlgebra\Double\Factorization\GramSchmidt.cs">
<Link>LinearAlgebra\Double\Factorization\GramSchmidt.cs</Link>
</Compile>
@ -332,6 +416,9 @@
<Compile Include="..\Numerics\LinearAlgebra\Generic\Factorization\Cholesky.cs">
<Link>LinearAlgebra\Generic\Factorization\Cholesky.cs</Link>
</Compile>
<Compile Include="..\Numerics\LinearAlgebra\Generic\Factorization\ExtensionMethods.cs">
<Link>LinearAlgebra\Generic\Factorization\ExtensionMethods.cs</Link>
</Compile>
<Compile Include="..\Numerics\LinearAlgebra\Generic\Factorization\LU.cs">
<Link>LinearAlgebra\Generic\Factorization\LU.cs</Link>
</Compile>

157
src/UnitTests/LinearAlgebraTests/Complex/DenseMatrixTests.cs

@ -0,0 +1,157 @@
// <copyright file="DenseMatrixTests.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>
namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex
{
using System.Collections.Generic;
using System.Numerics;
using LinearAlgebra.Generic;
using MbUnit.Framework;
using LinearAlgebra.Complex;
public class DenseMatrixTests : MatrixTests
{
protected override Matrix<Complex> CreateMatrix(int rows, int columns)
{
return new DenseMatrix(rows, columns);
}
protected override Matrix<Complex> CreateMatrix(Complex[,] data)
{
return new DenseMatrix(data);
}
protected override Vector<Complex> CreateVector(int size)
{
return new DenseVector(size);
}
protected override Vector<Complex> CreateVector(Complex[] data)
{
return new DenseVector(data);
}
[Test]
public void CanCreateMatrixFrom1DArray()
{
Dictionary<string, Matrix<Complex>> testData = new Dictionary<string, Matrix<Complex>>
{
{ "Singular3x3", new DenseMatrix(3, 3, new[] { new Complex(1.0, 1), new Complex(1.0, 1), new Complex(1.0, 1), new Complex(1.0, 1), new Complex(1.0, 1), new Complex(1.0, 1), new Complex(2.0, 1), new Complex(2.0, 1), new Complex(2.0, 1) }) },
{ "Square3x3", new DenseMatrix(3, 3, new[] { new Complex(-1.1, 1), Complex.Zero, new Complex(-4.4, 1), new Complex(-2.2, 1), new Complex(1.1, 1), new Complex(5.5, 1), new Complex(-3.3, 1), new Complex(2.2, 1), new Complex(6.6, 1) }) },
{ "Square4x4", new DenseMatrix(4, 4, new[] { new Complex(-1.1, 1), Complex.Zero, new Complex(1.0, 1), new Complex(-4.4, 1), new Complex(-2.2, 1), new Complex(1.1, 1), new Complex(2.1, 1), new Complex(5.5, 1), new Complex(-3.3, 1), new Complex(2.2, 1), new Complex(6.2, 1), new Complex(6.6, 1), new Complex(-4.4, 1), new Complex(3.3, 1), new Complex(4.3, 1), new Complex(-7.7, 1) }) },
{ "Tall3x2", new DenseMatrix(3, 2, new[] { new Complex(-1.1, 1), Complex.Zero, new Complex(-4.4, 1), new Complex(-2.2, 1), new Complex(1.1, 1), new Complex(5.5, 1) }) },
{ "Wide2x3", new DenseMatrix(2, 3, new[] { new Complex(-1.1, 1), Complex.Zero, new Complex(-2.2, 1), new Complex(1.1, 1), new Complex(-3.3, 1), new Complex(2.2, 1) }) }
};
foreach (var name in testData.Keys)
{
Assert.AreEqual(TestMatrices[name], testData[name]);
}
}
[Test]
public void MatrixFrom1DArrayIsReference()
{
var data = new[] { new Complex(1.0, 1), new Complex(1.0, 1), new Complex(1.0, 1), new Complex(1.0, 1), new Complex(1.0, 1), new Complex(1.0, 1), new Complex(2.0, 1), new Complex(2.0, 1), new Complex(2.0, 1) };
var matrix = new DenseMatrix(3, 3, data);
matrix[0, 0] = 10.0;
AssertHelpers.AreEqual(10.0, data[0]);
}
[Test]
public void MatrixFrom2DArrayIsCopy()
{
var matrix = new DenseMatrix(TestData2D["Singular3x3"]);
matrix[0, 0] = 10.0;
AssertHelpers.AreEqual(new Complex(1.0, 1), TestData2D["Singular3x3"][0, 0]);
}
[Test]
[Row("Singular3x3")]
[Row("Singular3x3")]
[Row("Square3x3")]
[Row("Square4x4")]
[Row("Tall3x2")]
[Row("Wide2x3")]
public void CanCreateMatrixFrom2DArray(string name)
{
var matrix = new DenseMatrix(TestData2D[name]);
for (var i = 0; i < TestData2D[name].GetLength(0); i++)
{
for (var j = 0; j < TestData2D[name].GetLength(1); j++)
{
AssertHelpers.AreEqual(TestData2D[name][i, j], matrix[i, j]);
}
}
}
[Test]
public void CanCreateMatrixWithUniformValues()
{
var matrix = new DenseMatrix(10, 10, new Complex(10.0, 1));
for (var i = 0; i < matrix.RowCount; i++)
{
for (var j = 0; j < matrix.ColumnCount; j++)
{
AssertHelpers.AreEqual(matrix[i, j], new Complex(10.0, 1));
}
}
}
[Test]
public void CanCreateIdentity()
{
var matrix = DenseMatrix.Identity(5);
for (var i = 0; i < matrix.RowCount; i++)
{
for (var j = 0; j < matrix.ColumnCount; j++)
{
if (i == j)
{
AssertHelpers.AreEqual(Complex.One, matrix[i, j]);
}
else
{
AssertHelpers.AreEqual(Complex.Zero, matrix[i, j]);
}
}
}
}
[Test]
[Row(0)]
[Row(-1)]
[ExpectedArgumentException]
public void IdentityFailsWithZeroOrNegativeOrder(int order)
{
DenseMatrix.Identity(order);
}
}
}

100
src/UnitTests/LinearAlgebraTests/Complex/DenseVectorTest.TextHandling.cs

@ -0,0 +1,100 @@
// <copyright file="DenseVectorTest.TextHandling.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>
namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex
{
using System;
using System.Globalization;
using LinearAlgebra.Complex;
using MbUnit.Framework;
[TestFixture]
public class DenseVectorTextHandlingTest
{
[Test]
[Row("2", "(2, 0)")]
[Row("(3)", "(3, 0)")]
[Row("[1,2,3]", "(1, 0),(2, 0),(3, 0)")]
[Row(" [ 1.1 , 2.1 , 3.1 ] ", "(1.1, 0),(2.1, 0),(3.1, 0)")]
[Row(" [ -1.1 , 2.1 , +3.1 ] ", "(-1.1, 0),(2.1, 0),(3.1, 0)")]
[Row(" [1.2,3.4 , 5.6] ", "(1.2, 0),(3.4, 0),(5.6, 0)")]
[Row("[1+1i,2+1i,3+1i]", "(1, 1),(2, 1),(3, 1)")]
[Row(" [ 1.1 + 1i , 2.1+1i , 3.1+1i ] ", "(1.1, 1),(2.1, 1),(3.1, 1)")]
[Row(" [ -1.1 + 1i , 2.1-1i , +3.1+1i ] ", "(-1.1, 1),(2.1, -1),(3.1, 1)")]
[Row(" [1.2+2.3i ,3.4+4.5i , 5.6+ 6.7i] ", "(1.2, 2.3),(3.4, 4.5),(5.6, 6.7)")]
public void CanParseComplexDenseVectorsWithInvariant(string stringToParse, string expectedToString)
{
var formatProvider = CultureInfo.InvariantCulture;
DenseVector vector = DenseVector.Parse(stringToParse, formatProvider);
Assert.AreEqual(expectedToString, vector.ToString(formatProvider));
}
[Test]
[Row(" 1.2 + 1i , 3.4 + 1i , 5.6 + 1i ", "(1.2, 1),(3.4, 1),(5.6, 1)", "en-US")]
[Row(" 1.2 + 1i ; 3.4 + 1i ; 5.6 + 1i ", "(1.2, 1);(3.4, 1);(5.6, 1)", "de-CH")]
[Row(" 1,2 + 1i ; 3,4 + 1i ; 5,6 + 1i ", "(1,2, 1);(3,4, 1);(5,6, 1)", "de-DE")]
public void CanParseComplexDenseVectorsWithCulture(string stringToParse, string expectedToString, string culture)
{
var formatProvider = CultureInfo.GetCultureInfo(culture);
DenseVector vector = DenseVector.Parse(stringToParse, formatProvider);
Assert.AreEqual(expectedToString, vector.ToString(formatProvider));
}
[Test]
[MultipleAsserts]
public void ParseThrowsFormatExceptionIfMissingClosingParen()
{
Assert.Throws<FormatException>(() => DenseVector.Parse("(1"));
Assert.Throws<FormatException>(() => DenseVector.Parse("[1"));
}
[Test]
[Row(null)]
[Row("")]
[Row(",")]
[Row("1,")]
[Row(",1")]
[Row(",1,2,")]
[Row("1,,2,,3")]
[Row("1e+")]
[Row("1e")]
[Row("()")]
[Row("[ ]")]
public void TryParseReturnsFalseWhenGivenBadValueWithInvariant(string str)
{
DenseVector vector;
var ret = DenseVector.TryParse(str, CultureInfo.InvariantCulture, out vector);
Assert.IsFalse(ret);
Assert.IsNull(vector);
}
}
}

291
src/UnitTests/LinearAlgebraTests/Complex/DenseVectorTests.cs

@ -0,0 +1,291 @@
// <copyright file="DenseVectorTests.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>
namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex
{
using System;
using System.Collections.Generic;
using System.Numerics;
using LinearAlgebra.Complex;
using LinearAlgebra.Generic;
using MbUnit.Framework;
public class DenseVectorTests : VectorTests
{
protected override Vector<Complex> CreateVector(int size)
{
return new DenseVector(size);
}
protected override Vector<Complex> CreateVector(IList<Complex> data)
{
var vector = new DenseVector(data.Count);
for (var index = 0; index < data.Count; index++)
{
vector[index] = data[index];
}
return vector;
}
[Test]
[MultipleAsserts]
public void CanCreateDenseVectorFromArray()
{
var data = new Complex[Data.Length];
Array.Copy(Data, data, Data.Length);
var vector = new DenseVector(data);
Assert.AreSame(data, vector.Data);
for (var i = 0; i < data.Length; i++)
{
AssertHelpers.AreEqual(data[i], vector[i]);
}
vector[0] = 100.0;
AssertHelpers.AreEqual(100.0, data[0]);
}
[Test]
[MultipleAsserts]
public void CanCreateDenseVectorFromAnotherDenseVector()
{
var vector = new DenseVector(Data);
var other = new DenseVector(vector);
Assert.AreNotSame(vector, other);
for (var i = 0; i < Data.Length; i++)
{
AssertHelpers.AreEqual(vector[i], other[i]);
}
}
[Test]
[MultipleAsserts]
public void CanCreateDenseVectorFromAnotherVector()
{
var vector = (Vector<Complex>)new DenseVector(Data);
var other = new DenseVector(vector);
Assert.AreNotSame(vector, other);
for (var i = 0; i < Data.Length; i++)
{
AssertHelpers.AreEqual(vector[i], other[i]);
}
}
[Test]
[MultipleAsserts]
public void CanCreateDenseVectorFromUserDefinedVector()
{
var vector = new UserDefinedVector(Data);
var other = new DenseVector(vector);
for (var i = 0; i < Data.Length; i++)
{
Assert.AreEqual(vector[i], other[i]);
}
}
[Test]
public void CanCreateDenseVectorWithConstantValues()
{
var vector = new DenseVector(5, 5);
Assert.ForAll(vector, value => value == 5);
}
[Test]
[MultipleAsserts]
public void CanCreateDenseMatrix()
{
var vector = new DenseVector(3);
var matrix = vector.CreateMatrix(2, 3);
Assert.AreEqual(2, matrix.RowCount);
Assert.AreEqual(3, matrix.ColumnCount);
}
[Test]
[MultipleAsserts]
public void CanConvertDenseVectorToArray()
{
var vector = new DenseVector(Data);
var array = (Complex[])vector;
Assert.IsInstanceOfType(typeof(Complex[]), array);
Assert.AreSame(vector.Data, array);
Assert.AreElementsEqual(vector, array);
}
[Test]
[MultipleAsserts]
public void CanConvertArrayToDenseVector()
{
var array = new[] { new Complex(1, 1), new Complex(2, 1), new Complex(3, 1), new Complex(4, 1) };
var vector = (DenseVector)array;
Assert.IsInstanceOfType(typeof(DenseVector), vector);
Assert.AreElementsEqual(array, array);
}
[Test]
public void CanCallUnaryPlusOperatorOnDenseVector()
{
var vector = new DenseVector(Data);
var other = +vector;
for (var i = 0; i < Data.Length; i++)
{
AssertHelpers.AreEqual(vector[i], other[i]);
}
}
[Test]
[MultipleAsserts]
public void CanAddTwoDenseVectorsUsingOperator()
{
var vector = new DenseVector(Data);
var other = new DenseVector(Data);
var result = vector + other;
for (var i = 0; i < Data.Length; i++)
{
AssertHelpers.AreEqual(Data[i], vector[i]);
AssertHelpers.AreEqual(Data[i], other[i]);
AssertHelpers.AreEqual(Data[i] * 2.0, result[i]);
}
}
[Test]
public void CanCallUnaryNegationOperatorOnDenseVector()
{
var vector = new DenseVector(Data);
var other = -vector;
for (var i = 0; i < Data.Length; i++)
{
AssertHelpers.AreEqual(-Data[i], other[i]);
}
}
[Test]
[MultipleAsserts]
public void CanSubtractTwoDenseVectorsUsingOperator()
{
var vector = new DenseVector(Data);
var other = new DenseVector(Data);
var result = vector - other;
for (var i = 0; i < Data.Length; i++)
{
AssertHelpers.AreEqual(Data[i], vector[i]);
AssertHelpers.AreEqual(Data[i], other[i]);
AssertHelpers.AreEqual(0.0, result[i]);
}
}
[Test]
[MultipleAsserts]
public void CanMultiplyDenseVectorByComplexUsingOperators()
{
var vector = new DenseVector(Data);
vector = vector * new Complex(2.0, 1);
for (var i = 0; i < Data.Length; i++)
{
AssertHelpers.AreEqual(Data[i] * new Complex(2.0, 1), vector[i]);
}
vector = vector * 1.0;
for (var i = 0; i < Data.Length; i++)
{
AssertHelpers.AreEqual(Data[i] * new Complex(2.0, 1), vector[i]);
}
vector = new DenseVector(Data);
vector = new Complex(2.0, 1) * vector;
for (var i = 0; i < Data.Length; i++)
{
AssertHelpers.AreEqual(Data[i] * new Complex(2.0, 1), vector[i]);
}
vector = 1.0 * vector;
for (var i = 0; i < Data.Length; i++)
{
AssertHelpers.AreEqual(Data[i] * new Complex(2.0, 1), vector[i]);
}
}
[Test]
[MultipleAsserts]
public void CanDivideDenseVectorByComplexUsingOperators()
{
var vector = new DenseVector(Data);
vector = vector / new Complex(2.0, 1);
for (var i = 0; i < Data.Length; i++)
{
AssertHelpers.AreEqual(Data[i] / new Complex(2.0, 1), vector[i]);
}
vector = vector / 1.0;
for (var i = 0; i < Data.Length; i++)
{
AssertHelpers.AreEqual(Data[i] / new Complex(2.0, 1), vector[i]);
}
}
[Test]
public void CanCalculateOuterProductForDenseVector()
{
var vector1 = CreateVector(Data);
var vector2 = CreateVector(Data);
Matrix<Complex> m = Vector<Complex>.OuterProduct(vector1, vector2);
for (var i = 0; i < vector1.Count; i++)
{
for (var j = 0; j < vector2.Count; j++)
{
AssertHelpers.AreEqual(m[i, j], vector1[i] * vector2[j]);
}
}
}
[Test]
[ExpectedArgumentNullException]
public void OuterProducForDenseVectortWithFirstParameterNullShouldThrowException()
{
DenseVector vector1 = null;
var vector2 = CreateVector(Data);
Vector<Complex>.OuterProduct(vector1, vector2);
}
[Test]
[ExpectedArgumentNullException]
public void OuterProductForDenseVectorWithSecondParameterNullShouldThrowException()
{
var vector1 = CreateVector(Data);
DenseVector vector2 = null;
Vector<Complex>.OuterProduct(vector1, vector2);
}
}
}

452
src/UnitTests/LinearAlgebraTests/Complex/DiagonalMatrixTests.cs

@ -0,0 +1,452 @@
// <copyright file="DiagonalMatrixTests.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>
namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using LinearAlgebra.Generic;
using MbUnit.Framework;
using LinearAlgebra.Complex;
public class DiagonalMatrixTests : MatrixTests
{
[SetUp]
public override void SetupMatrices()
{
TestData2D = new Dictionary<string, Complex[,]>
{
{ "Singular3x3", new [,] { { new Complex(1.0, 1), Complex.Zero, Complex.Zero }, { Complex.Zero, Complex.Zero, Complex.Zero }, { Complex.Zero, Complex.Zero, new Complex(3.0, 1) } } },
{ "Square3x3", new [,] { { new Complex(-1.1, 1), Complex.Zero, Complex.Zero }, { Complex.Zero, new Complex(1.1, 1), Complex.Zero }, { Complex.Zero, Complex.Zero, new Complex(6.6, 1) } } },
{ "Square4x4", new [,] { { new Complex(-1.1, 1), Complex.Zero, Complex.Zero, Complex.Zero }, { Complex.Zero, new Complex(1.1, 1), Complex.Zero, Complex.Zero }, { Complex.Zero, Complex.Zero, new Complex(6.2, 1), Complex.Zero}, { Complex.Zero, Complex.Zero, Complex.Zero, new Complex(-7.7, 1) } } },
{ "Singular4x4", new [,] { { new Complex(-1.1, 1), Complex.Zero, Complex.Zero, Complex.Zero }, { Complex.Zero, new Complex(-2.2, 1), Complex.Zero, Complex.Zero }, { Complex.Zero, Complex.Zero, Complex.Zero, Complex.Zero}, { Complex.Zero, Complex.Zero, Complex.Zero, new Complex(-4.4, 1) } } },
{ "Tall3x2", new [,] { { new Complex(-1.1, 1), Complex.Zero }, { Complex.Zero, new Complex(1.1, 1) }, { Complex.Zero, Complex.Zero } } },
{ "Wide2x3", new [,] { { new Complex(-1.1, 1), Complex.Zero, Complex.Zero }, { Complex.Zero, new Complex(1.1, 1), Complex.Zero } } }
};
TestMatrices = new Dictionary<string, Matrix<Complex>>();
foreach (var name in TestData2D.Keys)
{
TestMatrices.Add(name, CreateMatrix(TestData2D[name]));
}
}
protected override Matrix<Complex> CreateMatrix(int rows, int columns)
{
return new DiagonalMatrix(rows, columns);
}
protected override Matrix<Complex> CreateMatrix(Complex[,] data)
{
return new DiagonalMatrix(data);
}
protected override Vector<Complex> CreateVector(int size)
{
return new SparseVector(size);
}
protected override Vector<Complex> CreateVector(Complex[] data)
{
return new SparseVector(data);
}
[Test]
public void CanCreateMatrixFromDiagonalArray()
{
var testData = new Dictionary<string, Matrix<Complex>>
{
{ "Singular3x3", new DiagonalMatrix(3, 3, new[] { new Complex(1.0, 1), Complex.Zero, new Complex(3.0, 1) }) },
{ "Square3x3", new DiagonalMatrix(4, 4, new[] { new Complex(-1.1, 1), new Complex(1.1, 1), new Complex(6.6, 1) }) },
{ "Square4x4", new DiagonalMatrix(4, 4, new[] { new Complex(-1.1, 1), new Complex(1.1, 1), new Complex(6.2, 1), new Complex(-7.7, 1) }) },
{ "Tall3x2", new DiagonalMatrix(3, 2, new[] { new Complex(-1.1, 1), new Complex(1.1, 1) }) },
{ "Wide2x3", new DiagonalMatrix(2, 3, new[] { new Complex(-1.1, 1), new Complex(1.1, 1) }) },
};
foreach (var name in testData.Keys)
{
Assert.AreEqual(TestMatrices[name], testData[name]);
}
}
[Test]
public void MatrixFrom1DArrayIsReference()
{
var data = new[] { new Complex(1.0, 1), new Complex(2.0, 1), new Complex(3.0, 1), new Complex(4.0, 1), new Complex(5.0, 1) };
var matrix = new DiagonalMatrix(5, 5, data);
matrix[0, 0] = 10.0;
Assert.AreEqual(10.0, data[0]);
}
[Test]
[Row("Singular3x3")]
[Row("Singular3x3")]
[Row("Square3x3")]
[Row("Square4x4")]
[Row("Tall3x2")]
[Row("Wide2x3")]
public void CanCreateMatrixFrom2DArray(string name)
{
var matrix = new DiagonalMatrix(TestData2D[name]);
for (var i = 0; i < TestData2D[name].GetLength(0); i++)
{
for (var j = 0; j < TestData2D[name].GetLength(1); j++)
{
Assert.AreEqual(TestData2D[name][i, j], matrix[i, j]);
}
}
}
[Test]
public void CanCreateMatrixWithUniformValues()
{
var matrix = new DiagonalMatrix(10, 10, 10.0);
for (var i = 0; i < matrix.RowCount; i++)
{
Assert.AreEqual(matrix[i, i], 10.0);
}
}
[Test]
public void CanCreateIdentity()
{
var matrix = DiagonalMatrix.Identity(5);
for (var i = 0; i < matrix.RowCount; i++)
{
for (var j = 0; j < matrix.ColumnCount; j++)
{
Assert.AreEqual(i == j ? 1.0 : 0.0, matrix[i, j]);
}
}
}
[Test]
[Row(0)]
[Row(-1)]
[ExpectedArgumentException]
public void IdentityFailsWithZeroOrNegativeOrder(int order)
{
DiagonalMatrix.Identity(order);
}
[Test]
public override void CanDiagonallyStackMatricesWithPassingResult()
{
var top = TestMatrices["Tall3x2"];
var bottom = TestMatrices["Wide2x3"];
var result = new SparseMatrix(top.RowCount + bottom.RowCount, top.ColumnCount + bottom.ColumnCount);
top.DiagonalStack(bottom, result);
Assert.AreEqual(top.RowCount + bottom.RowCount, result.RowCount);
Assert.AreEqual(top.ColumnCount + bottom.ColumnCount, result.ColumnCount);
for (var i = 0; i < result.RowCount; i++)
{
for (var j = 0; j < result.ColumnCount; j++)
{
if (i < top.RowCount && j < top.ColumnCount)
{
Assert.AreEqual(top[i, j], result[i, j]);
}
else if (i >= top.RowCount && j >= top.ColumnCount)
{
Assert.AreEqual(bottom[i - top.RowCount, j - top.ColumnCount], result[i, j]);
}
else
{
Assert.AreEqual(0, result[i, j]);
}
}
}
}
public override void CanMultiplyMatrixWithMatrixIntoResult(string nameA, string nameB)
{
var matrixA = TestMatrices[nameA];
var matrixB = TestMatrices[nameB];
var matrixC = new SparseMatrix(matrixA.RowCount, matrixB.ColumnCount);
matrixA.Multiply(matrixB, matrixC);
Assert.AreEqual(matrixC.RowCount, matrixA.RowCount);
Assert.AreEqual(matrixC.ColumnCount, matrixB.ColumnCount);
for (var i = 0; i < matrixC.RowCount; i++)
{
for (var j = 0; j < matrixC.ColumnCount; j++)
{
AssertHelpers.AlmostEqual(matrixA.Row(i) * matrixB.Column(j), matrixC[i, j], 15);
}
}
}
[Test]
[Row("Singular3x3")]
[ExpectedException(typeof(InvalidOperationException))]
public void CanPermuteMatrixRowsThrowException(string name)
{
var matrix = CreateMatrix(TestData2D[name]);
var matrixp = CreateMatrix(TestData2D[name]);
var permutation = new Permutation(new[] { 2, 0, 1 });
matrixp.PermuteRows(permutation);
Assert.AreNotSame(matrix, matrixp);
Assert.AreEqual(matrix.RowCount, matrixp.RowCount);
Assert.AreEqual(matrix.ColumnCount, matrixp.ColumnCount);
for (var i = 0; i < matrix.RowCount; i++)
{
for (var j = 0; j < matrix.ColumnCount; j++)
{
Assert.AreEqual(matrix[i, j], matrixp[permutation[i], j]);
}
}
}
[Test]
[Row("Singular3x3")]
[ExpectedException(typeof(InvalidOperationException))]
public void CanPermuteMatrixColumnsThrowException(string name)
{
var matrix = CreateMatrix(TestData2D[name]);
var matrixp = CreateMatrix(TestData2D[name]);
var permutation = new Permutation(new[] { 2, 0, 1 });
matrixp.PermuteColumns(permutation);
Assert.AreNotSame(matrix, matrixp);
Assert.AreEqual(matrix.RowCount, matrixp.RowCount);
Assert.AreEqual(matrix.ColumnCount, matrixp.ColumnCount);
for (var i = 0; i < matrix.RowCount; i++)
{
for (var j = 0; j < matrix.ColumnCount; j++)
{
Assert.AreEqual(matrix[i, j], matrixp[i, permutation[j]]);
}
}
}
public override void CanPermuteMatrixRows(string name)
{
}
public override void CanPermuteMatrixColumns(string name)
{
}
public override void PointwiseDivideResult()
{
foreach (var data in TestMatrices.Values)
{
var other = data.Clone();
var result = data.Clone();
data.PointwiseDivide(other, result);
var min = Math.Min(data.RowCount, data.ColumnCount);
for (var i = 0; i < min; i++)
{
Assert.AreEqual((data[i, i] / other[i, i]).Real, result[i, i].Real);
Assert.AreEqual((data[i, i] / other[i, i]).Imaginary, result[i, i].Imaginary);
}
result = data.PointwiseDivide(other);
for (var i = 0; i < min; i++)
{
Assert.AreEqual((data[i, i] / other[i, i]).Real, result[i, i].Real);
Assert.AreEqual((data[i, i] / other[i, i]).Imaginary, result[i, i].Imaginary);
}
}
}
public override void SetColumnWithArray(string name, double[] column)
{
try
{
// Pass all invoke to base
base.SetColumnWithArray(name, column);
}
catch(AggregateException ex)
{
// Supress only IndexOutOfRangeException exceptions due to Diagonal matrix nature
if (ex.InnerExceptions.Any(innerException => !(innerException is IndexOutOfRangeException)))
{
throw;
}
}
}
public override void SetColumnWithVector(string name, double[] column)
{
try
{
// Pass all invoke to base
base.SetColumnWithVector(name, column);
}
catch (AggregateException ex)
{
// Supress only IndexOutOfRangeException exceptions due to Diagonal matrix nature
if (ex.InnerExceptions.Any(innerException => !(innerException is IndexOutOfRangeException)))
{
throw;
}
}
}
public override void SetRowWithArray(string name, double[] row)
{
try
{
// Pass all invoke to base
base.SetRowWithArray(name, row);
}
catch (AggregateException ex)
{
// Supress only IndexOutOfRangeException exceptions due to Diagonal matrix nature
if (ex.InnerExceptions.Any(innerException => !(innerException is IndexOutOfRangeException)))
{
throw;
}
}
}
public override void SetRowWithVector(string name, double[] row)
{
try
{
// Pass all invoke to base
base.SetRowWithVector(name, row);
}
catch (AggregateException ex)
{
// Supress only IndexOutOfRangeException exceptions due to Diagonal matrix nature
if (ex.InnerExceptions.Any(innerException => !(innerException is IndexOutOfRangeException)))
{
throw;
}
}
}
public override void SetSubMatrix(int rowStart, int rowLength, int colStart, int colLength)
{
try
{
// Pass all invoke to base
base.SetSubMatrix(rowStart, rowLength, colStart, colLength);
}
catch (AggregateException ex)
{
// Supress only IndexOutOfRangeException exceptions due to Diagonal matrix nature
if (ex.InnerExceptions.Any(innerException => !(innerException is IndexOutOfRangeException)))
{
throw;
}
}
}
public override void FrobeniusNorm()
{
var matrix = TestMatrices["Square3x3"];
var denseMatrix = new DenseMatrix(TestData2D["Square3x3"]);
AssertHelpers.AlmostEqual(denseMatrix.FrobeniusNorm(), matrix.FrobeniusNorm(), 14);
matrix = TestMatrices["Wide2x3"];
denseMatrix = new DenseMatrix(TestData2D["Wide2x3"]);
AssertHelpers.AlmostEqual(denseMatrix.FrobeniusNorm(), matrix.FrobeniusNorm(), 14);
matrix = TestMatrices["Tall3x2"];
denseMatrix = new DenseMatrix(TestData2D["Tall3x2"]);
AssertHelpers.AlmostEqual(denseMatrix.FrobeniusNorm(), matrix.FrobeniusNorm(), 14);
}
public override void InfinityNorm()
{
var matrix = TestMatrices["Square3x3"];
var denseMatrix = new DenseMatrix(TestData2D["Square3x3"]);
AssertHelpers.AlmostEqual(denseMatrix.InfinityNorm(), matrix.InfinityNorm(), 14);
matrix = TestMatrices["Wide2x3"];
denseMatrix = new DenseMatrix(TestData2D["Wide2x3"]);
AssertHelpers.AlmostEqual(denseMatrix.InfinityNorm(), matrix.InfinityNorm(), 14);
matrix = TestMatrices["Tall3x2"];
denseMatrix = new DenseMatrix(TestData2D["Tall3x2"]);
AssertHelpers.AlmostEqual(denseMatrix.InfinityNorm(), matrix.InfinityNorm(), 14);
}
public override void L1Norm()
{
var matrix = TestMatrices["Square3x3"];
var denseMatrix = new DenseMatrix(TestData2D["Square3x3"]);
AssertHelpers.AlmostEqual(denseMatrix.L1Norm(), matrix.L1Norm(), 14);
matrix = TestMatrices["Wide2x3"];
denseMatrix = new DenseMatrix(TestData2D["Wide2x3"]);
AssertHelpers.AlmostEqual(denseMatrix.L1Norm(), matrix.L1Norm(), 14);
matrix = TestMatrices["Tall3x2"];
denseMatrix = new DenseMatrix(TestData2D["Tall3x2"]);
AssertHelpers.AlmostEqual(denseMatrix.L1Norm(), matrix.L1Norm(), 14);
}
public override void L2Norm()
{
var matrix = TestMatrices["Square3x3"];
var denseMatrix = new DenseMatrix(TestData2D["Square3x3"]);
AssertHelpers.AlmostEqual(denseMatrix.L2Norm(), matrix.L2Norm(), 14);
matrix = TestMatrices["Wide2x3"];
denseMatrix = new DenseMatrix(TestData2D["Wide2x3"]);
AssertHelpers.AlmostEqual(denseMatrix.L2Norm(), matrix.L2Norm(), 14);
matrix = TestMatrices["Tall3x2"];
denseMatrix = new DenseMatrix(TestData2D["Tall3x2"]);
AssertHelpers.AlmostEqual(denseMatrix.L2Norm(), matrix.L2Norm(), 14);
}
[Test]
[MultipleAsserts]
public void Determinant()
{
var matrix = TestMatrices["Square3x3"];
var denseMatrix = new DenseMatrix(TestData2D["Square3x3"]);
AssertHelpers.AlmostEqual(denseMatrix.Determinant(), matrix.Determinant(), 14);
matrix = TestMatrices["Square4x4"];
denseMatrix = new DenseMatrix(TestData2D["Square4x4"]);
AssertHelpers.AlmostEqual(denseMatrix.Determinant(), matrix.Determinant(), 14);
}
[Test]
[ExpectedArgumentException]
public void DeterminantNotSquareMatrixThrowException()
{
var matrix = TestMatrices["Tall3x2"];
matrix.Determinant();
}
}
}

305
src/UnitTests/LinearAlgebraTests/Complex/Factorization/CholeskyTests.cs

@ -0,0 +1,305 @@
// <copyright file="CholeskyTests.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>
namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex.Factorization
{
using LinearAlgebra.Generic.Factorization;
using MbUnit.Framework;
using LinearAlgebra.Complex;
public class CholeskyTests
{
[Test]
[Row(1)]
[Row(10)]
[Row(100)]
public void CanFactorizeIdentity(int order)
{
var I = DenseMatrix.Identity(order);
var factorC = I.Cholesky();
Assert.AreEqual(I.RowCount, factorC.Factor.RowCount);
Assert.AreEqual(I.ColumnCount, factorC.Factor.ColumnCount);
for (var i = 0; i < factorC.Factor.RowCount; i++)
{
for (var j = 0; j < factorC.Factor.ColumnCount; j++)
{
Assert.AreEqual(i == j ? 1.0 : 0.0, factorC.Factor[i, j]);
}
}
}
[Test]
[ExpectedArgumentException]
public void CholeskyFailsWithDiagonalNonPositiveDefiniteMatrix()
{
var I = DenseMatrix.Identity(10);
I[3, 3] = -4.0;
I.Cholesky();
}
[Test]
[Row(3,5)]
[Row(5,3)]
[ExpectedArgumentException]
public void CholeskyFailsWithNonSquareMatrix(int row, int col)
{
var I = new DenseMatrix(row, col);
I.Cholesky();
}
[Test]
[Row(1)]
[Row(10)]
[Row(100)]
public void IdentityDeterminantIsOne(int order)
{
var I = DenseMatrix.Identity(order);
var factorC = I.Cholesky();
Assert.AreEqual(1.0, factorC.Determinant);
Assert.AreEqual(0.0, factorC.DeterminantLn);
}
[Test]
[Row(1)]
[Row(2)]
[Row(5)]
[Row(10)]
[Row(50)]
[Row(100)]
[MultipleAsserts]
public void CanFactorizeRandomMatrix(int order)
{
var matrixX = MatrixLoader.GenerateRandomPositiveDefiniteHermitianDenseMatrix(order);
var chol = matrixX.Cholesky();
var factorC = chol.Factor;
// Make sure the Cholesky factor has the right dimensions.
Assert.AreEqual(order, factorC.RowCount);
Assert.AreEqual(order, factorC.ColumnCount);
// Make sure the Cholesky factor is lower triangular.
for (var i = 0; i < factorC.RowCount; i++)
{
for (var j = i+1; j < factorC.ColumnCount; j++)
{
Assert.AreEqual(0.0, factorC[i, j]);
}
}
// Make sure the cholesky factor times it's transpose is the original matrix.
var matrixXfromC = factorC * factorC.ConjugateTranspose();
for (var i = 0; i < matrixXfromC.RowCount; i++)
{
for (var j = 0; j < matrixXfromC.ColumnCount; j++)
{
Assert.AreApproximatelyEqual(matrixX[i,j].Real, matrixXfromC[i, j].Real, 1.0e-9);
Assert.AreApproximatelyEqual(matrixX[i, j].Imaginary, matrixXfromC[i, j].Imaginary, 1.0e-9);
}
}
}
[Test]
[Row(1)]
[Row(2)]
[Row(5)]
[Row(10)]
[Row(50)]
[Row(100)]
[MultipleAsserts]
public void CanSolveForRandomVector(int order)
{
var matrixA = MatrixLoader.GenerateRandomPositiveDefiniteHermitianDenseMatrix(order);
var matrixACopy = matrixA.Clone();
var chol = matrixA.Cholesky();
var b = MatrixLoader.GenerateRandomDenseVector(order);
var x = chol.Solve(b);
Assert.AreEqual(b.Count, x.Count);
var bReconstruct = matrixA * x;
// Check the reconstruction.
for (var i = 0; i < order; i++)
{
Assert.AreApproximatelyEqual(b[i].Real, bReconstruct[i].Real, 1.0e-9);
Assert.AreApproximatelyEqual(b[i].Imaginary, bReconstruct[i].Imaginary, 1.0e-9);
}
// Make sure A didn't change.
for (var i = 0; i < matrixA.RowCount; i++)
{
for (var j = 0; j < matrixA.ColumnCount; j++)
{
Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]);
}
}
}
[Test]
[Row(1,1)]
[Row(2,4)]
[Row(5,8)]
[Row(10,3)]
[Row(50,10)]
[Row(100,100)]
[MultipleAsserts]
public void CanSolveForRandomMatrix(int row, int col)
{
var matrixA = MatrixLoader.GenerateRandomPositiveDefiniteHermitianDenseMatrix(row);
var matrixACopy = matrixA.Clone();
var chol = matrixA.Cholesky();
var matrixB = MatrixLoader.GenerateRandomDenseMatrix(row, col);
var matrixX = chol.Solve(matrixB);
Assert.AreEqual(matrixB.RowCount, matrixX.RowCount);
Assert.AreEqual(matrixB.ColumnCount, matrixX.ColumnCount);
var matrixBReconstruct = matrixA * matrixX;
// Check the reconstruction.
for (var i = 0; i < matrixB.RowCount; i++)
{
for (var j = 0; j < matrixB.ColumnCount; j++)
{
Assert.AreApproximatelyEqual(matrixB[i, j].Real, matrixBReconstruct[i, j].Real, 1.0e-9);
Assert.AreApproximatelyEqual(matrixB[i, j].Imaginary, matrixBReconstruct[i, j].Imaginary, 1.0e-9);
}
}
// Make sure A didn't change.
for (var i = 0; i < matrixA.RowCount; i++)
{
for (var j = 0; j < matrixA.ColumnCount; j++)
{
Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]);
}
}
}
[Test]
[Row(1)]
[Row(2)]
[Row(5)]
[Row(10)]
[Row(50)]
[Row(100)]
[MultipleAsserts]
public void CanSolveForRandomVectorWhenResultVectorGiven(int order)
{
var matrixA = MatrixLoader.GenerateRandomPositiveDefiniteHermitianDenseMatrix(order);
var matrixACopy = matrixA.Clone();
var chol = matrixA.Cholesky();
var b = MatrixLoader.GenerateRandomDenseVector(order);
var bCopy = b.Clone();
var x = new DenseVector(order);
chol.Solve(b, x);
Assert.AreEqual(b.Count, x.Count);
var bReconstruct = matrixA * x;
// Check the reconstruction.
for (var i = 0; i < order; i++)
{
Assert.AreApproximatelyEqual(b[i].Real, bReconstruct[i].Real, 1.0e-9);
Assert.AreApproximatelyEqual(b[i].Imaginary, bReconstruct[i].Imaginary, 1.0e-9);
}
// Make sure A didn't change.
for (var i = 0; i < matrixA.RowCount; i++)
{
for (var j = 0; j < matrixA.ColumnCount; j++)
{
Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]);
}
}
// Make sure b didn't change.
for (var i = 0; i < order; i++)
{
Assert.AreEqual(bCopy[i], b[i]);
}
}
[Test]
[Row(1, 1)]
[Row(2, 4)]
[Row(5, 8)]
[Row(10, 3)]
[Row(50, 10)]
[Row(100, 100)]
[MultipleAsserts]
public void CanSolveForRandomMatrixWhenResultMatrixGiven(int row, int col)
{
var matrixA = MatrixLoader.GenerateRandomPositiveDefiniteHermitianDenseMatrix(row);
var matrixACopy = matrixA.Clone();
var chol = matrixA.Cholesky();
var matrixB = MatrixLoader.GenerateRandomDenseMatrix(row, col);
var matrixBCopy = matrixB.Clone();
var matrixX = new DenseMatrix(row, col);
chol.Solve(matrixB, matrixX);
Assert.AreEqual(matrixB.RowCount, matrixX.RowCount);
Assert.AreEqual(matrixB.ColumnCount, matrixX.ColumnCount);
var matrixBReconstruct = matrixA * matrixX;
// Check the reconstruction.
for (var i = 0; i < matrixB.RowCount; i++)
{
for (var j = 0; j < matrixB.ColumnCount; j++)
{
Assert.AreApproximatelyEqual(matrixB[i, j].Real, matrixBReconstruct[i, j].Real, 1.0e-9);
Assert.AreApproximatelyEqual(matrixB[i, j].Imaginary, matrixBReconstruct[i, j].Imaginary, 1.0e-9);
}
}
// Make sure A didn't change.
for (var i = 0; i < matrixA.RowCount; i++)
{
for (var j = 0; j < matrixA.ColumnCount; j++)
{
Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]);
}
}
// Make sure B didn't change.
for (var i = 0; i < matrixB.RowCount; i++)
{
for (var j = 0; j < matrixB.ColumnCount; j++)
{
Assert.AreEqual(matrixBCopy[i, j], matrixB[i, j]);
}
}
}
}
}

356
src/UnitTests/LinearAlgebraTests/Complex/Factorization/GramSchmidtTests.cs

@ -0,0 +1,356 @@
// <copyright file="GramSchmidtTests.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>
namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex.Factorization
{
using LinearAlgebra.Generic.Factorization;
using MbUnit.Framework;
using LinearAlgebra.Complex.Factorization;
public class GramSchmidtTests
{
[Test]
[ExpectedArgumentNullException]
public void ConstructorNull()
{
new GramSchmidt(null);
}
[Test]
[ExpectedArgumentException]
public void WideMatrixThrowsInvalidMatrixOperationException()
{
new GramSchmidt(new UserDefinedMatrix(3, 4));
}
[Test]
[Row(1)]
[Row(10)]
[Row(100)]
public void CanFactorizeIdentity(int order)
{
var I = UserDefinedMatrix.Identity(order);
var factorGramSchmidt = I.GramSchmidt();
Assert.AreEqual(I.RowCount, factorGramSchmidt.Q.RowCount);
Assert.AreEqual(I.ColumnCount, factorGramSchmidt.Q.ColumnCount);
for (var i = 0; i < factorGramSchmidt.R.RowCount; i++)
{
for (var j = 0; j < factorGramSchmidt.R.ColumnCount; j++)
{
if (i == j)
{
Assert.AreEqual(1.0, factorGramSchmidt.R[i, j]);
}
else
{
Assert.AreEqual(0.0, factorGramSchmidt.R[i, j]);
}
}
}
for (var i = 0; i < factorGramSchmidt.Q.RowCount; i++)
{
for (var j = 0; j < factorGramSchmidt.Q.ColumnCount; j++)
{
if (i == j)
{
Assert.AreEqual(1.0, factorGramSchmidt.Q[i, j]);
}
else
{
Assert.AreEqual(0.0, factorGramSchmidt.Q[i, j]);
}
}
}
}
[Test]
[Row(1)]
[Row(10)]
[Row(100)]
public void IdentityDeterminantIsOne(int order)
{
var I = UserDefinedMatrix.Identity(order);
var factorGramSchmidt = I.GramSchmidt();
Assert.AreEqual(1.0, factorGramSchmidt.Determinant);
}
[Test]
[Row(1,1)]
[Row(2,2)]
[Row(5,5)]
[Row(10,6)]
[Row(50,48)]
[Row(100,98)]
[MultipleAsserts]
public void CanFactorizeRandomMatrix(int row, int column)
{
var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(row, column);
var factorGramSchmidt = matrixA.GramSchmidt();
// Make sure the Q has the right dimensions.
Assert.AreEqual(row, factorGramSchmidt.Q.RowCount);
Assert.AreEqual(column, factorGramSchmidt.Q.ColumnCount);
// Make sure the R has the right dimensions.
Assert.AreEqual(column, factorGramSchmidt.R.RowCount);
Assert.AreEqual(column, factorGramSchmidt.R.ColumnCount);
// Make sure the R factor is upper triangular.
for (var i = 0; i < factorGramSchmidt.R.RowCount; i++)
{
for (var j = 0; j < factorGramSchmidt.R.ColumnCount; j++)
{
if (i > j)
{
Assert.AreEqual(0.0, factorGramSchmidt.R[i, j]);
}
}
}
// Make sure the Q*R is the original matrix.
var matrixQfromR = factorGramSchmidt.Q * factorGramSchmidt.R;
for (var i = 0; i < matrixQfromR.RowCount; i++)
{
for (var j = 0; j < matrixQfromR.ColumnCount; j++)
{
Assert.AreApproximatelyEqual(matrixA[i, j].Real, matrixQfromR[i, j].Real, 1.0e-9);
Assert.AreApproximatelyEqual(matrixA[i, j].Imaginary, matrixQfromR[i, j].Imaginary, 1.0e-9);
}
}
// Make sure the Q is unitary --> (Q*)x(Q) = I
var matrixQсtQ = factorGramSchmidt.Q.ConjugateTranspose() * factorGramSchmidt.Q;
for (var i = 0; i < matrixQсtQ.RowCount; i++)
{
for (var j = 0; j < matrixQсtQ.ColumnCount; j++)
{
if (i == j)
{
Assert.AreApproximatelyEqual(matrixQсtQ[i, j].Real, 1.0, 1.0e-9);
Assert.AreApproximatelyEqual(matrixQсtQ[i, j].Imaginary, 0.0, 1.0e-9);
}
else
{
Assert.AreApproximatelyEqual(matrixQсtQ[i, j].Real, 0.0, 1.0e-9);
Assert.AreApproximatelyEqual(matrixQсtQ[i, j].Imaginary, 0.0, 1.0e-9);
}
}
}
}
[Test]
[Row(1)]
[Row(2)]
[Row(5)]
[Row(10)]
[Row(50)]
[Row(100)]
[MultipleAsserts]
public void CanSolveForRandomVector(int order)
{
var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order);
var matrixACopy = matrixA.Clone();
var factorGramSchmidt = matrixA.GramSchmidt();
var vectorb = MatrixLoader.GenerateRandomUserDefinedVector(order);
var resultx = factorGramSchmidt.Solve(vectorb);
Assert.AreEqual(matrixA.ColumnCount, resultx.Count);
var bReconstruct = matrixA * resultx;
// Check the reconstruction.
for (var i = 0; i < order; i++)
{
Assert.AreApproximatelyEqual(vectorb[i].Real, bReconstruct[i].Real, 1.0e-9);
Assert.AreApproximatelyEqual(vectorb[i].Imaginary, bReconstruct[i].Imaginary, 1.0e-9);
}
// Make sure A didn't change.
for (var i = 0; i < matrixA.RowCount; i++)
{
for (var j = 0; j < matrixA.ColumnCount; j++)
{
Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]);
}
}
}
[Test]
[Row(1)]
[Row(4)]
[Row(8)]
[Row(10)]
[Row(50)]
[Row(100)]
[MultipleAsserts]
public void CanSolveForRandomMatrix(int order)
{
var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order);
var matrixACopy = matrixA.Clone();
var factorGramSchmidt = matrixA.GramSchmidt();
var matrixB = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order);
var matrixX = factorGramSchmidt.Solve(matrixB);
// The solution X row dimension is equal to the column dimension of A
Assert.AreEqual(matrixA.ColumnCount, matrixX.RowCount);
// The solution X has the same number of columns as B
Assert.AreEqual(matrixB.ColumnCount, matrixX.ColumnCount);
var matrixBReconstruct = matrixA * matrixX;
// Check the reconstruction.
for (var i = 0; i < matrixB.RowCount; i++)
{
for (var j = 0; j < matrixB.ColumnCount; j++)
{
Assert.AreApproximatelyEqual(matrixB[i, j].Real, matrixBReconstruct[i, j].Real, 1.0e-9);
Assert.AreApproximatelyEqual(matrixB[i, j].Imaginary, matrixBReconstruct[i, j].Imaginary, 1.0e-9);
}
}
// Make sure A didn't change.
for (var i = 0; i < matrixA.RowCount; i++)
{
for (var j = 0; j < matrixA.ColumnCount; j++)
{
Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]);
}
}
}
[Test]
[Row(1)]
[Row(2)]
[Row(5)]
[Row(10)]
[Row(50)]
[Row(100)]
[MultipleAsserts]
public void CanSolveForRandomVectorWhenResultVectorGiven(int order)
{
var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order);
var matrixACopy = matrixA.Clone();
var factorGramSchmidt = matrixA.GramSchmidt();
var vectorb = MatrixLoader.GenerateRandomUserDefinedVector(order);
var vectorbCopy = vectorb.Clone();
var resultx = new UserDefinedVector(order);
factorGramSchmidt.Solve(vectorb,resultx);
Assert.AreEqual(vectorb.Count, resultx.Count);
var bReconstruct = matrixA * resultx;
// Check the reconstruction.
for (var i = 0; i < vectorb.Count; i++)
{
Assert.AreApproximatelyEqual(vectorb[i].Real, bReconstruct[i].Real, 1.0e-9);
Assert.AreApproximatelyEqual(vectorb[i].Imaginary, bReconstruct[i].Imaginary, 1.0e-9);
}
// Make sure A didn't change.
for (var i = 0; i < matrixA.RowCount; i++)
{
for (var j = 0; j < matrixA.ColumnCount; j++)
{
Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]);
}
}
// Make sure b didn't change.
for (var i = 0; i < vectorb.Count; i++)
{
Assert.AreEqual(vectorbCopy[i], vectorb[i]);
}
}
[Test]
[Row(1)]
[Row(4)]
[Row(8)]
[Row(10)]
[Row(50)]
[Row(100)]
[MultipleAsserts]
public void CanSolveForRandomMatrixWhenResultMatrixGiven(int order)
{
var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order);
var matrixACopy = matrixA.Clone();
var factorGramSchmidt = matrixA.GramSchmidt();
var matrixB = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order);
var matrixBCopy = matrixB.Clone();
var matrixX = new UserDefinedMatrix(order, order);
factorGramSchmidt.Solve(matrixB,matrixX);
// The solution X row dimension is equal to the column dimension of A
Assert.AreEqual(matrixA.ColumnCount, matrixX.RowCount);
// The solution X has the same number of columns as B
Assert.AreEqual(matrixB.ColumnCount, matrixX.ColumnCount);
var matrixBReconstruct = matrixA * matrixX;
// Check the reconstruction.
for (var i = 0; i < matrixB.RowCount; i++)
{
for (var j = 0; j < matrixB.ColumnCount; j++)
{
Assert.AreApproximatelyEqual(matrixB[i, j].Real, matrixBReconstruct[i, j].Real, 1.0e-9);
Assert.AreApproximatelyEqual(matrixB[i, j].Imaginary, matrixBReconstruct[i, j].Imaginary, 1.0e-9);
}
}
// Make sure A didn't change.
for (var i = 0; i < matrixA.RowCount; i++)
{
for (var j = 0; j < matrixA.ColumnCount; j++)
{
Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]);
}
}
// Make sure B didn't change.
for (var i = 0; i < matrixB.RowCount; i++)
{
for (var j = 0; j < matrixB.ColumnCount; j++)
{
Assert.AreEqual(matrixBCopy[i, j], matrixB[i, j]);
}
}
}
}
}

371
src/UnitTests/LinearAlgebraTests/Complex/Factorization/LUTests.cs

@ -0,0 +1,371 @@
// <copyright file="LUTests.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>
namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex.Factorization
{
using System.Numerics;
using LinearAlgebra.Generic.Factorization;
using MbUnit.Framework;
using LinearAlgebra.Complex;
public class LUTests
{
[Test]
[Row(1)]
[Row(10)]
[Row(100)]
public void CanFactorizeIdentity(int order)
{
var matrixI = DenseMatrix.Identity(order);
var factorLU = matrixI.LU();
// Check lower triangular part.
var matrixL = factorLU.L;
Assert.AreEqual(matrixI.RowCount, matrixL.RowCount);
Assert.AreEqual(matrixI.ColumnCount, matrixL.ColumnCount);
for (var i = 0; i < matrixL.RowCount; i++)
{
for (var j = 0; j < matrixL.ColumnCount; j++)
{
Assert.AreEqual(i == j ? Complex.One : Complex.Zero, matrixL[i, j]);
}
}
// Check upper triangular part.
var matrixU = factorLU.U;
Assert.AreEqual(matrixI.RowCount, matrixU.RowCount);
Assert.AreEqual(matrixI.ColumnCount, matrixU.ColumnCount);
for (var i = 0; i < matrixU.RowCount; i++)
{
for (var j = 0; j < matrixU.ColumnCount; j++)
{
Assert.AreEqual(i == j ? Complex.One : Complex.Zero, matrixU[i, j]);
}
}
}
[Test]
[Row(3,5)]
[Row(5,3)]
[ExpectedArgumentException]
public void LUFailsWithNonSquareMatrix(int row, int col)
{
var I = new DenseMatrix(row, col);
I.LU();
}
[Test]
[Row(1)]
[Row(10)]
[Row(100)]
public void IdentityDeterminantIsOne(int order)
{
var I = DenseMatrix.Identity(order);
var lu = I.LU();
Assert.AreEqual(Complex.One, lu.Determinant);
}
[Test]
[Row(1)]
[Row(2)]
[Row(5)]
[Row(10)]
[Row(50)]
[Row(100)]
[MultipleAsserts]
public void CanFactorizeRandomMatrix(int order)
{
var matrixX = MatrixLoader.GenerateRandomDenseMatrix(order, order);
var factorLU = matrixX.LU();
var matrixL = factorLU.L;
var matrixU = factorLU.U;
// Make sure the factors have the right dimensions.
Assert.AreEqual(order, matrixL.RowCount);
Assert.AreEqual(order, matrixL.ColumnCount);
Assert.AreEqual(order, matrixU.RowCount);
Assert.AreEqual(order, matrixU.ColumnCount);
// Make sure the L factor is lower triangular.
for (var i = 0; i < matrixL.RowCount; i++)
{
Assert.AreEqual(1.0, matrixL[i, i]);
for (var j = i+1; j < matrixL.ColumnCount; j++)
{
Assert.AreEqual(0.0, matrixL[i, j]);
}
}
// Make sure the U factor is upper triangular.
for (var i = 0; i < matrixL.RowCount; i++)
{
for (var j = 0; j < i; j++)
{
Assert.AreEqual(0.0, matrixU[i, j]);
}
}
// Make sure the LU factor times it's transpose is the original matrix.
var matrixXfromLU = matrixL * matrixU;
var permutationInverse = factorLU.P.Inverse();
matrixXfromLU.PermuteRows(permutationInverse);
for (var i = 0; i < matrixXfromLU.RowCount; i++)
{
for (var j = 0; j < matrixXfromLU.ColumnCount; j++)
{
Assert.AreApproximatelyEqual(matrixX[i, j].Real, matrixXfromLU[i, j].Real, 1.0e-9);
Assert.AreApproximatelyEqual(matrixX[i, j].Imaginary, matrixXfromLU[i, j].Imaginary, 1.0e-9);
}
}
}
[Test]
[Row(1)]
[Row(2)]
[Row(5)]
[Row(10)]
[Row(50)]
[Row(100)]
[MultipleAsserts]
public void CanSolveForRandomVector(int order)
{
var matrixA = MatrixLoader.GenerateRandomDenseMatrix(order, order);
var matrixACopy = matrixA.Clone();
var factorLU = matrixA.LU();
var vectorb = MatrixLoader.GenerateRandomDenseVector(order);
var resultx = factorLU.Solve(vectorb);
Assert.AreEqual(matrixA.ColumnCount, resultx.Count);
var bReconstruct = matrixA * resultx;
// Check the reconstruction.
for (var i = 0; i < order; i++)
{
Assert.AreApproximatelyEqual(vectorb[i].Real, bReconstruct[i].Real, 1.0e-9);
Assert.AreApproximatelyEqual(vectorb[i].Imaginary, bReconstruct[i].Imaginary, 1.0e-9);
}
// Make sure A didn't change.
for (var i = 0; i < matrixA.RowCount; i++)
{
for (var j = 0; j < matrixA.ColumnCount; j++)
{
Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]);
}
}
}
[Test]
[Row(1)]
[Row(4)]
[Row(8)]
[Row(10)]
[Row(50)]
[Row(100)]
[MultipleAsserts]
public void CanSolveForRandomMatrix(int order)
{
var matrixA = MatrixLoader.GenerateRandomDenseMatrix(order, order);
var matrixACopy = matrixA.Clone();
var factorLU = matrixA.LU();
var matrixB = MatrixLoader.GenerateRandomDenseMatrix(order, order);
var matrixX = factorLU.Solve(matrixB);
// The solution X row dimension is equal to the column dimension of A
Assert.AreEqual(matrixA.ColumnCount, matrixX.RowCount);
// The solution X has the same number of columns as B
Assert.AreEqual(matrixB.ColumnCount, matrixX.ColumnCount);
var matrixBReconstruct = matrixA * matrixX;
// Check the reconstruction.
for (var i = 0; i < matrixB.RowCount; i++)
{
for (var j = 0; j < matrixB.ColumnCount; j++)
{
Assert.AreApproximatelyEqual(matrixB[i, j].Real, matrixBReconstruct[i, j].Real, 1.0e-9);
Assert.AreApproximatelyEqual(matrixB[i, j].Imaginary, matrixBReconstruct[i, j].Imaginary, 1.0e-9);
}
}
// Make sure A didn't change.
for (var i = 0; i < matrixA.RowCount; i++)
{
for (var j = 0; j < matrixA.ColumnCount; j++)
{
Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]);
}
}
}
[Test]
[Row(1)]
[Row(2)]
[Row(5)]
[Row(10)]
[Row(50)]
[Row(100)]
[MultipleAsserts]
public void CanSolveForRandomVectorWhenResultVectorGiven(int order)
{
var matrixA = MatrixLoader.GenerateRandomDenseMatrix(order, order);
var matrixACopy = matrixA.Clone();
var factorLU = matrixA.LU();
var vectorb = MatrixLoader.GenerateRandomDenseVector(order);
var vectorbCopy = vectorb.Clone();
var resultx = new DenseVector(order);
factorLU.Solve(vectorb, resultx);
Assert.AreEqual(vectorb.Count, resultx.Count);
var bReconstruct = matrixA * resultx;
// Check the reconstruction.
for (var i = 0; i < vectorb.Count; i++)
{
Assert.AreApproximatelyEqual(vectorb[i].Real, bReconstruct[i].Real, 1.0e-9);
Assert.AreApproximatelyEqual(vectorb[i].Imaginary, bReconstruct[i].Imaginary, 1.0e-9);
}
// Make sure A didn't change.
for (var i = 0; i < matrixA.RowCount; i++)
{
for (var j = 0; j < matrixA.ColumnCount; j++)
{
Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]);
}
}
// Make sure b didn't change.
for (var i = 0; i < vectorb.Count; i++)
{
Assert.AreEqual(vectorbCopy[i], vectorb[i]);
}
}
[Test]
[Row(1)]
[Row(4)]
[Row(8)]
[Row(10)]
[Row(50)]
[Row(100)]
[MultipleAsserts]
public void CanSolveForRandomMatrixWhenResultMatrixGiven(int order)
{
var matrixA = MatrixLoader.GenerateRandomDenseMatrix(order, order);
var matrixACopy = matrixA.Clone();
var factorLU = matrixA.LU();
var matrixB = MatrixLoader.GenerateRandomDenseMatrix(order, order);
var matrixBCopy = matrixB.Clone();
var matrixX = new DenseMatrix(order, order);
factorLU.Solve(matrixB, matrixX);
// The solution X row dimension is equal to the column dimension of A
Assert.AreEqual(matrixA.ColumnCount, matrixX.RowCount);
// The solution X has the same number of columns as B
Assert.AreEqual(matrixB.ColumnCount, matrixX.ColumnCount);
var matrixBReconstruct = matrixA * matrixX;
// Check the reconstruction.
for (var i = 0; i < matrixB.RowCount; i++)
{
for (var j = 0; j < matrixB.ColumnCount; j++)
{
Assert.AreApproximatelyEqual(matrixB[i, j].Real, matrixBReconstruct[i, j].Real, 1.0e-9);
Assert.AreApproximatelyEqual(matrixB[i, j].Imaginary, matrixBReconstruct[i, j].Imaginary, 1.0e-9);
}
}
// Make sure A didn't change.
for (var i = 0; i < matrixA.RowCount; i++)
{
for (var j = 0; j < matrixA.ColumnCount; j++)
{
Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]);
}
}
// Make sure B didn't change.
for (var i = 0; i < matrixB.RowCount; i++)
{
for (var j = 0; j < matrixB.ColumnCount; j++)
{
Assert.AreEqual(matrixBCopy[i, j], matrixB[i, j]);
}
}
}
[Test]
[Row(1)]
[Row(4)]
[Row(8)]
[Row(10)]
[Row(50)]
[Row(100)]
[MultipleAsserts]
public void CanInverse(int order)
{
var matrixA = MatrixLoader.GenerateRandomDenseMatrix(order, order);
var matrixACopy = matrixA.Clone();
var factorLU = matrixA.LU();
var matrixAInverse = factorLU.Inverse();
// The inverse dimension is equal A
Assert.AreEqual(matrixAInverse.RowCount, matrixAInverse.RowCount);
Assert.AreEqual(matrixAInverse.ColumnCount, matrixAInverse.ColumnCount);
var matrixIdentity = matrixA * matrixAInverse;
// Make sure A didn't change.
for (var i = 0; i < matrixA.RowCount; i++)
{
for (var j = 0; j < matrixA.ColumnCount; j++)
{
Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]);
}
}
// Check if multiplication of A and AI produced identity matrix.
for (var i = 0; i < matrixIdentity.RowCount; i++)
{
Assert.AreApproximatelyEqual(matrixIdentity[i, i].Real, Complex.One.Real, 1.0e-9);
Assert.AreApproximatelyEqual(matrixIdentity[i, i].Imaginary, Complex.One.Imaginary, 1.0e-9);
}
}
}
}

343
src/UnitTests/LinearAlgebraTests/Complex/Factorization/QRTests.cs

@ -0,0 +1,343 @@
// <copyright file="QRTests.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>
namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex.Factorization
{
using System.Numerics;
using LinearAlgebra.Generic.Factorization;
using MbUnit.Framework;
using LinearAlgebra.Complex;
using LinearAlgebra.Complex.Factorization;
public class QRTests
{
[Test]
[ExpectedArgumentNullException]
public void ConstructorNull()
{
new DenseQR(null);
}
[Test]
[ExpectedArgumentException]
public void WideMatrixThrowsInvalidMatrixOperationException()
{
new DenseQR(new DenseMatrix(3, 4));
}
[Test]
[Row(1)]
[Row(10)]
[Row(100)]
public void CanFactorizeIdentity(int order)
{
var I = DenseMatrix.Identity(order);
var factorQR = I.QR();
Assert.AreEqual(I.RowCount, factorQR.R.RowCount);
Assert.AreEqual(I.ColumnCount, factorQR.R.ColumnCount);
for (var i = 0; i < factorQR.R.RowCount; i++)
{
for (var j = 0; j < factorQR.R.ColumnCount; j++)
{
if (i == j)
{
Assert.AreEqual(-Complex.One, factorQR.R[i, j]);
}
else
{
Assert.AreEqual(Complex.Zero, factorQR.R[i, j]);
}
}
}
}
[Test]
[Row(1)]
[Row(10)]
[Row(100)]
public void IdentityDeterminantIsOne(int order)
{
var I = DenseMatrix.Identity(order);
var factorQR = I.QR();
Assert.AreEqual(Complex.One, factorQR.Determinant);
}
[Test]
[Row(1,1)]
[Row(2,2)]
[Row(5,5)]
[Row(10,6)]
[Row(50,48)]
[Row(100,98)]
[MultipleAsserts]
public void CanFactorizeRandomMatrix(int row, int column)
{
var matrixA = MatrixLoader.GenerateRandomDenseMatrix(row, column);
var factorQR = matrixA.QR();
// Make sure the R has the right dimensions.
Assert.AreEqual(row, factorQR.R.RowCount);
Assert.AreEqual(column, factorQR.R.ColumnCount);
// Make sure the Q has the right dimensions.
Assert.AreEqual(row, factorQR.Q.RowCount);
Assert.AreEqual(row, factorQR.Q.ColumnCount);
// Make sure the R factor is upper triangular.
for (var i = 0; i < factorQR.R.RowCount; i++)
{
for (var j = 0; j < factorQR.R.ColumnCount; j++)
{
if (i > j)
{
Assert.AreEqual(Complex.Zero, factorQR.R[i, j]);
}
}
}
// Make sure the Q*R is the original matrix.
var matrixQfromR = factorQR.Q * factorQR.R;
for (var i = 0; i < matrixQfromR.RowCount; i++)
{
for (var j = 0; j < matrixQfromR.ColumnCount; j++)
{
Assert.AreApproximatelyEqual(matrixA[i, j].Real, matrixQfromR[i, j].Real, 1.0e-9);
Assert.AreApproximatelyEqual(matrixA[i, j].Imaginary, matrixQfromR[i, j].Imaginary, 1.0e-9);
}
}
// Make sure the Q is unitary --> (Q*)x(Q) = I
var matrixQсtQ = factorQR.Q.ConjugateTranspose() * factorQR.Q;
for (var i = 0; i < matrixQсtQ.RowCount; i++)
{
for (var j = 0; j < matrixQсtQ.ColumnCount; j++)
{
if (i == j)
{
Assert.AreApproximatelyEqual(matrixQсtQ[i, j].Real, 1.0, 1.0e-9);
Assert.AreApproximatelyEqual(matrixQсtQ[i, j].Imaginary, 0.0, 1.0e-9);
}
else
{
Assert.AreApproximatelyEqual(matrixQсtQ[i, j].Real, 0.0, 1.0e-9);
Assert.AreApproximatelyEqual(matrixQсtQ[i, j].Imaginary, 0.0, 1.0e-9);
}
}
}
}
[Test]
[Row(1)]
[Row(2)]
[Row(5)]
[Row(10)]
[Row(50)]
[Row(100)]
[MultipleAsserts]
public void CanSolveForRandomVector(int order)
{
var matrixA = MatrixLoader.GenerateRandomDenseMatrix(order, order);
var matrixACopy = matrixA.Clone();
var factorQR = matrixA.QR();
var vectorb = MatrixLoader.GenerateRandomDenseVector(order);
var resultx = factorQR.Solve(vectorb);
Assert.AreEqual(matrixA.ColumnCount, resultx.Count);
var bReconstruct = matrixA * resultx;
// Check the reconstruction.
for (var i = 0; i < order; i++)
{
Assert.AreApproximatelyEqual(vectorb[i].Real, bReconstruct[i].Real, 1.0e-9);
Assert.AreApproximatelyEqual(vectorb[i].Imaginary, bReconstruct[i].Imaginary, 1.0e-9);
}
// Make sure A didn't change.
for (var i = 0; i < matrixA.RowCount; i++)
{
for (var j = 0; j < matrixA.ColumnCount; j++)
{
Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]);
}
}
}
[Test]
[Row(1)]
[Row(4)]
[Row(8)]
[Row(10)]
[Row(50)]
[Row(100)]
[MultipleAsserts]
public void CanSolveForRandomMatrix(int order)
{
var matrixA = MatrixLoader.GenerateRandomDenseMatrix(order, order);
var matrixACopy = matrixA.Clone();
var factorQR = matrixA.QR();
var matrixB = MatrixLoader.GenerateRandomDenseMatrix(order, order);
var matrixX = factorQR.Solve(matrixB);
// The solution X row dimension is equal to the column dimension of A
Assert.AreEqual(matrixA.ColumnCount, matrixX.RowCount);
// The solution X has the same number of columns as B
Assert.AreEqual(matrixB.ColumnCount, matrixX.ColumnCount);
var matrixBReconstruct = matrixA * matrixX;
// Check the reconstruction.
for (var i = 0; i < matrixB.RowCount; i++)
{
for (var j = 0; j < matrixB.ColumnCount; j++)
{
Assert.AreApproximatelyEqual(matrixB[i, j].Real, matrixBReconstruct[i, j].Real, 1.0e-9);
Assert.AreApproximatelyEqual(matrixB[i, j].Imaginary, matrixBReconstruct[i, j].Imaginary, 1.0e-9);
}
}
// Make sure A didn't change.
for (var i = 0; i < matrixA.RowCount; i++)
{
for (var j = 0; j < matrixA.ColumnCount; j++)
{
Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]);
}
}
}
[Test]
[Row(1)]
[Row(2)]
[Row(5)]
[Row(10)]
[Row(50)]
[Row(100)]
[MultipleAsserts]
public void CanSolveForRandomVectorWhenResultVectorGiven(int order)
{
var matrixA = MatrixLoader.GenerateRandomDenseMatrix(order, order);
var matrixACopy = matrixA.Clone();
var factorQR = matrixA.QR();
var vectorb = MatrixLoader.GenerateRandomDenseVector(order);
var vectorbCopy = vectorb.Clone();
var resultx = new DenseVector(order);
factorQR.Solve(vectorb,resultx);
Assert.AreEqual(vectorb.Count, resultx.Count);
var bReconstruct = matrixA * resultx;
// Check the reconstruction.
for (var i = 0; i < vectorb.Count; i++)
{
Assert.AreApproximatelyEqual(vectorb[i].Real, bReconstruct[i].Real, 1.0e-9);
Assert.AreApproximatelyEqual(vectorb[i].Imaginary, bReconstruct[i].Imaginary, 1.0e-9);
}
// Make sure A didn't change.
for (var i = 0; i < matrixA.RowCount; i++)
{
for (var j = 0; j < matrixA.ColumnCount; j++)
{
Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]);
}
}
// Make sure b didn't change.
for (var i = 0; i < vectorb.Count; i++)
{
Assert.AreEqual(vectorbCopy[i], vectorb[i]);
}
}
[Test]
[Row(1)]
[Row(4)]
[Row(8)]
[Row(10)]
[Row(50)]
[Row(100)]
[MultipleAsserts]
public void CanSolveForRandomMatrixWhenResultMatrixGiven(int order)
{
var matrixA = MatrixLoader.GenerateRandomDenseMatrix(order, order);
var matrixACopy = matrixA.Clone();
var factorQR = matrixA.QR();
var matrixB = MatrixLoader.GenerateRandomDenseMatrix(order, order);
var matrixBCopy = matrixB.Clone();
var matrixX = new DenseMatrix(order, order);
factorQR.Solve(matrixB,matrixX);
// The solution X row dimension is equal to the column dimension of A
Assert.AreEqual(matrixA.ColumnCount, matrixX.RowCount);
// The solution X has the same number of columns as B
Assert.AreEqual(matrixB.ColumnCount, matrixX.ColumnCount);
var matrixBReconstruct = matrixA * matrixX;
// Check the reconstruction.
for (var i = 0; i < matrixB.RowCount; i++)
{
for (var j = 0; j < matrixB.ColumnCount; j++)
{
Assert.AreApproximatelyEqual(matrixB[i, j].Real, matrixBReconstruct[i, j].Real, 1.0e-9);
Assert.AreApproximatelyEqual(matrixB[i, j].Imaginary, matrixBReconstruct[i, j].Imaginary, 1.0e-9);
}
}
// Make sure A didn't change.
for (var i = 0; i < matrixA.RowCount; i++)
{
for (var j = 0; j < matrixA.ColumnCount; j++)
{
Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]);
}
}
// Make sure B didn't change.
for (var i = 0; i < matrixB.RowCount; i++)
{
for (var j = 0; j < matrixB.ColumnCount; j++)
{
Assert.AreEqual(matrixBCopy[i, j], matrixB[i, j]);
}
}
}
}
}

376
src/UnitTests/LinearAlgebraTests/Complex/Factorization/SvdTests.cs

@ -0,0 +1,376 @@
// <copyright file="SvdTests.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>
namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex.Factorization
{
using System;
using LinearAlgebra.Generic.Factorization;
using MbUnit.Framework;
using LinearAlgebra.Complex;
using LinearAlgebra.Complex.Factorization;
public class SvdTests
{
[Test]
[ExpectedArgumentNullException]
public void ConstructorNull()
{
new DenseSvd(null, true);
}
[Test]
[Row(1)]
[Row(10)]
[Row(100)]
public void CanFactorizeIdentity(int order)
{
var I = DenseMatrix.Identity(order);
var factorSvd = I.Svd(true);
Assert.AreEqual(I.RowCount, factorSvd.U().RowCount);
Assert.AreEqual(I.RowCount, factorSvd.U().ColumnCount);
Assert.AreEqual(I.ColumnCount, factorSvd.VT().RowCount);
Assert.AreEqual(I.ColumnCount, factorSvd.VT().ColumnCount);
Assert.AreEqual(I.RowCount, factorSvd.W().RowCount);
Assert.AreEqual(I.ColumnCount, factorSvd.W().ColumnCount);
for (var i = 0; i < factorSvd.W().RowCount; i++)
{
for (var j = 0; j < factorSvd.W().ColumnCount; j++)
{
Assert.AreEqual(i == j ? 1.0 : 0.0, factorSvd.W()[i, j]);
}
}
}
[Test]
[Row(1,1)]
[Row(2,2)]
[Row(5,5)]
[Row(10,6)]
[Row(48,52)]
[Row(100,93)]
[MultipleAsserts]
public void CanFactorizeRandomMatrix(int row, int column)
{
var matrixA = MatrixLoader.GenerateRandomDenseMatrix(row, column);
var factorSvd = matrixA.Svd(true);
// Make sure the U has the right dimensions.
Assert.AreEqual(row, factorSvd.U().RowCount);
Assert.AreEqual(row, factorSvd.U().ColumnCount);
// Make sure the VT has the right dimensions.
Assert.AreEqual(column, factorSvd.VT().RowCount);
Assert.AreEqual(column, factorSvd.VT().ColumnCount);
// Make sure the W has the right dimensions.
Assert.AreEqual(row, factorSvd.W().RowCount);
Assert.AreEqual(column, factorSvd.W().ColumnCount);
// Make sure the U*W*VT is the original matrix.
var matrix = factorSvd.U() * factorSvd.W() * factorSvd.VT();
for (var i = 0; i < matrix.RowCount; i++)
{
for (var j = 0; j < matrix.ColumnCount; j++)
{
Assert.AreApproximatelyEqual(matrixA[i, j].Real, matrix[i, j].Real, 1.0e-9);
Assert.AreApproximatelyEqual(matrixA[i, j].Imaginary, matrix[i, j].Imaginary, 1.0e-9);
}
}
}
[Test]
[Row(10, 8)]
[Row(48, 52)]
[Row(100, 93)]
[MultipleAsserts]
public void CheckRankOfNonSquare(int row, int column)
{
var matrixA = MatrixLoader.GenerateRandomDenseMatrix(row, column);
var factorSvd = matrixA.Svd(true);
var mn = Math.Min(row, column);
Assert.AreEqual(factorSvd.Rank, mn);
}
[Test]
[Row(1)]
[Row(2)]
[Row(5)]
[Row(9)]
[Row(50)]
[Row(90)]
[MultipleAsserts]
public void CheckRankSquare(int order)
{
var matrixA = MatrixLoader.GenerateRandomDenseMatrix(order, order);
var factorSvd = matrixA.Svd(true);
if (factorSvd.Determinant != 0)
{
Assert.AreEqual(factorSvd.Rank, order);
}
else
{
Assert.AreEqual(factorSvd.Rank, order - 1);
}
}
[Test]
[Row(10)]
[Row(50)]
[Row(100)]
[MultipleAsserts]
public void CheckRankOfSquareSingular(int order)
{
var matrixA = new DenseMatrix(order, order);
matrixA[0, 0] = 1;
matrixA[order - 1, order - 1] = 1;
for (var i = 1; i < order - 1; i++)
{
matrixA[i, i - 1] = 1;
matrixA[i, i + 1] = 1;
matrixA[i - 1, i] = 1;
matrixA[i + 1, i] = 1;
}
var factorSvd = matrixA.Svd(true);
Assert.AreEqual(factorSvd.Determinant, 0);
Assert.AreEqual(factorSvd.Rank, order - 1);
}
[Test]
[ExpectedException(typeof(InvalidOperationException))]
public void CannotSolveMatrixIfVectorsNotComputed()
{
var matrixA = MatrixLoader.GenerateRandomDenseMatrix(10, 10);
var factorSvd = matrixA.Svd(false);
var matrixB = MatrixLoader.GenerateRandomDenseMatrix(10, 10);
factorSvd.Solve(matrixB);
}
[Test]
[ExpectedException(typeof(InvalidOperationException))]
public void CannotSolveVectorIfVectorsNotComputed()
{
var matrixA = MatrixLoader.GenerateRandomDenseMatrix(10, 10);
var factorSvd = matrixA.Svd(false);
var vectorb = MatrixLoader.GenerateRandomDenseVector(10);
factorSvd.Solve(vectorb);
}
[Test]
[Row(1, 1)]
[Row(2, 2)]
[Row(5, 5)]
[Row(9, 10)]
[Row(50, 50)]
[Row(90, 100)]
[MultipleAsserts]
public void CanSolveForRandomVector(int row, int column)
{
var matrixA = MatrixLoader.GenerateRandomDenseMatrix(row, column);
var matrixACopy = matrixA.Clone();
var factorSvd = matrixA.Svd(true);
var vectorb = MatrixLoader.GenerateRandomDenseVector(row);
var resultx = factorSvd.Solve(vectorb);
Assert.AreEqual(matrixA.ColumnCount, resultx.Count);
var bReconstruct = matrixA * resultx;
// Check the reconstruction.
for (var i = 0; i < vectorb.Count; i++)
{
Assert.AreApproximatelyEqual(vectorb[i].Real, bReconstruct[i].Real, 1.0e-9);
Assert.AreApproximatelyEqual(vectorb[i].Imaginary, bReconstruct[i].Imaginary, 1.0e-9);
}
// Make sure A didn't change.
for (var i = 0; i < matrixA.RowCount; i++)
{
for (var j = 0; j < matrixA.ColumnCount; j++)
{
Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]);
}
}
}
[Test]
[Row(1, 1)]
[Row(4, 4)]
[Row(7, 8)]
[Row(10, 10)]
[Row(45, 50)]
[Row(80, 100)]
[MultipleAsserts]
public void CanSolveForRandomMatrix(int row, int count)
{
var matrixA = MatrixLoader.GenerateRandomDenseMatrix(row, count);
var matrixACopy = matrixA.Clone();
var factorSvd = matrixA.Svd(true);
var matrixB = MatrixLoader.GenerateRandomDenseMatrix(row, count);
var matrixX = factorSvd.Solve(matrixB);
// The solution X row dimension is equal to the column dimension of A
Assert.AreEqual(matrixA.ColumnCount, matrixX.RowCount);
// The solution X has the same number of columns as B
Assert.AreEqual(matrixB.ColumnCount, matrixX.ColumnCount);
var matrixBReconstruct = matrixA * matrixX;
// Check the reconstruction.
for (var i = 0; i < matrixB.RowCount; i++)
{
for (var j = 0; j < matrixB.ColumnCount; j++)
{
Assert.AreApproximatelyEqual(matrixB[i, j].Real, matrixBReconstruct[i, j].Real, 1.0e-9);
Assert.AreApproximatelyEqual(matrixB[i, j].Imaginary, matrixBReconstruct[i, j].Imaginary, 1.0e-9);
}
}
// Make sure A didn't change.
for (var i = 0; i < matrixA.RowCount; i++)
{
for (var j = 0; j < matrixA.ColumnCount; j++)
{
Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]);
}
}
}
[Test]
[Row(1, 1)]
[Row(2, 2)]
[Row(5, 5)]
[Row(9, 10)]
[Row(50, 50)]
[Row(90, 100)]
[MultipleAsserts]
public void CanSolveForRandomVectorWhenResultVectorGiven(int row, int column)
{
var matrixA = MatrixLoader.GenerateRandomDenseMatrix(row, column);
var matrixACopy = matrixA.Clone();
var factorSvd = matrixA.Svd(true);
var vectorb = MatrixLoader.GenerateRandomDenseVector(row);
var vectorbCopy = vectorb.Clone();
var resultx = new DenseVector(column);
factorSvd.Solve(vectorb,resultx);
var bReconstruct = matrixA * resultx;
// Check the reconstruction.
for (var i = 0; i < vectorb.Count; i++)
{
Assert.AreApproximatelyEqual(vectorb[i].Real, bReconstruct[i].Real, 1.0e-9);
Assert.AreApproximatelyEqual(vectorb[i].Imaginary, bReconstruct[i].Imaginary, 1.0e-9);
}
// Make sure A didn't change.
for (var i = 0; i < matrixA.RowCount; i++)
{
for (var j = 0; j < matrixA.ColumnCount; j++)
{
Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]);
}
}
// Make sure b didn't change.
for (var i = 0; i < vectorb.Count; i++)
{
Assert.AreEqual(vectorbCopy[i], vectorb[i]);
}
}
[Test]
[Row(1, 1)]
[Row(4, 4)]
[Row(7, 8)]
[Row(10, 10)]
[Row(45, 50)]
[Row(80, 100)]
[MultipleAsserts]
public void CanSolveForRandomMatrixWhenResultMatrixGiven(int row, int column)
{
var matrixA = MatrixLoader.GenerateRandomDenseMatrix(row, column);
var matrixACopy = matrixA.Clone();
var factorSvd = matrixA.Svd(true);
var matrixB = MatrixLoader.GenerateRandomDenseMatrix(row, column);
var matrixBCopy = matrixB.Clone();
var matrixX = new DenseMatrix(column, column);
factorSvd.Solve(matrixB,matrixX);
// The solution X row dimension is equal to the column dimension of A
Assert.AreEqual(matrixA.ColumnCount, matrixX.RowCount);
// The solution X has the same number of columns as B
Assert.AreEqual(matrixB.ColumnCount, matrixX.ColumnCount);
var matrixBReconstruct = matrixA * matrixX;
// Check the reconstruction.
for (var i = 0; i < matrixB.RowCount; i++)
{
for (var j = 0; j < matrixB.ColumnCount; j++)
{
Assert.AreApproximatelyEqual(matrixB[i, j].Real, matrixBReconstruct[i, j].Real, 1.0e-9);
Assert.AreApproximatelyEqual(matrixB[i, j].Imaginary, matrixBReconstruct[i, j].Imaginary, 1.0e-9);
}
}
// Make sure A didn't change.
for (var i = 0; i < matrixA.RowCount; i++)
{
for (var j = 0; j < matrixA.ColumnCount; j++)
{
Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]);
}
}
// Make sure B didn't change.
for (var i = 0; i < matrixB.RowCount; i++)
{
for (var j = 0; j < matrixB.ColumnCount; j++)
{
Assert.AreEqual(matrixBCopy[i, j], matrixB[i, j]);
}
}
}
}
}

304
src/UnitTests/LinearAlgebraTests/Complex/Factorization/UserCholeskyTests.cs

@ -0,0 +1,304 @@
// <copyright file="UserCholeskyTests.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>
namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex.Factorization
{
using LinearAlgebra.Generic.Factorization;
using MbUnit.Framework;
public class UserCholeskyTests
{
[Test]
[Row(1)]
[Row(10)]
[Row(100)]
public void CanFactorizeIdentity(int order)
{
var I = UserDefinedMatrix.Identity(order);
var factorC = I.Cholesky();
Assert.AreEqual(I.RowCount, factorC.Factor.RowCount);
Assert.AreEqual(I.ColumnCount, factorC.Factor.ColumnCount);
for (var i = 0; i < factorC.Factor.RowCount; i++)
{
for (var j = 0; j < factorC.Factor.ColumnCount; j++)
{
Assert.AreEqual(i == j ? 1.0 : 0.0, factorC.Factor[i, j]);
}
}
}
[Test]
[ExpectedArgumentException]
public void CholeskyFailsWithDiagonalNonPositiveDefiniteMatrix()
{
var I = UserDefinedMatrix.Identity(10);
I[3, 3] = -4.0;
I.Cholesky();
}
[Test]
[Row(3,5)]
[Row(5,3)]
[ExpectedArgumentException]
public void CholeskyFailsWithNonSquareMatrix(int row, int col)
{
var I = new UserDefinedMatrix(row, col);
I.Cholesky();
}
[Test]
[Row(1)]
[Row(10)]
[Row(100)]
public void IdentityDeterminantIsOne(int order)
{
var I = UserDefinedMatrix.Identity(order);
var factorC = I.Cholesky();
Assert.AreEqual(1.0, factorC.Determinant);
Assert.AreEqual(0.0, factorC.DeterminantLn);
}
[Test]
[Row(1)]
[Row(2)]
[Row(5)]
[Row(10)]
[Row(50)]
[Row(100)]
[MultipleAsserts]
public void CanFactorizeRandomMatrix(int order)
{
var matrixX = MatrixLoader.GenerateRandomPositiveDefiniteHermitianUserDefinedMatrix(order);
var chol = matrixX.Cholesky();
var factorC = chol.Factor;
// Make sure the Cholesky factor has the right dimensions.
Assert.AreEqual(order, factorC.RowCount);
Assert.AreEqual(order, factorC.ColumnCount);
// Make sure the Cholesky factor is lower triangular.
for (var i = 0; i < factorC.RowCount; i++)
{
for (var j = i+1; j < factorC.ColumnCount; j++)
{
Assert.AreEqual(0.0, factorC[i, j]);
}
}
// Make sure the cholesky factor times it's conjugate transpose is the original matrix.
var matrixXfromC = factorC * factorC.ConjugateTranspose();
for (var i = 0; i < matrixXfromC.RowCount; i++)
{
for (var j = 0; j < matrixXfromC.ColumnCount; j++)
{
Assert.AreApproximatelyEqual(matrixX[i,j].Real, matrixXfromC[i, j].Real, 1.0e-9);
Assert.AreApproximatelyEqual(matrixX[i, j].Imaginary, matrixXfromC[i, j].Imaginary, 1.0e-9);
}
}
}
[Test]
[Row(1)]
[Row(2)]
[Row(5)]
[Row(10)]
[Row(50)]
[Row(100)]
[MultipleAsserts]
public void CanSolveForRandomVector(int order)
{
var matrixA = MatrixLoader.GenerateRandomPositiveDefiniteHermitianUserDefinedMatrix(order);
var matrixACopy = matrixA.Clone();
var chol = matrixA.Cholesky();
var b = MatrixLoader.GenerateRandomUserDefinedVector(order);
var x = chol.Solve(b);
Assert.AreEqual(b.Count, x.Count);
var bReconstruct = matrixA * x;
// Check the reconstruction.
for (var i = 0; i < order; i++)
{
Assert.AreApproximatelyEqual(b[i].Real, bReconstruct[i].Real, 1.0e-11);
Assert.AreApproximatelyEqual(b[i].Imaginary, bReconstruct[i].Imaginary, 1.0e-9);
}
// Make sure A didn't change.
for (var i = 0; i < matrixA.RowCount; i++)
{
for (var j = 0; j < matrixA.ColumnCount; j++)
{
Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]);
}
}
}
[Test]
[Row(1,1)]
[Row(2,4)]
[Row(5,8)]
[Row(10,3)]
[Row(50,10)]
[Row(100,100)]
[MultipleAsserts]
public void CanSolveForRandomMatrix(int row, int col)
{
var matrixA = MatrixLoader.GenerateRandomPositiveDefiniteHermitianUserDefinedMatrix(row);
var matrixACopy = matrixA.Clone();
var chol = matrixA.Cholesky();
var matrixB = MatrixLoader.GenerateRandomUserDefinedMatrix(row, col);
var matrixX = chol.Solve(matrixB);
Assert.AreEqual(matrixB.RowCount, matrixX.RowCount);
Assert.AreEqual(matrixB.ColumnCount, matrixX.ColumnCount);
var matrixBReconstruct = matrixA * matrixX;
// Check the reconstruction.
for (var i = 0; i < matrixB.RowCount; i++)
{
for (var j = 0; j < matrixB.ColumnCount; j++)
{
Assert.AreApproximatelyEqual(matrixB[i, j].Real, matrixBReconstruct[i, j].Real, 1.0e-9);
Assert.AreApproximatelyEqual(matrixB[i, j].Imaginary, matrixBReconstruct[i, j].Imaginary, 1.0e-9);
}
}
// Make sure A didn't change.
for (var i = 0; i < matrixA.RowCount; i++)
{
for (var j = 0; j < matrixA.ColumnCount; j++)
{
Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]);
}
}
}
[Test]
[Row(1)]
[Row(2)]
[Row(5)]
[Row(10)]
[Row(50)]
[Row(100)]
[MultipleAsserts]
public void CanSolveForRandomVectorWhenResultVectorGiven(int order)
{
var matrixA = MatrixLoader.GenerateRandomPositiveDefiniteHermitianUserDefinedMatrix(order);
var matrixACopy = matrixA.Clone();
var chol = matrixA.Cholesky();
var b = MatrixLoader.GenerateRandomUserDefinedVector(order);
var bCopy = b.Clone();
var x = new UserDefinedVector(order);
chol.Solve(b, x);
Assert.AreEqual(b.Count, x.Count);
var bReconstruct = matrixA * x;
// Check the reconstruction.
for (var i = 0; i < order; i++)
{
Assert.AreApproximatelyEqual(b[i].Real, bReconstruct[i].Real, 1.0e-9);
Assert.AreApproximatelyEqual(b[i].Imaginary, bReconstruct[i].Imaginary, 1.0e-9);
}
// Make sure A didn't change.
for (var i = 0; i < matrixA.RowCount; i++)
{
for (var j = 0; j < matrixA.ColumnCount; j++)
{
Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]);
}
}
// Make sure b didn't change.
for (var i = 0; i < order; i++)
{
Assert.AreEqual(bCopy[i], b[i]);
}
}
[Test]
[Row(1, 1)]
[Row(2, 4)]
[Row(5, 8)]
[Row(10, 3)]
[Row(50, 10)]
[Row(100, 100)]
[MultipleAsserts]
public void CanSolveForRandomMatrixWhenResultMatrixGiven(int row, int col)
{
var matrixA = MatrixLoader.GenerateRandomPositiveDefiniteHermitianUserDefinedMatrix(row);
var matrixACopy = matrixA.Clone();
var chol = matrixA.Cholesky();
var matrixB = MatrixLoader.GenerateRandomUserDefinedMatrix(row, col);
var matrixBCopy = matrixB.Clone();
var matrixX = new UserDefinedMatrix(row, col);
chol.Solve(matrixB, matrixX);
Assert.AreEqual(matrixB.RowCount, matrixX.RowCount);
Assert.AreEqual(matrixB.ColumnCount, matrixX.ColumnCount);
var matrixBReconstruct = matrixA * matrixX;
// Check the reconstruction.
for (var i = 0; i < matrixB.RowCount; i++)
{
for (var j = 0; j < matrixB.ColumnCount; j++)
{
Assert.AreApproximatelyEqual(matrixB[i, j].Real, matrixBReconstruct[i, j].Real, 1.0e-9);
Assert.AreApproximatelyEqual(matrixB[i, j].Imaginary, matrixBReconstruct[i, j].Imaginary, 1.0e-9);
}
}
// Make sure A didn't change.
for (var i = 0; i < matrixA.RowCount; i++)
{
for (var j = 0; j < matrixA.ColumnCount; j++)
{
Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]);
}
}
// Make sure B didn't change.
for (var i = 0; i < matrixB.RowCount; i++)
{
for (var j = 0; j < matrixB.ColumnCount; j++)
{
Assert.AreEqual(matrixBCopy[i, j], matrixB[i, j]);
}
}
}
}
}

370
src/UnitTests/LinearAlgebraTests/Complex/Factorization/UserLUTests.cs

@ -0,0 +1,370 @@
// <copyright file="UserLUTests.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>
namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex.Factorization
{
using System.Numerics;
using LinearAlgebra.Generic.Factorization;
using MbUnit.Framework;
public class UserLUTests
{
[Test]
[Row(1)]
[Row(10)]
[Row(100)]
public void CanFactorizeIdentity(int order)
{
var matrixI = UserDefinedMatrix.Identity(order);
var factorLU = matrixI.LU();
// Check lower triangular part.
var matrixL = factorLU.L;
Assert.AreEqual(matrixI.RowCount, matrixL.RowCount);
Assert.AreEqual(matrixI.ColumnCount, matrixL.ColumnCount);
for (var i = 0; i < matrixL.RowCount; i++)
{
for (var j = 0; j < matrixL.ColumnCount; j++)
{
Assert.AreEqual(i == j ? Complex.One : Complex.Zero, matrixL[i, j]);
}
}
// Check upper triangular part.
var matrixU = factorLU.U;
Assert.AreEqual(matrixI.RowCount, matrixU.RowCount);
Assert.AreEqual(matrixI.ColumnCount, matrixU.ColumnCount);
for (var i = 0; i < matrixU.RowCount; i++)
{
for (var j = 0; j < matrixU.ColumnCount; j++)
{
Assert.AreEqual(i == j ? Complex.One : Complex.Zero, matrixU[i, j]);
}
}
}
[Test]
[Row(3,5)]
[Row(5,3)]
[ExpectedArgumentException]
public void LUFailsWithNonSquareMatrix(int row, int col)
{
var I = new UserDefinedMatrix(row, col);
I.LU();
}
[Test]
[Row(1)]
[Row(10)]
[Row(100)]
public void IdentityDeterminantIsOne(int order)
{
var I = UserDefinedMatrix.Identity(order);
var lu = I.LU();
Assert.AreEqual(Complex.One, lu.Determinant);
}
[Test]
[Row(1)]
[Row(2)]
[Row(5)]
[Row(10)]
[Row(50)]
[Row(100)]
[MultipleAsserts]
public void CanFactorizeRandomMatrix(int order)
{
var matrixX = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order);
var factorLU = matrixX.LU();
var matrixL = factorLU.L;
var matrixU = factorLU.U;
// Make sure the factors have the right dimensions.
Assert.AreEqual(order, matrixL.RowCount);
Assert.AreEqual(order, matrixL.ColumnCount);
Assert.AreEqual(order, matrixU.RowCount);
Assert.AreEqual(order, matrixU.ColumnCount);
// Make sure the L factor is lower triangular.
for (var i = 0; i < matrixL.RowCount; i++)
{
Assert.AreEqual(1.0, matrixL[i, i]);
for (var j = i+1; j < matrixL.ColumnCount; j++)
{
Assert.AreEqual(Complex.Zero, matrixL[i, j]);
}
}
// Make sure the U factor is upper triangular.
for (var i = 0; i < matrixL.RowCount; i++)
{
for (var j = 0; j < i; j++)
{
Assert.AreEqual(Complex.Zero, matrixU[i, j]);
}
}
// Make sure the LU factor times it's transpose is the original matrix.
var matrixXfromLU = matrixL * matrixU;
var permutationInverse = factorLU.P.Inverse();
matrixXfromLU.PermuteRows(permutationInverse);
for (var i = 0; i < matrixXfromLU.RowCount; i++)
{
for (var j = 0; j < matrixXfromLU.ColumnCount; j++)
{
Assert.AreApproximatelyEqual(matrixX[i, j].Real, matrixXfromLU[i, j].Real, 1.0e-9);
Assert.AreApproximatelyEqual(matrixX[i, j].Imaginary, matrixXfromLU[i, j].Imaginary, 1.0e-9);
}
}
}
[Test]
[Row(1)]
[Row(2)]
[Row(5)]
[Row(10)]
[Row(50)]
[Row(100)]
[MultipleAsserts]
public void CanSolveForRandomVector(int order)
{
var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order);
var matrixACopy = matrixA.Clone();
var factorLU = matrixA.LU();
var vectorb = MatrixLoader.GenerateRandomUserDefinedVector(order);
var resultx = factorLU.Solve(vectorb);
Assert.AreEqual(matrixA.ColumnCount, resultx.Count);
var bReconstruct = matrixA * resultx;
// Check the reconstruction.
for (var i = 0; i < order; i++)
{
Assert.AreApproximatelyEqual(vectorb[i].Real, bReconstruct[i].Real, 1.0e-9);
Assert.AreApproximatelyEqual(vectorb[i].Imaginary, bReconstruct[i].Imaginary, 1.0e-9);
}
// Make sure A didn't change.
for (var i = 0; i < matrixA.RowCount; i++)
{
for (var j = 0; j < matrixA.ColumnCount; j++)
{
Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]);
}
}
}
[Test]
[Row(1)]
[Row(4)]
[Row(8)]
[Row(10)]
[Row(50)]
[Row(100)]
[MultipleAsserts]
public void CanSolveForRandomMatrix(int order)
{
var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order);
var matrixACopy = matrixA.Clone();
var factorLU = matrixA.LU();
var matrixB = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order);
var matrixX = factorLU.Solve(matrixB);
// The solution X row dimension is equal to the column dimension of A
Assert.AreEqual(matrixA.ColumnCount, matrixX.RowCount);
// The solution X has the same number of columns as B
Assert.AreEqual(matrixB.ColumnCount, matrixX.ColumnCount);
var matrixBReconstruct = matrixA * matrixX;
// Check the reconstruction.
for (var i = 0; i < matrixB.RowCount; i++)
{
for (var j = 0; j < matrixB.ColumnCount; j++)
{
Assert.AreApproximatelyEqual(matrixB[i, j].Real, matrixBReconstruct[i, j].Real, 1.0e-9);
Assert.AreApproximatelyEqual(matrixB[i, j].Imaginary, matrixBReconstruct[i, j].Imaginary, 1.0e-9);
}
}
// Make sure A didn't change.
for (var i = 0; i < matrixA.RowCount; i++)
{
for (var j = 0; j < matrixA.ColumnCount; j++)
{
Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]);
}
}
}
[Test]
[Row(1)]
[Row(2)]
[Row(5)]
[Row(10)]
[Row(50)]
[Row(100)]
[MultipleAsserts]
public void CanSolveForRandomVectorWhenResultVectorGiven(int order)
{
var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order);
var matrixACopy = matrixA.Clone();
var factorLU = matrixA.LU();
var vectorb = MatrixLoader.GenerateRandomUserDefinedVector(order);
var vectorbCopy = vectorb.Clone();
var resultx = new UserDefinedVector(order);
factorLU.Solve(vectorb, resultx);
Assert.AreEqual(vectorb.Count, resultx.Count);
var bReconstruct = matrixA * resultx;
// Check the reconstruction.
for (var i = 0; i < vectorb.Count; i++)
{
Assert.AreApproximatelyEqual(vectorb[i].Real, bReconstruct[i].Real, 1.0e-9);
Assert.AreApproximatelyEqual(vectorb[i].Imaginary, bReconstruct[i].Imaginary, 1.0e-9);
}
// Make sure A didn't change.
for (var i = 0; i < matrixA.RowCount; i++)
{
for (var j = 0; j < matrixA.ColumnCount; j++)
{
Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]);
}
}
// Make sure b didn't change.
for (var i = 0; i < vectorb.Count; i++)
{
Assert.AreEqual(vectorbCopy[i], vectorb[i]);
}
}
[Test]
[Row(1)]
[Row(4)]
[Row(8)]
[Row(10)]
[Row(50)]
[Row(100)]
[MultipleAsserts]
public void CanSolveForRandomMatrixWhenResultMatrixGiven(int order)
{
var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order);
var matrixACopy = matrixA.Clone();
var factorLU = matrixA.LU();
var matrixB = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order);
var matrixBCopy = matrixB.Clone();
var matrixX = new UserDefinedMatrix(order, order);
factorLU.Solve(matrixB, matrixX);
// The solution X row dimension is equal to the column dimension of A
Assert.AreEqual(matrixA.ColumnCount, matrixX.RowCount);
// The solution X has the same number of columns as B
Assert.AreEqual(matrixB.ColumnCount, matrixX.ColumnCount);
var matrixBReconstruct = matrixA * matrixX;
// Check the reconstruction.
for (var i = 0; i < matrixB.RowCount; i++)
{
for (var j = 0; j < matrixB.ColumnCount; j++)
{
Assert.AreApproximatelyEqual(matrixB[i, j].Real, matrixBReconstruct[i, j].Real, 1.0e-9);
Assert.AreApproximatelyEqual(matrixB[i, j].Imaginary, matrixBReconstruct[i, j].Imaginary, 1.0e-9);
}
}
// Make sure A didn't change.
for (var i = 0; i < matrixA.RowCount; i++)
{
for (var j = 0; j < matrixA.ColumnCount; j++)
{
Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]);
}
}
// Make sure B didn't change.
for (var i = 0; i < matrixB.RowCount; i++)
{
for (var j = 0; j < matrixB.ColumnCount; j++)
{
Assert.AreEqual(matrixBCopy[i, j], matrixB[i, j]);
}
}
}
[Test]
[Row(1)]
[Row(4)]
[Row(8)]
[Row(10)]
[Row(50)]
[Row(100)]
[MultipleAsserts]
public void CanInverse(int order)
{
var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order);
var matrixACopy = matrixA.Clone();
var factorLU = matrixA.LU();
var matrixAInverse = factorLU.Inverse();
// The inverse dimension is equal A
Assert.AreEqual(matrixAInverse.RowCount, matrixAInverse.RowCount);
Assert.AreEqual(matrixAInverse.ColumnCount, matrixAInverse.ColumnCount);
var matrixIdentity = matrixA * matrixAInverse;
// Make sure A didn't change.
for (var i = 0; i < matrixA.RowCount; i++)
{
for (var j = 0; j < matrixA.ColumnCount; j++)
{
Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]);
}
}
// Check if multiplication of A and AI produced identity matrix.
for (var i = 0; i < matrixIdentity.RowCount; i++)
{
Assert.AreApproximatelyEqual(matrixIdentity[i, i].Real, Complex.One.Real, 1.0e-9);
Assert.AreApproximatelyEqual(matrixIdentity[i, i].Imaginary, Complex.One.Imaginary, 1.0e-9);
}
}
}
}

342
src/UnitTests/LinearAlgebraTests/Complex/Factorization/UserQRTests.cs

@ -0,0 +1,342 @@
// <copyright file="UserQRTests.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>
namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex.Factorization
{
using System.Numerics;
using LinearAlgebra.Generic.Factorization;
using MbUnit.Framework;
using LinearAlgebra.Complex.Factorization;
public class UserQRTests
{
[Test]
[ExpectedArgumentNullException]
public void ConstructorNull()
{
new UserQR(null);
}
[Test]
[ExpectedArgumentException]
public void WideMatrixThrowsInvalidMatrixOperationException()
{
new UserQR(new UserDefinedMatrix(3, 4));
}
[Test]
[Row(1)]
[Row(10)]
[Row(100)]
public void CanFactorizeIdentity(int order)
{
var I = UserDefinedMatrix.Identity(order);
var factorQR = I.QR();
Assert.AreEqual(I.RowCount, factorQR.R.RowCount);
Assert.AreEqual(I.ColumnCount, factorQR.R.ColumnCount);
for (var i = 0; i < factorQR.R.RowCount; i++)
{
for (var j = 0; j < factorQR.R.ColumnCount; j++)
{
if (i == j)
{
Assert.AreEqual(-1.0, factorQR.R[i, j]);
}
else
{
Assert.AreEqual(0.0, factorQR.R[i, j]);
}
}
}
}
[Test]
[Row(1)]
[Row(10)]
[Row(100)]
public void IdentityDeterminantIsOne(int order)
{
var I = UserDefinedMatrix.Identity(order);
var factorQR = I.QR();
Assert.AreEqual(1.0, factorQR.Determinant);
}
[Test]
[Row(1,1)]
[Row(2,2)]
[Row(5,5)]
[Row(10,6)]
[Row(50,48)]
[Row(100,98)]
[MultipleAsserts]
public void CanFactorizeRandomMatrix(int row, int column)
{
var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(row, column);
var factorQR = matrixA.QR();
// Make sure the R has the right dimensions.
Assert.AreEqual(row, factorQR.R.RowCount);
Assert.AreEqual(column, factorQR.R.ColumnCount);
// Make sure the Q has the right dimensions.
Assert.AreEqual(row, factorQR.Q.RowCount);
Assert.AreEqual(row, factorQR.Q.ColumnCount);
// Make sure the R factor is upper triangular.
for (var i = 0; i < factorQR.R.RowCount; i++)
{
for (var j = 0; j < factorQR.R.ColumnCount; j++)
{
if (i > j)
{
Assert.AreEqual(0.0, factorQR.R[i, j]);
}
}
}
// Make sure the Q*R is the original matrix.
var matrixQfromR = factorQR.Q * factorQR.R;
for (var i = 0; i < matrixQfromR.RowCount; i++)
{
for (var j = 0; j < matrixQfromR.ColumnCount; j++)
{
Assert.AreApproximatelyEqual(matrixA[i, j].Real, matrixQfromR[i, j].Real, 1.0e-9);
Assert.AreApproximatelyEqual(matrixA[i, j].Imaginary, matrixQfromR[i, j].Imaginary, 1.0e-9);
}
}
// Make sure the Q is unitary --> (Q*)x(Q) = I
var matrixQсtQ = factorQR.Q.ConjugateTranspose() * factorQR.Q;
for (var i = 0; i < matrixQсtQ.RowCount; i++)
{
for (var j = 0; j < matrixQсtQ.ColumnCount; j++)
{
if (i == j)
{
Assert.AreApproximatelyEqual(matrixQсtQ[i, j].Real, 1.0, 1.0e-9);
Assert.AreApproximatelyEqual(matrixQсtQ[i, j].Imaginary, 0.0, 1.0e-9);
}
else
{
Assert.AreApproximatelyEqual(matrixQсtQ[i, j].Real, 0.0, 1.0e-9);
Assert.AreApproximatelyEqual(matrixQсtQ[i, j].Imaginary, 0.0, 1.0e-9);
}
}
}
}
[Test]
[Row(1)]
[Row(2)]
[Row(5)]
[Row(10)]
[Row(50)]
[Row(100)]
[MultipleAsserts]
public void CanSolveForRandomVector(int order)
{
var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order);
var matrixACopy = matrixA.Clone();
var factorQR = matrixA.QR();
var vectorb = MatrixLoader.GenerateRandomUserDefinedVector(order);
var resultx = factorQR.Solve(vectorb);
Assert.AreEqual(matrixA.ColumnCount, resultx.Count);
var bReconstruct = matrixA * resultx;
// Check the reconstruction.
for (var i = 0; i < order; i++)
{
Assert.AreApproximatelyEqual(vectorb[i].Real, bReconstruct[i].Real, 1.0e-9);
Assert.AreApproximatelyEqual(vectorb[i].Imaginary, bReconstruct[i].Imaginary, 1.0e-9);
}
// Make sure A didn't change.
for (var i = 0; i < matrixA.RowCount; i++)
{
for (var j = 0; j < matrixA.ColumnCount; j++)
{
Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]);
}
}
}
[Test]
[Row(1)]
[Row(4)]
[Row(8)]
[Row(10)]
[Row(50)]
[Row(100)]
[MultipleAsserts]
public void CanSolveForRandomMatrix(int order)
{
var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order);
var matrixACopy = matrixA.Clone();
var factorQR = matrixA.QR();
var matrixB = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order);
var matrixX = factorQR.Solve(matrixB);
// The solution X row dimension is equal to the column dimension of A
Assert.AreEqual(matrixA.ColumnCount, matrixX.RowCount);
// The solution X has the same number of columns as B
Assert.AreEqual(matrixB.ColumnCount, matrixX.ColumnCount);
var matrixBReconstruct = matrixA * matrixX;
// Check the reconstruction.
for (var i = 0; i < matrixB.RowCount; i++)
{
for (var j = 0; j < matrixB.ColumnCount; j++)
{
Assert.AreApproximatelyEqual(matrixB[i, j].Real, matrixBReconstruct[i, j].Real, 1.0e-9);
Assert.AreApproximatelyEqual(matrixB[i, j].Imaginary, matrixBReconstruct[i, j].Imaginary, 1.0e-9);
}
}
// Make sure A didn't change.
for (var i = 0; i < matrixA.RowCount; i++)
{
for (var j = 0; j < matrixA.ColumnCount; j++)
{
Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]);
}
}
}
[Test]
[Row(1)]
[Row(2)]
[Row(5)]
[Row(10)]
[Row(50)]
[Row(100)]
[MultipleAsserts]
public void CanSolveForRandomVectorWhenResultVectorGiven(int order)
{
var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order);
var matrixACopy = matrixA.Clone();
var factorQR = matrixA.QR();
var vectorb = MatrixLoader.GenerateRandomUserDefinedVector(order);
var vectorbCopy = vectorb.Clone();
var resultx = new UserDefinedVector(order);
factorQR.Solve(vectorb,resultx);
Assert.AreEqual(vectorb.Count, resultx.Count);
var bReconstruct = matrixA * resultx;
// Check the reconstruction.
for (var i = 0; i < vectorb.Count; i++)
{
Assert.AreApproximatelyEqual(vectorb[i].Real, bReconstruct[i].Real, 1.0e-9);
Assert.AreApproximatelyEqual(vectorb[i].Imaginary, bReconstruct[i].Imaginary, 1.0e-9);
}
// Make sure A didn't change.
for (var i = 0; i < matrixA.RowCount; i++)
{
for (var j = 0; j < matrixA.ColumnCount; j++)
{
Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]);
}
}
// Make sure b didn't change.
for (var i = 0; i < vectorb.Count; i++)
{
Assert.AreEqual(vectorbCopy[i], vectorb[i]);
}
}
[Test]
[Row(1)]
[Row(4)]
[Row(8)]
[Row(10)]
[Row(50)]
[Row(100)]
[MultipleAsserts]
public void CanSolveForRandomMatrixWhenResultMatrixGiven(int order)
{
var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order);
var matrixACopy = matrixA.Clone();
var factorQR = matrixA.QR();
var matrixB = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order);
var matrixBCopy = matrixB.Clone();
var matrixX = new UserDefinedMatrix(order, order);
factorQR.Solve(matrixB,matrixX);
// The solution X row dimension is equal to the column dimension of A
Assert.AreEqual(matrixA.ColumnCount, matrixX.RowCount);
// The solution X has the same number of columns as B
Assert.AreEqual(matrixB.ColumnCount, matrixX.ColumnCount);
var matrixBReconstruct = matrixA * matrixX;
// Check the reconstruction.
for (var i = 0; i < matrixB.RowCount; i++)
{
for (var j = 0; j < matrixB.ColumnCount; j++)
{
Assert.AreApproximatelyEqual(matrixB[i, j].Real, matrixBReconstruct[i, j].Real, 1.0e-9);
Assert.AreApproximatelyEqual(matrixB[i, j].Imaginary, matrixBReconstruct[i, j].Imaginary, 1.0e-9);
}
}
// Make sure A didn't change.
for (var i = 0; i < matrixA.RowCount; i++)
{
for (var j = 0; j < matrixA.ColumnCount; j++)
{
Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]);
}
}
// Make sure B didn't change.
for (var i = 0; i < matrixB.RowCount; i++)
{
for (var j = 0; j < matrixB.ColumnCount; j++)
{
Assert.AreEqual(matrixBCopy[i, j], matrixB[i, j]);
}
}
}
}
}

376
src/UnitTests/LinearAlgebraTests/Complex/Factorization/UserSvdTests.cs

@ -0,0 +1,376 @@
// <copyright file="UserSvdTests.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>
namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex.Factorization
{
using System;
using System.Numerics;
using LinearAlgebra.Generic.Factorization;
using MbUnit.Framework;
using LinearAlgebra.Complex.Factorization;
public class UserSvdTests
{
[Test]
[ExpectedArgumentNullException]
public void ConstructorNull()
{
new UserSvd(null, true);
}
[Test]
[Row(1)]
[Row(10)]
[Row(100)]
public void CanFactorizeIdentity(int order)
{
var I = UserDefinedMatrix.Identity(order);
var factorSvd = I.Svd(true);
Assert.AreEqual(I.RowCount, factorSvd.U().RowCount);
Assert.AreEqual(I.RowCount, factorSvd.U().ColumnCount);
Assert.AreEqual(I.ColumnCount, factorSvd.VT().RowCount);
Assert.AreEqual(I.ColumnCount, factorSvd.VT().ColumnCount);
Assert.AreEqual(I.RowCount, factorSvd.W().RowCount);
Assert.AreEqual(I.ColumnCount, factorSvd.W().ColumnCount);
for (var i = 0; i < factorSvd.W().RowCount; i++)
{
for (var j = 0; j < factorSvd.W().ColumnCount; j++)
{
Assert.AreEqual(i == j ? Complex.One : Complex.Zero, factorSvd.W()[i, j]);
}
}
}
[Test]
[Row(1,1)]
[Row(2,2)]
[Row(5,5)]
[Row(10,6)]
[Row(48,52)]
[Row(100,93)]
[MultipleAsserts]
public void CanFactorizeRandomMatrix(int row, int column)
{
var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(row, column);
var factorSvd = matrixA.Svd(true);
// Make sure the U has the right dimensions.
Assert.AreEqual(row, factorSvd.U().RowCount);
Assert.AreEqual(row, factorSvd.U().ColumnCount);
// Make sure the VT has the right dimensions.
Assert.AreEqual(column, factorSvd.VT().RowCount);
Assert.AreEqual(column, factorSvd.VT().ColumnCount);
// Make sure the W has the right dimensions.
Assert.AreEqual(row, factorSvd.W().RowCount);
Assert.AreEqual(column, factorSvd.W().ColumnCount);
// Make sure the U*W*VT is the original matrix.
var matrix = factorSvd.U() * factorSvd.W() * factorSvd.VT();
for (var i = 0; i < matrix.RowCount; i++)
{
for (var j = 0; j < matrix.ColumnCount; j++)
{
Assert.AreApproximatelyEqual(matrixA[i, j].Real, matrix[i, j].Real, 1.0e-9);
Assert.AreApproximatelyEqual(matrixA[i, j].Imaginary, matrix[i, j].Imaginary, 1.0e-9);
}
}
}
[Test]
[Row(10, 8)]
[Row(48, 52)]
[Row(100, 93)]
[MultipleAsserts]
public void CheckRankOfNonSquare(int row, int column)
{
var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(row, column);
var factorSvd = matrixA.Svd(true);
var mn = Math.Min(row, column);
Assert.AreEqual(factorSvd.Rank, mn);
}
[Test]
[Row(1)]
[Row(2)]
[Row(5)]
[Row(9)]
[Row(50)]
[Row(90)]
[MultipleAsserts]
public void CheckRankSquare(int order)
{
var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order);
var factorSvd = matrixA.Svd(true);
if (factorSvd.Determinant != 0)
{
Assert.AreEqual(factorSvd.Rank, order);
}
else
{
Assert.AreEqual(factorSvd.Rank, order - 1);
}
}
[Test]
[Row(10)]
[Row(50)]
[Row(100)]
[MultipleAsserts]
public void CheckRankOfSquareSingular(int order)
{
var matrixA = new UserDefinedMatrix(order, order);
matrixA[0, 0] = 1;
matrixA[order - 1, order - 1] = 1;
for (var i = 1; i < order - 1; i++)
{
matrixA[i, i - 1] = 1;
matrixA[i, i + 1] = 1;
matrixA[i - 1, i] = 1;
matrixA[i + 1, i] = 1;
}
var factorSvd = matrixA.Svd(true);
Assert.AreEqual(factorSvd.Determinant, 0);
Assert.AreEqual(factorSvd.Rank, order - 1);
}
[Test]
[ExpectedException(typeof(InvalidOperationException))]
public void CannotSolveMatrixIfVectorsNotComputed()
{
var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(10, 10);
var factorSvd = matrixA.Svd(false);
var matrixB = MatrixLoader.GenerateRandomUserDefinedMatrix(10, 10);
factorSvd.Solve(matrixB);
}
[Test]
[ExpectedException(typeof(InvalidOperationException))]
public void CannotSolveVectorIfVectorsNotComputed()
{
var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(10, 10);
var factorSvd = matrixA.Svd(false);
var vectorb = MatrixLoader.GenerateRandomUserDefinedVector(10);
factorSvd.Solve(vectorb);
}
[Test]
[Row(1, 1)]
[Row(2, 2)]
[Row(5, 5)]
[Row(9, 10)]
[Row(50, 50)]
[Row(90, 100)]
[MultipleAsserts]
public void CanSolveForRandomVector(int row, int column)
{
var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(row, column);
var matrixACopy = matrixA.Clone();
var factorSvd = matrixA.Svd(true);
var vectorb = MatrixLoader.GenerateRandomUserDefinedVector(row);
var resultx = factorSvd.Solve(vectorb);
Assert.AreEqual(matrixA.ColumnCount, resultx.Count);
var bReconstruct = matrixA * resultx;
// Check the reconstruction.
for (var i = 0; i < vectorb.Count; i++)
{
Assert.AreApproximatelyEqual(vectorb[i].Real, bReconstruct[i].Real, 1.0e-9);
Assert.AreApproximatelyEqual(vectorb[i].Imaginary, bReconstruct[i].Imaginary, 1.0e-9);
}
// Make sure A didn't change.
for (var i = 0; i < matrixA.RowCount; i++)
{
for (var j = 0; j < matrixA.ColumnCount; j++)
{
Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]);
}
}
}
[Test]
[Row(1, 1)]
[Row(4, 4)]
[Row(7, 8)]
[Row(10, 10)]
[Row(45, 50)]
[Row(80, 100)]
[MultipleAsserts]
public void CanSolveForRandomMatrix(int row, int count)
{
var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(row, count);
var matrixACopy = matrixA.Clone();
var factorSvd = matrixA.Svd(true);
var matrixB = MatrixLoader.GenerateRandomUserDefinedMatrix(row, count);
var matrixX = factorSvd.Solve(matrixB);
// The solution X row dimension is equal to the column dimension of A
Assert.AreEqual(matrixA.ColumnCount, matrixX.RowCount);
// The solution X has the same number of columns as B
Assert.AreEqual(matrixB.ColumnCount, matrixX.ColumnCount);
var matrixBReconstruct = matrixA * matrixX;
// Check the reconstruction.
for (var i = 0; i < matrixB.RowCount; i++)
{
for (var j = 0; j < matrixB.ColumnCount; j++)
{
Assert.AreApproximatelyEqual(matrixB[i, j].Real, matrixBReconstruct[i, j].Real, 1.0e-9);
Assert.AreApproximatelyEqual(matrixB[i, j].Imaginary, matrixBReconstruct[i, j].Imaginary, 1.0e-9);
}
}
// Make sure A didn't change.
for (var i = 0; i < matrixA.RowCount; i++)
{
for (var j = 0; j < matrixA.ColumnCount; j++)
{
Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]);
}
}
}
[Test]
[Row(1, 1)]
[Row(2, 2)]
[Row(5, 5)]
[Row(9, 10)]
[Row(50, 50)]
[Row(90, 100)]
[MultipleAsserts]
public void CanSolveForRandomVectorWhenResultVectorGiven(int row, int column)
{
var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(row, column);
var matrixACopy = matrixA.Clone();
var factorSvd = matrixA.Svd(true);
var vectorb = MatrixLoader.GenerateRandomUserDefinedVector(row);
var vectorbCopy = vectorb.Clone();
var resultx = new UserDefinedVector(column);
factorSvd.Solve(vectorb,resultx);
var bReconstruct = matrixA * resultx;
// Check the reconstruction.
for (var i = 0; i < vectorb.Count; i++)
{
Assert.AreApproximatelyEqual(vectorb[i].Real, bReconstruct[i].Real, 1.0e-9);
Assert.AreApproximatelyEqual(vectorb[i].Imaginary, bReconstruct[i].Imaginary, 1.0e-9);
}
// Make sure A didn't change.
for (var i = 0; i < matrixA.RowCount; i++)
{
for (var j = 0; j < matrixA.ColumnCount; j++)
{
Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]);
}
}
// Make sure b didn't change.
for (var i = 0; i < vectorb.Count; i++)
{
Assert.AreEqual(vectorbCopy[i], vectorb[i]);
}
}
[Test]
[Row(1, 1)]
[Row(4, 4)]
[Row(7, 8)]
[Row(10, 10)]
[Row(45, 50)]
[Row(80, 100)]
[MultipleAsserts]
public void CanSolveForRandomMatrixWhenResultMatrixGiven(int row, int column)
{
var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(row, column);
var matrixACopy = matrixA.Clone();
var factorSvd = matrixA.Svd(true);
var matrixB = MatrixLoader.GenerateRandomUserDefinedMatrix(row, column);
var matrixBCopy = matrixB.Clone();
var matrixX = new UserDefinedMatrix(column, column);
factorSvd.Solve(matrixB,matrixX);
// The solution X row dimension is equal to the column dimension of A
Assert.AreEqual(matrixA.ColumnCount, matrixX.RowCount);
// The solution X has the same number of columns as B
Assert.AreEqual(matrixB.ColumnCount, matrixX.ColumnCount);
var matrixBReconstruct = matrixA * matrixX;
// Check the reconstruction.
for (var i = 0; i < matrixB.RowCount; i++)
{
for (var j = 0; j < matrixB.ColumnCount; j++)
{
Assert.AreApproximatelyEqual(matrixB[i, j].Real, matrixBReconstruct[i, j].Real, 1.0e-9);
Assert.AreApproximatelyEqual(matrixB[i, j].Imaginary, matrixBReconstruct[i, j].Imaginary, 1.0e-9);
}
}
// Make sure A didn't change.
for (var i = 0; i < matrixA.RowCount; i++)
{
for (var j = 0; j < matrixA.ColumnCount; j++)
{
Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]);
}
}
// Make sure B didn't change.
for (var i = 0; i < matrixB.RowCount; i++)
{
for (var j = 0; j < matrixB.ColumnCount; j++)
{
Assert.AreEqual(matrixBCopy[i, j], matrixB[i, j]);
}
}
}
}
}

184
src/UnitTests/LinearAlgebraTests/Complex/MatrixLoader.cs

@ -0,0 +1,184 @@
// <copyright file="MatrixLoader.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>
namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex
{
using System.Collections.Generic;
using System.Numerics;
using LinearAlgebra.Complex;
using LinearAlgebra.Generic;
using MbUnit.Framework;
public abstract class MatrixLoader
{
protected Dictionary<string, Complex[,]> TestData2D;
protected Dictionary<string, Matrix<Complex>> TestMatrices;
protected abstract Matrix<Complex> CreateMatrix(int rows, int columns);
protected abstract Matrix<Complex> CreateMatrix(Complex[,] data);
protected abstract Vector<Complex> CreateVector(int size);
protected abstract Vector<Complex> CreateVector(Complex[] data);
[SetUp]
public virtual void SetupMatrices()
{
TestData2D = new Dictionary<string, Complex[,]>
{
{ "Singular3x3", new[,] { { new Complex(1.0, 1), new Complex(1.0, 1), new Complex(2.0, 1) }, { new Complex(1.0, 1), new Complex(1.0, 1), new Complex(2.0, 1) }, { new Complex(1.0, 1), new Complex(1.0, 1), new Complex(2.0, 1) } } },
{ "Square3x3", new[,] { { new Complex(-1.1, 1), new Complex(-2.2, 1), new Complex(-3.3, 1) }, { Complex.Zero, new Complex(1.1, 1), new Complex(2.2, 1) }, { new Complex(-4.4, 1), new Complex(5.5, 1), new Complex(6.6, 1) } } },
{ "Square4x4", new[,] { { new Complex(-1.1, 1), new Complex(-2.2, 1), new Complex(-3.3, 1), new Complex(-4.4, 1) }, { Complex.Zero, new Complex(1.1, 1), new Complex(2.2, 1), new Complex(3.3, 1) }, { new Complex(1.0, 1), new Complex(2.1, 1), new Complex(6.2, 1), new Complex(4.3, 1) }, { new Complex(-4.4, 1), new Complex(5.5, 1), new Complex(6.6, 1), new Complex(-7.7, 1) } } },
{ "Singular4x4", new[,] { { new Complex(-1.1, 1), new Complex(-2.2, 1), new Complex(-3.3, 1), new Complex(-4.4, 1) }, { new Complex(-1.1, 1), new Complex(-2.2, 1), new Complex(-3.3, 1), new Complex(-4.4, 1) }, { new Complex(-1.1, 1), new Complex(-2.2, 1), new Complex(-3.3, 1), new Complex(-4.4, 1) }, { new Complex(-1.1, 1), new Complex(-2.2, 1), new Complex(-3.3, 1), new Complex(-4.4, 1) } } },
{ "Tall3x2", new[,] { { new Complex(-1.1, 1), new Complex(-2.2, 1) }, { Complex.Zero, new Complex(1.1, 1) }, { new Complex(-4.4, 1), new Complex(5.5, 1) } } },
{ "Wide2x3", new[,] { { new Complex(-1.1, 1), new Complex(-2.2, 1), new Complex(-3.3, 1) }, { Complex.Zero, new Complex(1.1, 1), new Complex(2.2, 1) } } },
};
TestMatrices = new Dictionary<string, Matrix<Complex>>();
foreach (var name in TestData2D.Keys)
{
TestMatrices.Add(name, CreateMatrix(TestData2D[name]));
}
}
public static Matrix<Complex> GenerateRandomDenseMatrix(int row, int col)
{
// Fill a matrix with standard random numbers.
var normal = new Distributions.Normal
{
RandomSource = new Random.MersenneTwister(1)
};
var matrixA = new DenseMatrix(row, col);
for (var i = 0; i < row; i++)
{
for (var j = 0; j < col; j++)
{
matrixA[i, j] = new Complex(normal.Sample(), normal.Sample());
}
}
// Generate a matrix which is positive definite.
return matrixA;
}
public static Matrix<Complex> GenerateRandomPositiveDefiniteHermitianDenseMatrix(int order)
{
// Fill a matrix with standard random numbers.
var normal = new Distributions.Normal
{
RandomSource = new Random.MersenneTwister(1)
};
var matrixA = new DenseMatrix(order);
for (var i = 0; i < order; i++)
{
for (var j = 0; j < order; j++)
{
matrixA[i, j] = new Complex(normal.Sample(), normal.Sample());
}
}
// Generate a Hermitian matrix which is positive definite.
return matrixA.ConjugateTranspose() * matrixA;
}
public static Vector<Complex> GenerateRandomDenseVector(int order)
{
// Fill a matrix with standard random numbers.
var normal = new Distributions.Normal
{
RandomSource = new Random.MersenneTwister(1)
};
var v = new DenseVector(order);
for (var i = 0; i < order; i++)
{
v[i] = new Complex(normal.Sample(), normal.Sample());
}
// Generate a matrix which is positive definite.
return v;
}
public static Matrix<Complex> GenerateRandomUserDefinedMatrix(int row, int col)
{
// Fill a matrix with standard random numbers.
var normal = new Distributions.Normal
{
RandomSource = new Random.MersenneTwister(1)
};
var matrixA = new UserDefinedMatrix(row, col);
for (var i = 0; i < row; i++)
{
for (var j = 0; j < col; j++)
{
matrixA[i, j] = new Complex(normal.Sample(), normal.Sample());
}
}
// Generate a matrix which is positive definite.
return matrixA;
}
public static Matrix<Complex> GenerateRandomPositiveDefiniteHermitianUserDefinedMatrix(int order)
{
// Fill a matrix with standard random numbers.
var normal = new Distributions.Normal
{
RandomSource = new Random.MersenneTwister(1)
};
var matrixA = new UserDefinedMatrix(order);
for (var i = 0; i < order; i++)
{
for (var j = 0; j < order; j++)
{
matrixA[i, j] = new Complex(normal.Sample(), normal.Sample());
}
}
// Generate a Hermitian matrix which is positive definite.
return matrixA.ConjugateTranspose() * matrixA;
}
public static Vector<Complex> GenerateRandomUserDefinedVector(int order)
{
// Fill a matrix with standard random numbers.
var normal = new Distributions.Normal
{
RandomSource = new Random.MersenneTwister(1)
};
var v = new UserDefinedVector(order);
for (var i = 0; i < order; i++)
{
v[i] = new Complex(normal.Sample(), normal.Sample());
}
// Generate a matrix which is positive definite.
return v;
}
}
}

894
src/UnitTests/LinearAlgebraTests/Complex/MatrixTests.Arithmetic.cs

@ -0,0 +1,894 @@
// <copyright file="MatrixTests.Arithmetic.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>
namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex
{
using System;
using System.Numerics;
using Distributions;
using LinearAlgebra.Complex;
using LinearAlgebra.Generic;
using MbUnit.Framework;
public abstract partial class MatrixTests
{
[Test]
[Row(1.0, 1.0)]
[Row(2.0, 1.0)]
[Row(2.2, 1.0)]
[MultipleAsserts]
public void CanMultiplyWithComplex(double real, double imaginary)
{
var value = new Complex(real, imaginary);
var matrix = TestMatrices["Singular3x3"];
var clone = matrix.Clone();
clone.Multiply(value);
for (var i = 0; i < matrix.RowCount; i++)
{
for (var j = 0; j < matrix.ColumnCount; j++)
{
AssertHelpers.AreEqual(matrix[i, j] * value, clone[i, j]);
}
}
}
[Test]
public void CanMultiplyWithVector()
{
var matrixA = TestMatrices["Singular3x3"];
var x = new DenseVector(new[] { new Complex(1, 1), new Complex(2, 1), new Complex(3, 1) });
var y = matrixA * x;
Assert.AreEqual(matrixA.RowCount, y.Count);
for (var i = 0; i < matrixA.RowCount; i++)
{
var ar = matrixA.Row(i);
var dot = ar * x;
AssertHelpers.AreEqual(dot, y[i]);
}
}
[Test]
public void CanMultiplyWithVectorIntoResult()
{
var matrixA = TestMatrices["Singular3x3"];
var x = new DenseVector(new[] { new Complex(1, 1), new Complex(2, 1), new Complex(3, 1) });
var y = new DenseVector(3);
matrixA.Multiply(x, y);
for (var i = 0; i < matrixA.RowCount; i++)
{
var ar = matrixA.Row(i);
var dot = ar * x;
AssertHelpers.AreEqual(dot, y[i]);
}
}
[Test]
public void CanMultiplyWithVectorIntoResultWhenUpdatingInputArgument()
{
var matrixA = TestMatrices["Singular3x3"];
var x = new DenseVector(new[] { new Complex(1, 1), new Complex(2, 1), new Complex(3, 1) });
var y = x;
matrixA.Multiply(x, x);
Assert.AreSame(y, x);
y = new DenseVector(new[] { new Complex(1, 1), new Complex(2, 1), new Complex(3, 1) });
for (var i = 0; i < matrixA.RowCount; i++)
{
var ar = matrixA.Row(i);
var dot = ar * y;
AssertHelpers.AreEqual(dot, x[i]);
}
}
[Test]
[ExpectedArgumentNullException]
public void MultiplyWithVectorIntoResultFailsWhenResultIsNull()
{
var matrixA = TestMatrices["Singular3x3"];
var x = new DenseVector(new[] { new Complex(1, 1), new Complex(2, 1), new Complex(3, 1) });
Vector<Complex> y = null;
matrixA.Multiply(x, y);
}
[Test]
[ExpectedArgumentException]
public void MultiplyWithVectorIntoResultFailsWhenResultIsTooLarge()
{
var matrixA = TestMatrices["Singular3x3"];
var x = new DenseVector(new[] { new Complex(1, 1), new Complex(2, 1), new Complex(3, 1) });
Vector<Complex> y = new DenseVector(4);
matrixA.Multiply(x, y);
}
[Test]
[Row(1.0, 1.0)]
[Row(2.0, 1.0)]
[Row(2.2, 1.0)]
[MultipleAsserts]
public void CanOperatorLeftMultiplyWithComplex(double real, double imaginary)
{
var value = new Complex(real, imaginary);
var matrix = TestMatrices["Singular3x3"];
var clone = matrix * value;
for (var i = 0; i < matrix.RowCount; i++)
{
for (var j = 0; j < matrix.ColumnCount; j++)
{
AssertHelpers.AreEqual(matrix[i, j] * value, clone[i, j]);
}
}
}
[Test]
[Row(1.0, 1.0)]
[Row(2.0, 1.0)]
[Row(2.2, 1.0)]
[MultipleAsserts]
public void CanOperatorRightMultiplyWithComplex(double real, double imaginary)
{
var value = new Complex(real, imaginary);
var matrix = TestMatrices["Singular3x3"];
var clone = matrix * value;
for (var i = 0; i < matrix.RowCount; i++)
{
for (var j = 0; j < matrix.ColumnCount; j++)
{
AssertHelpers.AreEqual(matrix[i, j] * value, clone[i, j]);
}
}
}
[Test]
[Row(1.0, 1.0)]
[Row(2.0, 1.0)]
[Row(2.2, 1.0)]
[MultipleAsserts]
public void CanMultiplyWithComplexIntoResult(double real, double imaginary)
{
var value = new Complex(real, imaginary);
var matrix = TestMatrices["Singular3x3"];
var result = matrix.Clone();
matrix.Multiply(value, result);
for (var i = 0; i < matrix.RowCount; i++)
{
for (var j = 0; j < matrix.ColumnCount; j++)
{
AssertHelpers.AreEqual(matrix[i, j] * value, result[i, j]);
}
}
}
[Test]
[ExpectedException(typeof(ArgumentNullException))]
public void MultiplyWithComplexIntoResultFailsWhenResultIsNull()
{
var matrix = TestMatrices["Singular3x3"];
Matrix<Complex> result = null;
matrix.Multiply(2.3, result);
}
[Test]
[ExpectedArgumentException]
public void MultiplyWithComplexFailsWhenResultHasMoreRows()
{
var matrix = TestMatrices["Singular3x3"];
var result = CreateMatrix(matrix.RowCount + 1, matrix.ColumnCount);
matrix.Multiply(2.3, result);
}
[Test]
[ExpectedArgumentException]
public void MultiplyWithComplexFailsWhenResultHasMoreColumns()
{
var matrix = TestMatrices["Singular3x3"];
var result = CreateMatrix(matrix.RowCount, matrix.ColumnCount + 1);
matrix.Multiply(2.3, result);
}
[Test]
[ExpectedException(typeof(ArgumentNullException))]
public void OperatorLeftMultiplyWithComplexFailsWhenMatrixIsNull()
{
Matrix<Complex> matrix = null;
var result = 2.3 * matrix;
}
[Test]
[ExpectedException(typeof(ArgumentNullException))]
public void OperatorRightMultiplyWithComplexFailsWhenMatrixIsNull()
{
Matrix<Complex> matrix = null;
var result = matrix * 2.3;
}
[Test]
[Row("Singular3x3", "Square3x3")]
[Row("Singular4x4", "Square4x4")]
public void CanAddMatrix(string mtxA, string mtxB)
{
var matrixA = TestMatrices[mtxA];
var matrixB = TestMatrices[mtxB];
var matrix = matrixA.Clone();
matrix.Add(matrixB);
for (var i = 0; i < matrix.RowCount; i++)
{
for (var j = 0; j < matrix.ColumnCount; j++)
{
AssertHelpers.AreEqual(matrix[i, j], matrixA[i, j] + matrixB[i, j]);
}
}
}
[Test]
[ExpectedException(typeof(ArgumentNullException))]
public void AddMatrixThrowsExceptionWhenArgumentIsNull()
{
var matrix = TestMatrices["Singular4x4"];
Matrix<Complex> other = null;
matrix.Add(other);
}
[Test]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void AddMatrixThrowsExceptionArgumentHasTooFewColumns()
{
var matrix = TestMatrices["Singular3x3"];
var other = TestMatrices["Tall3x2"];
matrix.Add(other);
}
[Test]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void AddMatrixThrowsExceptionArgumentHasTooFewRows()
{
var matrix = TestMatrices["Singular3x3"];
var other = TestMatrices["Wide2x3"];
matrix.Add(other);
}
[Test]
[Row("Singular3x3", "Square3x3")]
[Row("Singular4x4", "Square4x4")]
public void AddOperator(string mtxA, string mtxB)
{
var matrixA = TestMatrices[mtxA];
var matrixB = TestMatrices[mtxB];
var result = matrixA + matrixB;
for (var i = 0; i < matrixA.RowCount; i++)
{
for (var j = 0; j < matrixA.ColumnCount; j++)
{
AssertHelpers.AreEqual(result[i, j], matrixA[i, j] + matrixB[i, j]);
}
}
}
[Test]
[ExpectedException(typeof(ArgumentNullException))]
public void AddOperatorThrowsExceptionWhenLeftsideIsNull()
{
Matrix<Complex> matrix = null;
var other = TestMatrices["Singular3x3"];
var result = matrix + other;
}
[Test]
[ExpectedException(typeof(ArgumentNullException))]
public void AddOperatorThrowsExceptionWhenRightsideIsNull()
{
var matrix = TestMatrices["Singular3x3"];
Matrix<Complex> other = null;
var result = matrix + other;
}
[Test]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void AddOperatorThrowsExceptionWhenRightsideHasTooFewColumns()
{
var matrix = TestMatrices["Singular3x3"];
var other = TestMatrices["Tall3x2"];
var result = matrix + other;
}
[Test]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void AddOperatorThrowsExceptionWhenRightsideHasTooFewRows()
{
var matrix = TestMatrices["Singular3x3"];
var other = TestMatrices["Wide2x3"];
var result = matrix + other;
}
[Test]
[Row("Singular3x3", "Square3x3")]
[Row("Singular4x4", "Square4x4")]
public void CanSubtractMatrix(string mtxA, string mtxB)
{
var matrixA = TestMatrices[mtxA];
var matrixB = TestMatrices[mtxB];
var matrix = matrixA.Clone();
matrix.Subtract(matrixB);
for (var i = 0; i < matrix.RowCount; i++)
{
for (var j = 0; j < matrix.ColumnCount; j++)
{
AssertHelpers.AreEqual(matrix[i, j], matrixA[i, j] - matrixB[i, j]);
}
}
}
[Test]
[ExpectedException(typeof(ArgumentNullException))]
public void SubtractMatrixThrowsExceptionWhenRightSideIsNull()
{
var matrix = TestMatrices["Singular4x4"];
Matrix<Complex> other = null;
matrix.Subtract(other);
}
[Test]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void SubtractMatrixThrowsExceptionWhenRightSideHasTooFewColumns()
{
var matrix = TestMatrices["Singular3x3"];
var other = TestMatrices["Tall3x2"];
matrix.Subtract(other);
}
[Test]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void SubtractMatrixThrowsExceptionWhenRightSideHasTooFewRows()
{
var matrix = TestMatrices["Singular3x3"];
var other = TestMatrices["Wide2x3"];
matrix.Subtract(other);
}
[Test]
[Row("Singular3x3", "Square3x3")]
[Row("Singular4x4", "Square4x4")]
public void SubtractOperator(string mtxA, string mtxB)
{
var matrixA = TestMatrices[mtxA];
var matrixB = TestMatrices[mtxB];
var result = matrixA - matrixB;
for (var i = 0; i < matrixA.RowCount; i++)
{
for (var j = 0; j < matrixA.ColumnCount; j++)
{
AssertHelpers.AreEqual(result[i, j], matrixA[i, j] - matrixB[i, j]);
}
}
}
[Test]
[ExpectedException(typeof(ArgumentNullException))]
public void SubtractOperatorThrowsExceptionWhenLeftsideIsNull()
{
Matrix<Complex> matrix = null;
var other = TestMatrices["Singular3x3"];
var result = matrix - other;
}
[Test]
[ExpectedException(typeof(ArgumentNullException))]
public void SubtractOperatorThrowsExceptionWhenRightsideIsNull()
{
var matrix = TestMatrices["Singular3x3"];
Matrix<Complex> other = null;
var result = matrix - other;
}
[Test]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void SubtractOperatorThrowsExceptionWhenRightsideHasTooFewColumns()
{
var matrix = TestMatrices["Singular3x3"];
var other = TestMatrices["Tall3x2"];
var result = matrix - other;
}
[Test]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void SubtractOperatorThrowsExceptionWhenRightsideHasTooFewRows()
{
var matrix = TestMatrices["Singular3x3"];
var other = TestMatrices["Wide2x3"];
var result = matrix - other;
}
[Test]
[Row("Singular3x3", "Square3x3")]
[Row("Singular4x4", "Square4x4")]
[Row("Wide2x3", "Square3x3")]
[Row("Wide2x3", "Tall3x2")]
[Row("Tall3x2", "Wide2x3")]
[MultipleAsserts]
public void CanMultiplyMatrixWithMatrix(string nameA, string nameB)
{
var matrixA = TestMatrices[nameA];
var matrixB = TestMatrices[nameB];
var matrixC = matrixA * matrixB;
Assert.AreEqual(matrixC.RowCount, matrixA.RowCount);
Assert.AreEqual(matrixC.ColumnCount, matrixB.ColumnCount);
for (var i = 0; i < matrixC.RowCount; i++)
{
for (var j = 0; j < matrixC.ColumnCount; j++)
{
AssertHelpers.AlmostEqual(matrixA.Row(i) * matrixB.Column(j), matrixC[i, j], 15);
}
}
}
[Test]
[Row("Singular3x3")]
[Row("Singular4x4")]
[Row("Wide2x3")]
[Row("Tall3x2")]
[MultipleAsserts]
public void CanTransposeAndMultiplyMatrixWithMatrix(string nameA)
{
var matrixA = TestMatrices[nameA];
var matrixB = TestMatrices[nameA];
var matrixC = matrixA.TransposeAndMultiply(matrixB);
Assert.AreEqual(matrixC.RowCount, matrixA.RowCount);
Assert.AreEqual(matrixC.ColumnCount, matrixB.RowCount);
for (var i = 0; i < matrixC.RowCount; i++)
{
for (var j = 0; j < matrixC.ColumnCount; j++)
{
AssertHelpers.AlmostEqual(matrixA.Row(i) * matrixB.Row(j), matrixC[i, j], 15);
}
}
}
[Test]
[ExpectedException(typeof(ArgumentException))]
public void TransposeAndMultiplyMatrixMatrixFailsWhenSizesAreIncompatible()
{
var matrix = TestMatrices["Singular3x3"];
var other = TestMatrices["Tall3x2"];
var result = matrix.TransposeAndMultiply(other);
}
[Test]
[ExpectedException(typeof(ArgumentNullException))]
public void TransposeAndMultiplyMatrixMatrixFailsWhenRightArgumentIsNull()
{
var matrix = TestMatrices["Wide2x3"];
Matrix<Complex> other = null;
var result = matrix.TransposeAndMultiply(other);
}
[Test]
[Row("Singular3x3")]
[Row("Singular4x4")]
[Row("Wide2x3")]
[Row("Wide2x3")]
[Row("Tall3x2")]
[MultipleAsserts]
public void CanTransposeAndMultiplyMatrixWithMatrixIntoResult(string nameA)
{
var matrixA = TestMatrices[nameA];
var matrixB = TestMatrices[nameA];
var matrixC = CreateMatrix(matrixA.RowCount, matrixB.RowCount);
matrixA.TransposeAndMultiply(matrixB, matrixC);
Assert.AreEqual(matrixC.RowCount, matrixA.RowCount);
Assert.AreEqual(matrixC.ColumnCount, matrixB.RowCount);
for (var i = 0; i < matrixC.RowCount; i++)
{
for (var j = 0; j < matrixC.ColumnCount; j++)
{
AssertHelpers.AlmostEqual(matrixA.Row(i) * matrixB.Row(j), matrixC[i, j], 15);
}
}
}
[Test]
[ExpectedException(typeof(ArgumentException))]
public void MultiplyMatrixMatrixFailsWhenSizesAreIncompatible()
{
var matrix = TestMatrices["Singular3x3"];
var other = TestMatrices["Wide2x3"];
var result = matrix * other;
}
[Test]
[ExpectedException(typeof(ArgumentNullException))]
public void MultiplyMatrixMatrixFailsWhenLeftArgumentIsNull()
{
Matrix<Complex> matrix = null;
var other = TestMatrices["Wide2x3"];
var result = matrix * other;
}
[Test]
[ExpectedException(typeof(ArgumentNullException))]
public void MultiplyMatrixMatrixFailsWhenRightArgumentIsNull()
{
var matrix = TestMatrices["Wide2x3"];
Matrix<Complex> other = null;
var result = matrix * other;
}
[Test]
[Row("Singular3x3", "Square3x3")]
[Row("Singular4x4", "Square4x4")]
[Row("Wide2x3", "Square3x3")]
[Row("Wide2x3", "Tall3x2")]
[Row("Tall3x2", "Wide2x3")]
[MultipleAsserts]
public virtual void CanMultiplyMatrixWithMatrixIntoResult(string nameA, string nameB)
{
var matrixA = TestMatrices[nameA];
var matrixB = TestMatrices[nameB];
var matrixC = CreateMatrix(matrixA.RowCount, matrixB.ColumnCount);
matrixA.Multiply(matrixB, matrixC);
Assert.AreEqual(matrixC.RowCount, matrixA.RowCount);
Assert.AreEqual(matrixC.ColumnCount, matrixB.ColumnCount);
for (var i = 0; i < matrixC.RowCount; i++)
{
for (var j = 0; j < matrixC.ColumnCount; j++)
{
AssertHelpers.AlmostEqual(matrixA.Row(i) * matrixB.Column(j), matrixC[i, j], 15);
}
}
}
[Test]
[Row("Singular3x3")]
[Row("Square3x3")]
[Row("Square4x4")]
[Row("Tall3x2")]
[Row("Wide2x3")]
[MultipleAsserts]
public void CanNegate(string name)
{
var matrix = TestMatrices[name];
var copy = matrix.Clone();
copy.Negate();
for (var i = 0; i < matrix.RowCount; i++)
{
for (var j = 0; j < matrix.ColumnCount; j++)
{
AssertHelpers.AreEqual(-matrix[i, j], copy[i, j]);
}
}
}
[Test]
[Row("Singular3x3")]
[Row("Square3x3")]
[Row("Square4x4")]
[Row("Tall3x2")]
[Row("Wide2x3")]
[MultipleAsserts]
public void CanNegateIntoResult(string name)
{
var matrix = TestMatrices[name];
var copy = matrix.Clone();
matrix.Negate(copy);
for (var i = 0; i < matrix.RowCount; i++)
{
for (var j = 0; j < matrix.ColumnCount; j++)
{
AssertHelpers.AreEqual(-matrix[i, j], copy[i, j]);
}
}
}
[Test]
[ExpectedArgumentNullException]
public void NegateIntoResultFailsWhenResultIsNull()
{
var matrix = TestMatrices["Singular3x3"];
Matrix<Complex> copy = null;
matrix.Negate(copy);
}
[Test]
[ExpectedArgumentException]
public void NegateIntoResultFailsWhenResultHasMoreRows()
{
var matrix = TestMatrices["Singular3x3"];
var target = CreateMatrix(matrix.RowCount + 1, matrix.ColumnCount);
matrix.Negate(target);
}
[Test]
[ExpectedArgumentException]
public void NegateIntoResultFailsWhenResultHasMoreColumns()
{
var matrix = TestMatrices["Singular3x3"];
var target = CreateMatrix(matrix.RowCount + 1, matrix.ColumnCount);
matrix.Negate(target);
}
[Test]
public void KroneckerProduct()
{
var matrixA = TestMatrices["Wide2x3"];
var matrixB = TestMatrices["Square3x3"];
var result = CreateMatrix(matrixA.RowCount * matrixB.RowCount, matrixA.ColumnCount * matrixB.ColumnCount);
matrixA.KroneckerProduct(matrixB, result);
for (var i = 0; i < matrixA.RowCount; i++)
{
for (var j = 0; j < matrixA.ColumnCount; j++)
{
for (var ii = 0; ii < matrixB.RowCount; ii++)
{
for (var jj = 0; jj < matrixB.ColumnCount; jj++)
{
AssertHelpers.AreEqual(result[i * matrixB.RowCount + ii, j * matrixB.ColumnCount + jj], matrixA[i, j] * matrixB[ii, jj]);
}
}
}
}
}
[Test]
public void KroneckerProductResult()
{
var matrixA = TestMatrices["Wide2x3"];
var matrixB = TestMatrices["Square3x3"];
var result = matrixA.KroneckerProduct(matrixB);
for (var i = 0; i < matrixA.RowCount; i++)
{
for (var j = 0; j < matrixA.ColumnCount; j++)
{
for (var ii = 0; ii < matrixB.RowCount; ii++)
{
for (var jj = 0; jj < matrixB.ColumnCount; jj++)
{
AssertHelpers.AreEqual(result[i * matrixB.RowCount + ii, j * matrixB.ColumnCount + jj], matrixA[i, j] * matrixB[ii, jj]);
}
}
}
}
}
[Test]
[Row(1)]
[Row(2)]
[Row(-4, ExpectedException = typeof(ArgumentOutOfRangeException))]
public void NormalizeColumns(int pValue)
{
var matrix = TestMatrices["Square4x4"];
var result = matrix.NormalizeColumns(pValue);
for (var j = 0; j < result.ColumnCount; j++)
{
var col = result.Column(j);
AssertHelpers.AlmostEqual(Complex.One, col.Norm(pValue), 12);
}
}
[Test]
[Row(1)]
[Row(2)]
[Row(-3, ExpectedException = typeof(ArgumentOutOfRangeException))]
public void NormalizeRows(int pValue)
{
var matrix = TestMatrices["Square4x4"].NormalizeRows(pValue);
for (var i = 0; i < matrix.RowCount; i++)
{
var row = matrix.Row(i);
AssertHelpers.AlmostEqual(Complex.One, row.Norm(pValue), 12);
}
}
[Test]
public void PointwiseMultiplyResult()
{
foreach (var data in TestMatrices.Values)
{
var other = data.Clone();
var result = data.Clone();
data.PointwiseMultiply(other, result);
for (var i = 0; i < data.RowCount; i++)
{
for (var j = 0; j < data.ColumnCount; j++)
{
AssertHelpers.AreEqual(data[i, j] * other[i, j], result[i, j]);
}
}
result = data.PointwiseMultiply(other);
for (var i = 0; i < data.RowCount; i++)
{
for (var j = 0; j < data.ColumnCount; j++)
{
AssertHelpers.AreEqual(data[i, j] * other[i, j], result[i, j]);
}
}
}
}
[Test]
[ExpectedException(typeof(ArgumentNullException))]
public void PointwiseMultiplyWithNullOtherShouldThrowException()
{
var matrix = TestMatrices["Wide2x3"];
Matrix<Complex> other = null;
var result = matrix.Clone();
matrix.PointwiseMultiply(other, result);
}
[Test]
[ExpectedException(typeof(ArgumentNullException))]
public void PointwiseMultiplyWithResultNullShouldThrowException()
{
var matrix = TestMatrices["Wide2x3"];
var other = matrix.Clone();
matrix.PointwiseMultiply(other, null);
}
[Test]
[ExpectedException(typeof(ArgumentException))]
public void PointwiseMultiplyWithInvalidOtherMatrixDimensionsShouldThrowException()
{
var matrix = TestMatrices["Wide2x3"];
var other = CreateMatrix(matrix.RowCount + 1, matrix.ColumnCount);
var result = matrix.Clone();
matrix.PointwiseMultiply(other, result);
}
[Test]
[ExpectedException(typeof(ArgumentException))]
public void PointwiseMultiplyWithInvalidResultMatrixDimensionsShouldThrowException()
{
var matrix = TestMatrices["Wide2x3"];
var other = matrix.Clone();
var result = CreateMatrix(matrix.RowCount + 1, matrix.ColumnCount);
matrix.PointwiseMultiply(other, result);
}
[Test]
public virtual void PointwiseDivideResult()
{
foreach (var data in TestMatrices.Values)
{
var other = data.Clone();
var result = data.Clone();
data.PointwiseDivide(other, result);
for (var i = 0; i < data.RowCount; i++)
{
for (var j = 0; j < data.ColumnCount; j++)
{
AssertHelpers.AreEqual(data[i, j] / other[i, j], result[i, j]);
}
}
result = data.PointwiseDivide(other);
for (var i = 0; i < data.RowCount; i++)
{
for (var j = 0; j < data.ColumnCount; j++)
{
AssertHelpers.AreEqual(data[i, j] / other[i, j], result[i, j]);
}
}
}
}
[Test]
[ExpectedException(typeof(ArgumentNullException))]
public void PointwiseDivideWithNullOtherShouldThrowException()
{
var matrix = TestMatrices["Wide2x3"];
Matrix<Complex> other = null;
var result = matrix.Clone();
matrix.PointwiseDivide(other, result);
}
[Test]
[ExpectedException(typeof(ArgumentNullException))]
public void PointwiseDivideWithResultNullShouldThrowException()
{
var matrix = TestMatrices["Wide2x3"];
var other = matrix.Clone();
matrix.PointwiseDivide(other, null);
}
[Test]
[ExpectedException(typeof(ArgumentException))]
public void PointwiseDivideWithInvalidOtherMatrixDimensionsShouldThrowException()
{
var matrix = TestMatrices["Wide2x3"];
var other = CreateMatrix(matrix.RowCount + 1, matrix.ColumnCount);
var result = matrix.Clone();
matrix.PointwiseDivide(other, result);
}
[Test]
[ExpectedException(typeof(ArgumentException))]
public void PointwiseDivideWithInvalidResultMatrixDimensionsShouldThrowException()
{
var matrix = TestMatrices["Wide2x3"];
var other = matrix.Clone();
var result = CreateMatrix(matrix.RowCount + 1, matrix.ColumnCount);
matrix.PointwiseDivide(other, result);
}
[Test]
[Row(0, ExpectedException = typeof(ArgumentException))]
[Row(-2, ExpectedException = typeof(ArgumentException))]
public void RandomWithNonPositiveNumberOfRowsShouldThrowException(int numberOfRows)
{
var matrix = CreateMatrix(2, 3);
matrix = matrix.Random(numberOfRows, 4, new ContinuousUniform());
}
[Test]
[Row(0, ExpectedException = typeof(ArgumentException))]
[Row(-2, ExpectedException = typeof(ArgumentException))]
public void RandomWithNonPositiveNumberOfRowsShouldThrowException2(int numberOfRows)
{
var matrix = CreateMatrix(2, 3);
matrix = matrix.Random(numberOfRows, 4, new DiscreteUniform(0, 2));
}
[Test]
public void Trace()
{
var matrix = TestMatrices["Square3x3"];
var trace = matrix.Trace();
AssertHelpers.AreEqual(new Complex(6.6, 3), trace);
}
[Test]
[ExpectedArgumentException]
public void TraceOfNonSquareMatrixShouldThrowException()
{
var matrix = TestMatrices["Wide2x3"];
var trace = matrix.Trace();
}
}
}

1499
src/UnitTests/LinearAlgebraTests/Complex/MatrixTests.cs

File diff suppressed because it is too large

268
src/UnitTests/LinearAlgebraTests/Complex/Solvers/Iterative/BiCgStabTest.cs

@ -0,0 +1,268 @@
namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex.Solvers.Iterative
{
using System.Numerics;
using LinearAlgebra.Complex;
using LinearAlgebra.Complex.Solvers;
using LinearAlgebra.Complex.Solvers.Iterative;
using LinearAlgebra.Complex.Solvers.StopCriterium;
using LinearAlgebra.Generic;
using LinearAlgebra.Generic.Solvers.Status;
using LinearAlgebra.Generic.Solvers.StopCriterium;
using MbUnit.Framework;
[TestFixture]
public class BiCgStabTest
{
private const double ConvergenceBoundary = 1e-10;
private const int MaximumIterations = 1000;
[Test]
[ExpectedArgumentException]
public void SolveWideMatrix()
{
var matrix = new SparseMatrix(2, 3);
Vector<Complex> input = new DenseVector(2);
var solver = new BiCgStab();
solver.Solve(matrix, input);
}
[Test]
[ExpectedArgumentException]
public void SolveLongMatrix()
{
var matrix = new SparseMatrix(3, 2);
Vector<Complex> input = new DenseVector(3);
var solver = new BiCgStab();
solver.Solve(matrix, input);
}
[Test]
[MultipleAsserts]
public void SolveUnitMatrixAndBackMultiply()
{
// Create the identity matrix
Matrix<Complex> matrix = SparseMatrix.Identity(100);
// Create the y vector
Vector<Complex> y = new DenseVector(matrix.RowCount, Complex.One);
// Create an iteration monitor which will keep track of iterative convergence
var monitor = new Iterator(new IIterationStopCriterium<Complex>[]
{
new IterationCountStopCriterium(MaximumIterations),
new ResidualStopCriterium(ConvergenceBoundary),
new DivergenceStopCriterium(),
new FailureStopCriterium()
});
var solver = new BiCgStab(monitor);
// Solve equation Ax = y
var x = solver.Solve(matrix, y);
// Now compare the results
Assert.IsNotNull(x, "#02");
Assert.AreEqual(y.Count, x.Count, "#03");
// Back multiply the vector
var z = matrix.Multiply(x);
// Check that the solution converged
Assert.IsTrue(monitor.Status is CalculationConverged, "#04");
// Now compare the vectors
for (var i = 0; i < y.Count; i++)
{
Assert.IsTrue((y[i] - z[i]).Magnitude.IsSmaller(ConvergenceBoundary, 1), "#05-" + i);
}
}
[Test]
[MultipleAsserts]
public void SolveScaledUnitMatrixAndBackMultiply()
{
// Create the identity matrix
Matrix<Complex> matrix = SparseMatrix.Identity(100);
// Scale it with a funny number
matrix.Multiply(new Complex(System.Math.PI, System.Math.PI));
// Create the y vector
Vector<Complex> y = new DenseVector(matrix.RowCount, Complex.One);
// Create an iteration monitor which will keep track of iterative convergence
var monitor = new Iterator(new IIterationStopCriterium<Complex>[]
{
new IterationCountStopCriterium(MaximumIterations),
new ResidualStopCriterium(ConvergenceBoundary),
new DivergenceStopCriterium(),
new FailureStopCriterium()
});
var solver = new BiCgStab(monitor);
// Solve equation Ax = y
var x = solver.Solve(matrix, y);
// Now compare the results
Assert.IsNotNull(x, "#02");
Assert.AreEqual(y.Count, x.Count, "#03");
// Back multiply the vector
var z = matrix.Multiply(x);
// Check that the solution converged
Assert.IsTrue(monitor.Status is CalculationConverged, "#04");
// Now compare the vectors
for (var i = 0; i < y.Count; i++)
{
Assert.IsTrue((y[i] - z[i]).Magnitude.IsSmaller(ConvergenceBoundary, 1), "#05-" + i);
}
}
[Test]
[MultipleAsserts]
public void SolvePoissonMatrixAndBackMultiply()
{
// Create the matrix
var matrix = new SparseMatrix(100);
// Assemble the matrix. We assume we're solving the Poisson equation
// on a rectangular 10 x 10 grid
const int GridSize = 10;
// The pattern is:
// 0 .... 0 -1 0 0 0 0 0 0 0 0 -1 4 -1 0 0 0 0 0 0 0 0 -1 0 0 ... 0
for (var i = 0; i < matrix.RowCount; i++)
{
// Insert the first set of -1's
if (i > (GridSize - 1))
{
matrix[i, i - GridSize] = -1;
}
// Insert the second set of -1's
if (i > 0)
{
matrix[i, i - 1] = -1;
}
// Insert the centerline values
matrix[i, i] = 4;
// Insert the first trailing set of -1's
if (i < matrix.RowCount - 1)
{
matrix[i, i + 1] = -1;
}
// Insert the second trailing set of -1's
if (i < matrix.RowCount - GridSize)
{
matrix[i, i + GridSize] = -1;
}
}
// Create the y vector
Vector<Complex> y = new DenseVector(matrix.RowCount, Complex.One);
// Create an iteration monitor which will keep track of iterative convergence
var monitor = new Iterator(new IIterationStopCriterium<Complex>[]
{
new IterationCountStopCriterium(MaximumIterations),
new ResidualStopCriterium(ConvergenceBoundary),
new DivergenceStopCriterium(),
new FailureStopCriterium()
});
var solver = new BiCgStab(monitor);
// Solve equation Ax = y
var x = solver.Solve(matrix, y);
// Now compare the results
Assert.IsNotNull(x, "#02");
Assert.AreEqual(y.Count, x.Count, "#03");
// Back multiply the vector
var z = matrix.Multiply(x);
// Check that the solution converged
Assert.IsTrue(monitor.Status is CalculationConverged, "#04");
// Now compare the vectors
for (var i = 0; i < y.Count; i++)
{
Assert.IsTrue((y[i] - z[i]).Magnitude.IsSmaller(ConvergenceBoundary, 1), "#05-" + i);
}
}
[Test]
[Row(4)]
[Row(8)]
[Row(10)]
[Row(20)]
[MultipleAsserts]
public void CanSolveForRandomVector(int order)
{
var matrixA = MatrixLoader.GenerateRandomDenseMatrix(order, order);
var vectorb = MatrixLoader.GenerateRandomDenseVector(order);
var monitor = new Iterator(new IIterationStopCriterium<Complex>[]
{
new IterationCountStopCriterium(1000),
new ResidualStopCriterium(1e-10),
});
var solver = new BiCgStab(monitor);
var resultx = solver.Solve(matrixA, vectorb);
Assert.AreEqual(matrixA.ColumnCount, resultx.Count);
var bReconstruct = matrixA * resultx;
// Check the reconstruction.
for (var i = 0; i < order; i++)
{
Assert.AreApproximatelyEqual(vectorb[i].Real, bReconstruct[i].Real, 1e-5);
Assert.AreApproximatelyEqual(vectorb[i].Imaginary, bReconstruct[i].Imaginary, 1e-5);
}
}
[Test]
[Row(4)]
[Row(8)]
[Row(10)]
[Row(20)]
[MultipleAsserts]
public void CanSolveForRandomMatrix(int order)
{
var matrixA = MatrixLoader.GenerateRandomDenseMatrix(order, order);
var matrixB = MatrixLoader.GenerateRandomDenseMatrix(order, order);
var monitor = new Iterator(new IIterationStopCriterium<Complex>[]
{
new IterationCountStopCriterium(1000),
new ResidualStopCriterium(1e-10),
});
var solver = new BiCgStab(monitor);
var matrixX = solver.Solve(matrixA, matrixB);
// The solution X row dimension is equal to the column dimension of A
Assert.AreEqual(matrixA.ColumnCount, matrixX.RowCount);
// The solution X has the same number of columns as B
Assert.AreEqual(matrixB.ColumnCount, matrixX.ColumnCount);
var matrixBReconstruct = matrixA * matrixX;
// Check the reconstruction.
for (var i = 0; i < matrixB.RowCount; i++)
{
for (var j = 0; j < matrixB.ColumnCount; j++)
{
Assert.AreApproximatelyEqual(matrixB[i, j].Real, matrixBReconstruct[i, j].Real, 1.0e-5);
Assert.AreApproximatelyEqual(matrixB[i, j].Imaginary, matrixBReconstruct[i, j].Imaginary, 1.0e-5);
}
}
}
}
}

270
src/UnitTests/LinearAlgebraTests/Complex/Solvers/Iterative/GpBiCgTest.cs

@ -0,0 +1,270 @@
namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex.Solvers.Iterative
{
using System.Numerics;
using LinearAlgebra.Complex;
using LinearAlgebra.Complex.Solvers;
using LinearAlgebra.Complex.Solvers.Iterative;
using LinearAlgebra.Complex.Solvers.StopCriterium;
using LinearAlgebra.Generic;
using LinearAlgebra.Generic.Solvers.Status;
using LinearAlgebra.Generic.Solvers.StopCriterium;
using MbUnit.Framework;
[TestFixture]
public class GpBiCgTest
{
private const double ConvergenceBoundary = 1e-10;
private const int MaximumIterations = 1000;
[Test]
[ExpectedArgumentException]
public void SolveWideMatrix()
{
var matrix = new SparseMatrix(2, 3);
Vector<Complex> input = new DenseVector(2);
var solver = new GpBiCg();
solver.Solve(matrix, input);
}
[Test]
[ExpectedArgumentException]
public void SolveLongMatrix()
{
var matrix = new SparseMatrix(3, 2);
Vector<Complex> input = new DenseVector(3);
var solver = new GpBiCg();
solver.Solve(matrix, input);
}
[Test]
[MultipleAsserts]
public void SolveUnitMatrixAndBackMultiply()
{
// Create the identity matrix
Matrix<Complex> matrix = SparseMatrix.Identity(100);
// Create the y vector
Vector<Complex> y = new DenseVector(matrix.RowCount, 1);
// Create an iteration monitor which will keep track of iterative convergence
var monitor = new Iterator(new IIterationStopCriterium<Complex>[]
{
new IterationCountStopCriterium(MaximumIterations),
new ResidualStopCriterium(ConvergenceBoundary),
new DivergenceStopCriterium(),
new FailureStopCriterium()
});
var solver = new GpBiCg(monitor);
// Solve equation Ax = y
var x = solver.Solve(matrix, y);
// Now compare the results
Assert.IsNotNull(x, "#02");
Assert.AreEqual(y.Count, x.Count, "#03");
// Back multiply the vector
var z = matrix.Multiply(x);
// Check that the solution converged
Assert.IsTrue(monitor.Status is CalculationConverged, "#04");
// Now compare the vectors
for (var i = 0; i < y.Count; i++)
{
Assert.IsTrue((y[i] - z[i]).Magnitude.IsSmaller(ConvergenceBoundary, 1), "#05-" + i);
}
}
[Test]
[MultipleAsserts]
public void SolveScaledUnitMatrixAndBackMultiply()
{
// Create the identity matrix
Matrix<Complex> matrix = SparseMatrix.Identity(100);
// Scale it with a funny number
matrix.Multiply(System.Math.PI);
// Create the y vector
Vector<Complex> y = new DenseVector(matrix.RowCount, 1);
// Create an iteration monitor which will keep track of iterative convergence
var monitor = new Iterator(new IIterationStopCriterium<Complex>[]
{
new IterationCountStopCriterium(MaximumIterations),
new ResidualStopCriterium(ConvergenceBoundary),
new DivergenceStopCriterium(),
new FailureStopCriterium()
});
var solver = new GpBiCg(monitor);
// Solve equation Ax = y
var x = solver.Solve(matrix, y);
// Now compare the results
Assert.IsNotNull(x, "#02");
Assert.AreEqual(y.Count, x.Count, "#03");
// Back multiply the vector
var z = matrix.Multiply(x);
// Check that the solution converged
Assert.IsTrue(monitor.Status is CalculationConverged, "#04");
// Now compare the vectors
for (var i = 0; i < y.Count; i++)
{
Assert.IsTrue((y[i] - z[i]).Magnitude.IsSmaller(ConvergenceBoundary, 1), "#05-" + i);
}
}
[Test]
[MultipleAsserts]
public void SolvePoissonMatrixAndBackMultiply()
{
// Create the matrix
var matrix = new SparseMatrix(100);
// Assemble the matrix. We assume we're solving the Poisson equation
// on a rectangular 10 x 10 grid
const int GridSize = 10;
// The pattern is:
// 0 .... 0 -1 0 0 0 0 0 0 0 0 -1 4 -1 0 0 0 0 0 0 0 0 -1 0 0 ... 0
for (var i = 0; i < matrix.RowCount; i++)
{
// Insert the first set of -1's
if (i > (GridSize - 1))
{
matrix[i, i - GridSize] = -1;
}
// Insert the second set of -1's
if (i > 0)
{
matrix[i, i - 1] = -1;
}
// Insert the centerline values
matrix[i, i] = 4;
// Insert the first trailing set of -1's
if (i < matrix.RowCount - 1)
{
matrix[i, i + 1] = -1;
}
// Insert the second trailing set of -1's
if (i < matrix.RowCount - GridSize)
{
matrix[i, i + GridSize] = -1;
}
}
// Create the y vector
Vector<Complex> y = new DenseVector(matrix.RowCount, 1);
// Create an iteration monitor which will keep track of iterative convergence
var monitor = new Iterator(new IIterationStopCriterium<Complex>[]
{
new IterationCountStopCriterium(MaximumIterations),
new ResidualStopCriterium(ConvergenceBoundary),
new DivergenceStopCriterium(),
new FailureStopCriterium()
});
var solver = new GpBiCg(monitor);
// Solve equation Ax = y
var x = solver.Solve(matrix, y);
// Now compare the results
Assert.IsNotNull(x, "#02");
Assert.AreEqual(y.Count, x.Count, "#03");
// Back multiply the vector
var z = matrix.Multiply(x);
// Check that the solution converged
Assert.IsTrue(monitor.Status is CalculationConverged, "#04");
// Now compare the vectors
for (var i = 0; i < y.Count; i++)
{
Assert.IsTrue((y[i] - z[i]).Magnitude.IsSmaller(ConvergenceBoundary, 1), "#05-" + i);
}
}
[Test]
[Row(4)]
[Row(8)]
[Row(10)]
[Row(20)]
[MultipleAsserts]
public void CanSolveForRandomVector(int order)
{
var matrixA = MatrixLoader.GenerateRandomDenseMatrix(order, order);
var vectorb = MatrixLoader.GenerateRandomDenseVector(order);
var monitor = new Iterator(new IIterationStopCriterium<Complex>[]
{
new IterationCountStopCriterium(1000),
new ResidualStopCriterium(1e-10),
});
var solver = new GpBiCg(monitor);
var resultx = solver.Solve(matrixA, vectorb);
Assert.AreEqual(matrixA.ColumnCount, resultx.Count);
var bReconstruct = matrixA * resultx;
// Check the reconstruction.
for (var i = 0; i < order; i++)
{
Assert.AreApproximatelyEqual(vectorb[i].Real, bReconstruct[i].Real, 1e-5);
Assert.AreApproximatelyEqual(vectorb[i].Imaginary, bReconstruct[i].Imaginary, 1e-5);
}
}
[Test]
[Row(4)]
[Row(8)]
[Row(10)]
[Row(20)]
[MultipleAsserts]
public void CanSolveForRandomMatrix(int order)
{
var matrixA = MatrixLoader.GenerateRandomDenseMatrix(order, order);
var matrixB = MatrixLoader.GenerateRandomDenseMatrix(order, order);
var monitor = new Iterator(new IIterationStopCriterium<Complex>[]
{
new IterationCountStopCriterium(1000),
new ResidualStopCriterium(1e-10),
});
var solver = new GpBiCg(monitor);
var matrixX = solver.Solve(matrixA, matrixB);
// The solution X row dimension is equal to the column dimension of A
Assert.AreEqual(matrixA.ColumnCount, matrixX.RowCount);
// The solution X has the same number of columns as B
Assert.AreEqual(matrixB.ColumnCount, matrixX.ColumnCount);
var matrixBReconstruct = matrixA * matrixX;
// Check the reconstruction.
for (var i = 0; i < matrixB.RowCount; i++)
{
for (var j = 0; j < matrixB.ColumnCount; j++)
{
Assert.AreApproximatelyEqual(matrixB[i, j].Real, matrixBReconstruct[i, j].Real, 1.0e-5);
Assert.AreApproximatelyEqual(matrixB[i, j].Imaginary, matrixBReconstruct[i, j].Imaginary, 1.0e-5);
}
}
}
}
}

266
src/UnitTests/LinearAlgebraTests/Complex/Solvers/Iterative/MlkBiCgStabTest.cs

@ -0,0 +1,266 @@
namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex.Solvers.Iterative
{
using System.Numerics;
using LinearAlgebra.Complex;
using LinearAlgebra.Complex.Solvers;
using LinearAlgebra.Complex.Solvers.Iterative;
using LinearAlgebra.Complex.Solvers.StopCriterium;
using LinearAlgebra.Generic;
using LinearAlgebra.Generic.Solvers.Status;
using LinearAlgebra.Generic.Solvers.StopCriterium;
using MbUnit.Framework;
[TestFixture]
public class MlkBiCgStabTest
{
private const double ConvergenceBoundary = 1e-10;
private const int MaximumIterations = 1000;
[Test]
[ExpectedArgumentException]
public void SolveWideMatrix()
{
var matrix = new SparseMatrix(2, 3);
Vector<Complex> input = new DenseVector(2);
var solver = new MlkBiCgStab();
solver.Solve(matrix, input);
}
[Test]
[ExpectedArgumentException]
public void SolveLongMatrix()
{
var matrix = new SparseMatrix(3, 2);
Vector<Complex> input = new DenseVector(3);
var solver = new MlkBiCgStab();
solver.Solve(matrix, input);
}
[Test]
[MultipleAsserts]
public void SolveUnitMatrixAndBackMultiply()
{
// Create the identity matrix
Matrix<Complex> matrix = SparseMatrix.Identity(100);
// Create the y vector
Vector<Complex> y = new DenseVector(matrix.RowCount, 1);
// Create an iteration monitor which will keep track of iterative convergence
var monitor = new Iterator(new IIterationStopCriterium<Complex>[]
{
new IterationCountStopCriterium(MaximumIterations),
new ResidualStopCriterium(ConvergenceBoundary),
new DivergenceStopCriterium(),
new FailureStopCriterium()
});
var solver = new MlkBiCgStab(monitor);
// Solve equation Ax = y
var x = solver.Solve(matrix, y);
// Now compare the results
Assert.IsNotNull(x, "#02");
Assert.AreEqual(y.Count, x.Count, "#03");
// Back multiply the vector
var z = matrix.Multiply(x);
// Check that the solution converged
Assert.IsTrue(monitor.Status is CalculationConverged, "#04");
// Now compare the vectors
for (var i = 0; i < y.Count; i++)
{
Assert.IsTrue((y[i] - z[i]).Magnitude.IsSmaller(ConvergenceBoundary, 1), "#05-" + i);
}
}
[Test]
[MultipleAsserts]
public void SolveScaledUnitMatrixAndBackMultiply()
{
// Create the identity matrix
Matrix<Complex> matrix = SparseMatrix.Identity(100);
// Scale it with a funny number
matrix.Multiply(System.Math.PI);
// Create the y vector
Vector<Complex> y = new DenseVector(matrix.RowCount, 1);
// Create an iteration monitor which will keep track of iterative convergence
var monitor = new Iterator(new IIterationStopCriterium<Complex>[]
{
new IterationCountStopCriterium(MaximumIterations),
new ResidualStopCriterium(ConvergenceBoundary),
new DivergenceStopCriterium(),
new FailureStopCriterium()
});
var solver = new MlkBiCgStab(monitor);
// Solve equation Ax = y
var x = solver.Solve(matrix, y);
// Now compare the results
Assert.IsNotNull(x, "#02");
Assert.AreEqual(y.Count, x.Count, "#03");
// Back multiply the vector
var z = matrix.Multiply(x);
// Check that the solution converged
Assert.IsTrue(monitor.Status is CalculationConverged, "#04");
// Now compare the vectors
for (var i = 0; i < y.Count; i++)
{
Assert.IsTrue((y[i] - z[i]).Magnitude.IsSmaller(ConvergenceBoundary, 1), "#05-" + i);
}
}
[Test]
[MultipleAsserts]
public void SolvePoissonMatrixAndBackMultiply()
{
// Create the matrix
var matrix = new SparseMatrix(100);
// Assemble the matrix. We assume we're solving the Poisson equation
// on a rectangular 10 x 10 grid
const int GridSize = 10;
// The pattern is:
// 0 .... 0 -1 0 0 0 0 0 0 0 0 -1 4 -1 0 0 0 0 0 0 0 0 -1 0 0 ... 0
for (var i = 0; i < matrix.RowCount; i++)
{
// Insert the first set of -1's
if (i > (GridSize - 1))
{
matrix[i, i - GridSize] = -1;
}
// Insert the second set of -1's
if (i > 0)
{
matrix[i, i - 1] = -1;
}
// Insert the centerline values
matrix[i, i] = 4;
// Insert the first trailing set of -1's
if (i < matrix.RowCount - 1)
{
matrix[i, i + 1] = -1;
}
// Insert the second trailing set of -1's
if (i < matrix.RowCount - GridSize)
{
matrix[i, i + GridSize] = -1;
}
}
// Create the y vector
Vector<Complex> y = new DenseVector(matrix.RowCount, 1);
// Create an iteration monitor which will keep track of iterative convergence
var monitor = new Iterator(new IIterationStopCriterium<Complex>[]
{
new IterationCountStopCriterium(MaximumIterations),
new ResidualStopCriterium(ConvergenceBoundary),
new DivergenceStopCriterium(),
new FailureStopCriterium()
});
var solver = new MlkBiCgStab(monitor);
// Solve equation Ax = y
var x = solver.Solve(matrix, y);
// Now compare the results
Assert.IsNotNull(x, "#02");
Assert.AreEqual(y.Count, x.Count, "#03");
// Back multiply the vector
var z = matrix.Multiply(x);
// Check that the solution converged
Assert.IsTrue(monitor.Status is CalculationConverged, "#04");
// Now compare the vectors
for (var i = 0; i < y.Count; i++)
{
Assert.IsTrue((y[i] - z[i]).Magnitude.IsSmaller(ConvergenceBoundary, 1), "#05-" + i);
}
}
[Test]
[Row(4)]
[Row(8)]
[Row(10)]
[MultipleAsserts]
public void CanSolveForRandomVector(int order)
{
var matrixA = MatrixLoader.GenerateRandomDenseMatrix(order, order);
var vectorb = MatrixLoader.GenerateRandomDenseVector(order);
var monitor = new Iterator(new IIterationStopCriterium<Complex>[]
{
new IterationCountStopCriterium(1000),
new ResidualStopCriterium(1e-10),
});
var solver = new MlkBiCgStab(monitor);
var resultx = solver.Solve(matrixA, vectorb);
Assert.AreEqual(matrixA.ColumnCount, resultx.Count);
var bReconstruct = matrixA * resultx;
// Check the reconstruction.
for (var i = 0; i < order; i++)
{
Assert.AreApproximatelyEqual(vectorb[i].Real, bReconstruct[i].Real, 1e-5);
Assert.AreApproximatelyEqual(vectorb[i].Imaginary, bReconstruct[i].Imaginary, 1e-5);
}
}
[Test]
[Row(4)]
[Row(8)]
[Row(10)]
[MultipleAsserts]
public void CanSolveForRandomMatrix(int order)
{
var matrixA = MatrixLoader.GenerateRandomDenseMatrix(order, order);
var matrixB = MatrixLoader.GenerateRandomDenseMatrix(order, order);
var monitor = new Iterator(new IIterationStopCriterium<Complex>[]
{
new IterationCountStopCriterium(1000),
new ResidualStopCriterium(1e-10),
});
var solver = new MlkBiCgStab(monitor);
var matrixX = solver.Solve(matrixA, matrixB);
// The solution X row dimension is equal to the column dimension of A
Assert.AreEqual(matrixA.ColumnCount, matrixX.RowCount);
// The solution X has the same number of columns as B
Assert.AreEqual(matrixB.ColumnCount, matrixX.ColumnCount);
var matrixBReconstruct = matrixA * matrixX;
// Check the reconstruction.
for (var i = 0; i < matrixB.RowCount; i++)
{
for (var j = 0; j < matrixB.ColumnCount; j++)
{
Assert.AreApproximatelyEqual(matrixB[i, j].Real, matrixBReconstruct[i, j].Real, 1.0e-5);
Assert.AreApproximatelyEqual(matrixB[i, j].Imaginary, matrixBReconstruct[i, j].Imaginary, 1.0e-5);
}
}
}
}
}

268
src/UnitTests/LinearAlgebraTests/Complex/Solvers/Iterative/TFQMRTest.cs

@ -0,0 +1,268 @@
namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex.Solvers.Iterative
{
using System.Numerics;
using LinearAlgebra.Complex;
using LinearAlgebra.Complex.Solvers;
using LinearAlgebra.Complex.Solvers.Iterative;
using LinearAlgebra.Complex.Solvers.StopCriterium;
using LinearAlgebra.Generic;
using LinearAlgebra.Generic.Solvers.Status;
using LinearAlgebra.Generic.Solvers.StopCriterium;
using MbUnit.Framework;
[TestFixture]
public class TFQMRTest
{
private const double ConvergenceBoundary = 1e-10;
private const int MaximumIterations = 1000;
[Test]
[ExpectedArgumentException]
public void SolveWideMatrix()
{
var matrix = new SparseMatrix(2, 3);
Vector<Complex> input = new DenseVector(2);
var solver = new TFQMR();
solver.Solve(matrix, input);
}
[Test]
[ExpectedArgumentException]
public void SolveLongMatrix()
{
var matrix = new SparseMatrix(3, 2);
Vector<Complex> input = new DenseVector(3);
var solver = new TFQMR();
solver.Solve(matrix, input);
}
[Test]
[MultipleAsserts]
public void SolveUnitMatrixAndBackMultiply()
{
// Create the identity matrix
Matrix<Complex> matrix = SparseMatrix.Identity(100);
// Create the y vector
Vector<Complex> y = new DenseVector(matrix.RowCount, 1);
// Create an iteration monitor which will keep track of iterative convergence
var monitor = new Iterator(new IIterationStopCriterium<Complex>[]
{
new IterationCountStopCriterium(MaximumIterations),
new ResidualStopCriterium(ConvergenceBoundary),
new DivergenceStopCriterium(),
new FailureStopCriterium()
});
var solver = new TFQMR(monitor);
// Solve equation Ax = y
var x = solver.Solve(matrix, y);
// Now compare the results
Assert.IsNotNull(x, "#02");
Assert.AreEqual(y.Count, x.Count, "#03");
// Back multiply the vector
var z = matrix.Multiply(x);
// Check that the solution converged
Assert.IsTrue(monitor.Status is CalculationConverged, "#04");
// Now compare the vectors
for (var i = 0; i < y.Count; i++)
{
Assert.IsTrue((y[i] - z[i]).Magnitude.IsSmaller(ConvergenceBoundary, 1), "#05-" + i);
}
}
[Test]
[MultipleAsserts]
public void SolveScaledUnitMatrixAndBackMultiply()
{
// Create the identity matrix
Matrix<Complex> matrix = SparseMatrix.Identity(100);
// Scale it with a funny number
matrix.Multiply(System.Math.PI);
// Create the y vector
Vector<Complex> y = new DenseVector(matrix.RowCount, 1);
// Create an iteration monitor which will keep track of iterative convergence
var monitor = new Iterator(new IIterationStopCriterium<Complex>[]
{
new IterationCountStopCriterium(MaximumIterations),
new ResidualStopCriterium(ConvergenceBoundary),
new DivergenceStopCriterium(),
new FailureStopCriterium()
});
var solver = new TFQMR(monitor);
// Solve equation Ax = y
var x = solver.Solve(matrix, y);
// Now compare the results
Assert.IsNotNull(x, "#02");
Assert.AreEqual(y.Count, x.Count, "#03");
// Back multiply the vector
var z = matrix.Multiply(x);
// Check that the solution converged
Assert.IsTrue(monitor.Status is CalculationConverged, "#04");
// Now compare the vectors
for (var i = 0; i < y.Count; i++)
{
Assert.IsTrue((y[i] - z[i]).Magnitude.IsSmaller(ConvergenceBoundary, 1), "#05-" + i);
}
}
[Test]
[MultipleAsserts]
public void SolvePoissonMatrixAndBackMultiply()
{
// Create the matrix
var matrix = new SparseMatrix(100);
// Assemble the matrix. We assume we're solving the Poisson equation
// on a rectangular 10 x 10 grid
const int GridSize = 10;
// The pattern is:
// 0 .... 0 -1 0 0 0 0 0 0 0 0 -1 4 -1 0 0 0 0 0 0 0 0 -1 0 0 ... 0
for (var i = 0; i < matrix.RowCount; i++)
{
// Insert the first set of -1's
if (i > (GridSize - 1))
{
matrix[i, i - GridSize] = -1;
}
// Insert the second set of -1's
if (i > 0)
{
matrix[i, i - 1] = -1;
}
// Insert the centerline values
matrix[i, i] = 4;
// Insert the first trailing set of -1's
if (i < matrix.RowCount - 1)
{
matrix[i, i + 1] = -1;
}
// Insert the second trailing set of -1's
if (i < matrix.RowCount - GridSize)
{
matrix[i, i + GridSize] = -1;
}
}
// Create the y vector
Vector<Complex> y = new DenseVector(matrix.RowCount, 1);
// Create an iteration monitor which will keep track of iterative convergence
var monitor = new Iterator(new IIterationStopCriterium<Complex>[]
{
new IterationCountStopCriterium(MaximumIterations),
new ResidualStopCriterium(ConvergenceBoundary),
new DivergenceStopCriterium(),
new FailureStopCriterium()
});
var solver = new TFQMR(monitor);
// Solve equation Ax = y
var x = solver.Solve(matrix, y);
// Now compare the results
Assert.IsNotNull(x, "#02");
Assert.AreEqual(y.Count, x.Count, "#03");
// Back multiply the vector
var z = matrix.Multiply(x);
// Check that the solution converged
Assert.IsTrue(monitor.Status is CalculationConverged, "#04");
// Now compare the vectors
for (var i = 0; i < y.Count; i++)
{
Assert.IsTrue((y[i] - z[i]).Magnitude.IsSmaller(ConvergenceBoundary, 1), "#05-" + i);
}
}
[Test]
[Row(4)]
[Row(8)]
[Row(10)]
[Row(20)]
[MultipleAsserts]
public void CanSolveForRandomVector(int order)
{
var matrixA = MatrixLoader.GenerateRandomDenseMatrix(order, order);
var vectorb = MatrixLoader.GenerateRandomDenseVector(order);
var monitor = new Iterator(new IIterationStopCriterium<Complex>[]
{
new IterationCountStopCriterium(1000),
new ResidualStopCriterium(1e-10),
});
var solver = new TFQMR(monitor);
var resultx = solver.Solve(matrixA, vectorb);
Assert.AreEqual(matrixA.ColumnCount, resultx.Count);
var bReconstruct = matrixA * resultx;
// Check the reconstruction.
for (var i = 0; i < order; i++)
{
Assert.AreApproximatelyEqual(vectorb[i].Real, bReconstruct[i].Real, 1e-5);
Assert.AreApproximatelyEqual(vectorb[i].Imaginary, bReconstruct[i].Imaginary, 1e-5);
}
}
[Test]
[Row(4)]
[Row(8)]
[Row(10)]
[Row(20)]
[MultipleAsserts]
public void CanSolveForRandomMatrix(int order)
{
var matrixA = MatrixLoader.GenerateRandomDenseMatrix(order, order);
var matrixB = MatrixLoader.GenerateRandomDenseMatrix(order, order);
var monitor = new Iterator(new IIterationStopCriterium<Complex>[]
{
new IterationCountStopCriterium(1000),
new ResidualStopCriterium(1e-10),
});
var solver = new TFQMR(monitor);
var matrixX = solver.Solve(matrixA, matrixB);
// The solution X row dimension is equal to the column dimension of A
Assert.AreEqual(matrixA.ColumnCount, matrixX.RowCount);
// The solution X has the same number of columns as B
Assert.AreEqual(matrixB.ColumnCount, matrixX.ColumnCount);
var matrixBReconstruct = matrixA * matrixX;
// Check the reconstruction.
for (var i = 0; i < matrixB.RowCount; i++)
{
for (var j = 0; j < matrixB.ColumnCount; j++)
{
Assert.AreApproximatelyEqual(matrixB[i, j].Real, matrixBReconstruct[i, j].Real, 1.0e-5);
Assert.AreApproximatelyEqual(matrixB[i, j].Imaginary, matrixBReconstruct[i, j].Imaginary, 1.0e-5);
}
}
}
}
}

355
src/UnitTests/LinearAlgebraTests/Complex/Solvers/IteratorTest.cs

@ -0,0 +1,355 @@
namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex.Solvers
{
using System;
using System.Collections.Generic;
using System.Numerics;
using LinearAlgebra.Complex;
using LinearAlgebra.Complex.Solvers;
using LinearAlgebra.Complex.Solvers.StopCriterium;
using LinearAlgebra.Generic.Solvers.Status;
using LinearAlgebra.Generic.Solvers.StopCriterium;
using MbUnit.Framework;
[TestFixture]
public class IteratorTest
{
[Test]
[MultipleAsserts]
public void CreateWithNullCollection()
{
var iterator = new Iterator(null);
Assert.IsNotNull(iterator, "Should have an iterator");
Assert.AreEqual(0, iterator.NumberOfCriteria, "There shouldn't be any criteria");
}
[Test]
[MultipleAsserts]
public void CreateWithEmptyCollection()
{
var iterator = new Iterator(new IIterationStopCriterium<Complex>[] { });
Assert.IsNotNull(iterator, "Should have an iterator");
Assert.AreEqual(0, iterator.NumberOfCriteria, "There shouldn't be any criteria");
}
[Test]
[MultipleAsserts]
public void CreateWithCollectionWithNulls()
{
var iterator = new Iterator(new IIterationStopCriterium<Complex>[] { null, null });
Assert.IsNotNull(iterator, "Should have an iterator");
Assert.AreEqual(0, iterator.NumberOfCriteria, "There shouldn't be any criteria");
}
[Test]
[ExpectedArgumentException]
public void CreateWithDuplicates()
{
new Iterator(new IIterationStopCriterium<Complex>[]
{
new FailureStopCriterium(),
new FailureStopCriterium()
});
}
[Test]
[MultipleAsserts]
public void CreateWithCollection()
{
var criteria = new List<IIterationStopCriterium<Complex>>
{
new FailureStopCriterium(),
new DivergenceStopCriterium(),
new IterationCountStopCriterium(),
new ResidualStopCriterium()
};
var iterator = new Iterator(criteria);
Assert.IsNotNull(iterator, "Should have an iterator");
// Check that we have all the criteria
Assert.AreEqual(criteria.Count, iterator.NumberOfCriteria, "Incorrect criterium count");
var enumerator = iterator.StoredStopCriteria;
while (enumerator.MoveNext())
{
var criterium = enumerator.Current;
Assert.IsTrue(criteria.Exists( c => ReferenceEquals(c, criterium)), "Criterium missing");
}
}
[Test]
[ExpectedArgumentNullException]
public void AddWithNullStopCriterium()
{
var iterator = new Iterator();
iterator.Add(null);
}
[Test]
[ExpectedArgumentException]
public void AddWithExistingStopCriterium()
{
var iterator = new Iterator();
iterator.Add(new FailureStopCriterium());
Assert.AreEqual(1, iterator.NumberOfCriteria, "Incorrect criterium count");
iterator.Add(new FailureStopCriterium());
}
[Test]
[MultipleAsserts]
public void Add()
{
var criteria = new List<IIterationStopCriterium<Complex>>
{
new FailureStopCriterium(),
new DivergenceStopCriterium(),
new IterationCountStopCriterium(),
new ResidualStopCriterium()
};
var iterator = new Iterator();
Assert.AreEqual(0, iterator.NumberOfCriteria, "Incorrect criterium count");
foreach (var criterium in criteria)
{
iterator.Add(criterium);
Assert.IsTrue(iterator.Contains(criterium), "Missing criterium");
}
// Check that we have all the criteria
Assert.AreEqual(criteria.Count, iterator.NumberOfCriteria, "Incorrect criterium count");
var enumerator = iterator.StoredStopCriteria;
while (enumerator.MoveNext())
{
var criterium = enumerator.Current;
Assert.IsTrue(criteria.Exists( c => ReferenceEquals(c, criterium)), "Criterium missing");
}
}
[Test]
[ExpectedArgumentNullException]
public void RemoveWithNullStopCriterium()
{
var criteria = new List<IIterationStopCriterium<Complex>>
{
new FailureStopCriterium(),
new DivergenceStopCriterium(),
new IterationCountStopCriterium(),
new ResidualStopCriterium()
};
var iterator = new Iterator(criteria);
Assert.AreEqual(criteria.Count, iterator.NumberOfCriteria, "Incorrect criterium count");
iterator.Remove(null);
}
[Test]
[MultipleAsserts]
public void RemoveWithNonExistingStopCriterium()
{
var criteria = new List<IIterationStopCriterium<Complex>>
{
new FailureStopCriterium(),
new DivergenceStopCriterium(),
new IterationCountStopCriterium(),
};
var iterator = new Iterator(criteria);
Assert.AreEqual(criteria.Count, iterator.NumberOfCriteria, "Incorrect criterium count");
iterator.Remove(new ResidualStopCriterium());
Assert.AreEqual(criteria.Count, iterator.NumberOfCriteria, "Incorrect criterium count");
}
[Test]
[MultipleAsserts]
public void Remove()
{
var criteria = new List<IIterationStopCriterium<Complex>>
{
new FailureStopCriterium(),
new DivergenceStopCriterium(),
new IterationCountStopCriterium(),
new ResidualStopCriterium()
};
var iterator = new Iterator(criteria);
Assert.AreEqual(criteria.Count, iterator.NumberOfCriteria, "Incorrect criterium count");
foreach (var criterium in criteria)
{
iterator.Remove(criterium);
Assert.IsFalse(iterator.Contains(criterium), "Did not remove the criterium");
}
}
[Test]
[ExpectedArgumentException]
public void DetermineStatusWithoutStopCriteria()
{
var iterator = new Iterator();
iterator.DetermineStatus(0,
new DenseVector(3, 4),
new DenseVector(3, 5),
new DenseVector(3, 6));
}
[Test]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void DetermineStatusWithNegativeIterationNumber()
{
var criteria = new List<IIterationStopCriterium<Complex>>
{
new FailureStopCriterium(),
new DivergenceStopCriterium(),
new IterationCountStopCriterium(),
new ResidualStopCriterium()
};
var iterator = new Iterator(criteria);
iterator.DetermineStatus(-1,
new DenseVector(3, 4),
new DenseVector(3, 5),
new DenseVector(3, 6));
}
[Test]
[ExpectedArgumentNullException]
public void DetermineStatusWithNullSolutionVector()
{
var criteria = new List<IIterationStopCriterium<Complex>>
{
new FailureStopCriterium(),
new DivergenceStopCriterium(),
new IterationCountStopCriterium(),
new ResidualStopCriterium()
};
var iterator = new Iterator(criteria);
iterator.DetermineStatus(1,
null,
new DenseVector(3, 5),
new DenseVector(3, 6));
}
[Test]
[ExpectedArgumentNullException]
public void DetermineStatusWithNullSourceVector()
{
var criteria = new List<IIterationStopCriterium<Complex>>
{
new FailureStopCriterium(),
new DivergenceStopCriterium(),
new IterationCountStopCriterium(),
new ResidualStopCriterium()
};
var iterator = new Iterator(criteria);
iterator.DetermineStatus(1,
new DenseVector(3, 5),
null,
new DenseVector(3, 6));
}
[Test]
[ExpectedArgumentNullException]
public void DetermineStatusWithNullResidualVector()
{
var criteria = new List<IIterationStopCriterium<Complex>>
{
new FailureStopCriterium(),
new DivergenceStopCriterium(),
new IterationCountStopCriterium(),
new ResidualStopCriterium()
};
var iterator = new Iterator(criteria);
iterator.DetermineStatus(1,
new DenseVector(3, 4),
new DenseVector(3, 5),
null);
}
[Test]
[MultipleAsserts]
public void DetermineStatus()
{
var criteria = new List<IIterationStopCriterium<Complex>>
{
new FailureStopCriterium(),
new DivergenceStopCriterium(),
new IterationCountStopCriterium(1)
};
var iterator = new Iterator(criteria);
// First step, nothing should happen.
iterator.DetermineStatus(0,
new DenseVector(3, 4),
new DenseVector(3, 4),
new DenseVector(3, 4));
Assert.IsInstanceOfType(typeof(CalculationRunning), iterator.Status, "Incorrect status");
// Second step, should run out of iterations.
iterator.DetermineStatus(1,
new DenseVector(3, 4),
new DenseVector(3, 4),
new DenseVector(3, 4));
Assert.IsInstanceOfType(typeof(CalculationStoppedWithoutConvergence), iterator.Status, "Incorrect status");
}
[Test]
[MultipleAsserts]
public void ResetToPrecalculationState()
{
var criteria = new List<IIterationStopCriterium<Complex>>
{
new FailureStopCriterium(),
new DivergenceStopCriterium(),
new IterationCountStopCriterium(1)
};
var iterator = new Iterator(criteria);
// First step, nothing should happen.
iterator.DetermineStatus(0,
new DenseVector(3, 4),
new DenseVector(3, 4),
new DenseVector(3, 4));
Assert.IsInstanceOfType(typeof(CalculationRunning), iterator.Status, "Incorrect status");
iterator.ResetToPrecalculationState();
Assert.IsInstanceOfType(typeof(CalculationIndetermined), iterator.Status, "Incorrect status");
Assert.IsInstanceOfType(typeof(CalculationIndetermined), criteria[0].Status, "Incorrect status");
Assert.IsInstanceOfType(typeof(CalculationIndetermined), criteria[1].Status, "Incorrect status");
Assert.IsInstanceOfType(typeof(CalculationIndetermined), criteria[2].Status, "Incorrect status");
}
[Test]
[MultipleAsserts]
public void Clone()
{
var criteria = new List<IIterationStopCriterium<Complex>>
{
new FailureStopCriterium(),
new DivergenceStopCriterium(),
new IterationCountStopCriterium(),
new ResidualStopCriterium()
};
var iterator = new Iterator(criteria);
var clonedIterator = iterator.Clone();
Assert.IsInstanceOfType(typeof(Iterator), clonedIterator, "Incorrect type");
var clone = clonedIterator as Iterator;
Assert.IsNotNull(clone);
// ReSharper disable PossibleNullReferenceException
Assert.AreEqual(iterator.NumberOfCriteria, clone.NumberOfCriteria, "Incorrect criterium count");
// ReSharper restore PossibleNullReferenceException
var enumerator = clone.StoredStopCriteria;
while (enumerator.MoveNext())
{
var criterium = enumerator.Current;
Assert.IsTrue(criteria.Exists(c => c.GetType().Equals(criterium.GetType())), "Criterium missing");
}
}
}
}

34
src/UnitTests/LinearAlgebraTests/Complex/Solvers/Preconditioners/DiagonalTest.cs

@ -0,0 +1,34 @@
namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex.Solvers.Preconditioners
{
using System.Numerics;
using LinearAlgebra.Complex;
using LinearAlgebra.Complex.Solvers.Preconditioners;
using LinearAlgebra.Generic;
using LinearAlgebra.Generic.Solvers.Preconditioners;
using MbUnit.Framework;
[TestFixture]
public sealed class DiagonalTest : PreconditionerTest
{
internal override IPreConditioner<Complex> CreatePreconditioner()
{
return new Diagonal();
}
protected override void CheckResult(IPreConditioner<Complex> preconditioner, SparseMatrix matrix, Vector<Complex> vector, Vector<Complex> result)
{
Assert.AreEqual(typeof(Diagonal), preconditioner.GetType(), "#01");
// Compute M * result = product
// compare vector and product. Should be equal
Vector<Complex> product = new DenseVector(result.Count);
matrix.Multiply(result, product);
for (var i = 0; i < product.Count; i++)
{
Assert.IsTrue(vector[i].Real.AlmostEqual(product[i].Real, -Epsilon.Magnitude()), "#02-" + i);
Assert.IsTrue(vector[i].Imaginary.AlmostEqual(product[i].Imaginary, -Epsilon.Magnitude()), "#03-" + i);
}
}
}
}

502
src/UnitTests/LinearAlgebraTests/Complex/Solvers/Preconditioners/IluptElementSorterTest.cs

@ -0,0 +1,502 @@
// NOTE: This class file Build Action is not set to Compile by default. Because IlutpElementSorter class is internal. If you want
// NOTE: to test IlutpElementSorter you should make it public, set Build Action=Compile of this file (in properties) and run tets.
// NOTE: After all tests passed please do all actions vice versa. IlutpElementSorter class is only for internal usage.
namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex.Solvers.Preconditioners
{
using LinearAlgebra.Complex;
using LinearAlgebra.Complex.Solvers.Preconditioners;
using MbUnit.Framework;
[TestFixture]
public sealed class IluptElementSorterTest
{
[Test]
[MultipleAsserts]
public void HeapSortWithIncreasingIntergerArray()
{
var sortedIndices = new [] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
IlutpElementSorter.SortIntegersDecreasing(sortedIndices);
for (var i = 0; i < sortedIndices.Length; i++)
{
Assert.AreEqual(sortedIndices.Length - 1 - i, sortedIndices[i], "#01-" + i);
}
}
[Test]
[MultipleAsserts]
public void HeapSortWithDecreasingIntegerArray()
{
var sortedIndices = new [] { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };
IlutpElementSorter.SortIntegersDecreasing(sortedIndices);
for (var i = 0; i < sortedIndices.Length; i++)
{
Assert.AreEqual(sortedIndices.Length - 1 - i, sortedIndices[i], "#01-" + i);
}
}
[Test]
[MultipleAsserts]
public void HeapSortWithRandomIntegerArray()
{
var sortedIndices = new []{ 5, 2, 8, 6, 0, 4, 1, 7, 3, 9 };
IlutpElementSorter.SortIntegersDecreasing(sortedIndices);
for (var i = 0; i < sortedIndices.Length; i++)
{
Assert.AreEqual(sortedIndices.Length - 1 - i, sortedIndices[i], "#01-" + i);
}
}
[Test]
[MultipleAsserts]
public void HeapSortWithDuplicateEntries()
{
var sortedIndices = new []{ 1, 1, 1, 1, 2, 2, 2, 2, 3, 4 };
IlutpElementSorter.SortIntegersDecreasing(sortedIndices);
for (var i = 0; i < sortedIndices.Length; i++)
{
if (i == 0)
{
Assert.AreEqual(4, sortedIndices[i], "#01-" + i);
}
else
{
if (i == 1)
{
Assert.AreEqual(3, sortedIndices[i], "#01-" + i);
}
else
{
if (i < 6)
{
if (sortedIndices[i] != 2)
{
Assert.Fail("#01-" + i);
}
}
else
{
if (sortedIndices[i] != 1)
{
Assert.Fail("#01-" + i);
}
}
}
}
}
}
[Test]
[MultipleAsserts]
public void HeapSortWithSpecialConstructedIntegerArray()
{
var sortedIndices = new []{ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0 };
IlutpElementSorter.SortIntegersDecreasing(sortedIndices);
for (var i = 0; i < sortedIndices.Length; i++)
{
if (i == 0)
{
Assert.AreEqual(1, sortedIndices[i], "#01-" + i);
break;
}
}
sortedIndices = new []{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
IlutpElementSorter.SortIntegersDecreasing(sortedIndices);
for (var i = 0; i < sortedIndices.Length; i++)
{
if (i == 0)
{
Assert.AreEqual(1, sortedIndices[i], "#02-" + i);
break;
}
}
sortedIndices = new []{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 };
IlutpElementSorter.SortIntegersDecreasing(sortedIndices);
for (var i = 0; i < sortedIndices.Length; i++)
{
if (i == 0)
{
Assert.AreEqual(1, sortedIndices[i], "#03-" + i);
break;
}
}
sortedIndices = new []{ 1, 1, 1, 0, 1, 1, 1, 1, 1, 1 };
IlutpElementSorter.SortIntegersDecreasing(sortedIndices);
for (var i = 0; i < sortedIndices.Length; i++)
{
if (i == 9)
{
Assert.AreEqual(0, sortedIndices[i], "#04-" + i);
break;
}
}
}
[Test]
[MultipleAsserts]
public void HeapSortWithIncreasingDoubleArray()
{
var sortedIndices = new int[10];
Vector values = new DenseVector(10);
values[0] = 0;
values[1] = 1;
values[2] = 2;
values[3] = 3;
values[4] = 4;
values[5] = 5;
values[6] = 6;
values[7] = 7;
values[8] = 8;
values[9] = 9;
for (var i = 0; i < sortedIndices.Length; i++)
{
sortedIndices[i] = i;
}
IlutpElementSorter.SortDoubleIndicesDecreasing(0, sortedIndices.Length - 1, sortedIndices, values);
for (var i = 0; i < sortedIndices.Length; i++)
{
Assert.AreEqual(sortedIndices.Length - 1 - i, sortedIndices[i], "#01-" + i);
}
}
[Test]
[MultipleAsserts]
public void HeapSortWithDecreasingDoubleArray()
{
var sortedIndices = new int[10];
Vector values = new DenseVector(10);
values[0] = 9;
values[1] = 8;
values[2] = 7;
values[3] = 6;
values[4] = 5;
values[5] = 4;
values[6] = 3;
values[7] = 2;
values[8] = 1;
values[9] = 0;
for (var i = 0; i < sortedIndices.Length; i++)
{
sortedIndices[i] = i;
}
IlutpElementSorter.SortDoubleIndicesDecreasing(0, sortedIndices.Length - 1, sortedIndices, values);
for (var i = 0; i < sortedIndices.Length; i++)
{
Assert.AreEqual(i, sortedIndices[i], "#01-" + i);
}
}
[Test]
[MultipleAsserts]
public void HeapSortWithRandomDoubleArray()
{
var sortedIndices = new int[10];
Vector values = new DenseVector(10);
values[0] = 5;
values[1] = 2;
values[2] = 8;
values[3] = 6;
values[4] = 0;
values[5] = 4;
values[6] = 1;
values[7] = 7;
values[8] = 3;
values[9] = 9;
for (var i = 0; i < sortedIndices.Length; i++)
{
sortedIndices[i] = i;
}
IlutpElementSorter.SortDoubleIndicesDecreasing(0, sortedIndices.Length - 1, sortedIndices, values);
for (var i = 0; i < sortedIndices.Length; i++)
{
switch (i)
{
case 0:
Assert.AreEqual(9, sortedIndices[i], "#01-" + i);
break;
case 1:
Assert.AreEqual(2, sortedIndices[i], "#01-" + i);
break;
case 2:
Assert.AreEqual(7, sortedIndices[i], "#01-" + i);
break;
case 3:
Assert.AreEqual(3, sortedIndices[i], "#01-" + i);
break;
case 4:
Assert.AreEqual(0, sortedIndices[i], "#01-" + i);
break;
case 5:
Assert.AreEqual(5, sortedIndices[i], "#01-" + i);
break;
case 6:
Assert.AreEqual(8, sortedIndices[i], "#01-" + i);
break;
case 7:
Assert.AreEqual(1, sortedIndices[i], "#01-" + i);
break;
case 8:
Assert.AreEqual(6, sortedIndices[i], "#01-" + i);
break;
case 9:
Assert.AreEqual(4, sortedIndices[i], "#01-" + i);
break;
}
}
}
[Test]
[MultipleAsserts]
public void HeapSortWithDuplicateDoubleEntries()
{
var sortedIndices = new int[10];
Vector values = new DenseVector(10);
values[0] = 1;
values[1] = 1;
values[2] = 1;
values[3] = 1;
values[4] = 2;
values[5] = 2;
values[6] = 2;
values[7] = 2;
values[8] = 3;
values[9] = 4;
for (var i = 0; i < sortedIndices.Length; i++)
{
sortedIndices[i] = i;
}
IlutpElementSorter.SortDoubleIndicesDecreasing(0, sortedIndices.Length - 1, sortedIndices, values);
for (var i = 0; i < sortedIndices.Length; i++)
{
if (i == 0)
{
Assert.AreEqual(9, sortedIndices[i], "#01-" + i);
}
else
{
if (i == 1)
{
Assert.AreEqual(8, sortedIndices[i], "#01-" + i);
}
else
{
if (i < 6)
{
if ((sortedIndices[i] != 4) &&
(sortedIndices[i] != 5) &&
(sortedIndices[i] != 6) &&
(sortedIndices[i] != 7))
{
Assert.Fail("#01-" + i);
}
}
else
{
if ((sortedIndices[i] != 0) &&
(sortedIndices[i] != 1) &&
(sortedIndices[i] != 2) &&
(sortedIndices[i] != 3))
{
Assert.Fail("#01-" + i);
}
}
}
}
}
}
[Test]
[MultipleAsserts]
public void HeapSortWithSpecialConstructedDoubleArray()
{
var sortedIndices = new int[10];
Vector values = new DenseVector(10);
values[0] = 0;
values[1] = 0;
values[2] = 0;
values[3] = 0;
values[4] = 0;
values[5] = 1;
values[6] = 0;
values[7] = 0;
values[8] = 0;
values[9] = 0;
for (var i = 0; i < sortedIndices.Length; i++)
{
sortedIndices[i] = i;
}
IlutpElementSorter.SortDoubleIndicesDecreasing(0, sortedIndices.Length - 1, sortedIndices, values);
for (var i = 0; i < sortedIndices.Length; i++)
{
if (i == 0)
{
Assert.AreEqual(5, sortedIndices[i], "#01-" + i);
break;
}
}
values[0] = 1;
values[1] = 0;
values[2] = 0;
values[3] = 0;
values[4] = 0;
values[5] = 0;
values[6] = 0;
values[7] = 0;
values[8] = 0;
values[9] = 0;
for (var i = 0; i < sortedIndices.Length; i++)
{
sortedIndices[i] = i;
}
IlutpElementSorter.SortDoubleIndicesDecreasing(0, sortedIndices.Length - 1, sortedIndices, values);
for (var i = 0; i < sortedIndices.Length; i++)
{
if (i == 0)
{
Assert.AreEqual(0, sortedIndices[i], "#02-" + i);
break;
}
}
values[0] = 0;
values[1] = 0;
values[2] = 0;
values[3] = 0;
values[4] = 0;
values[5] = 0;
values[6] = 0;
values[7] = 0;
values[8] = 0;
values[9] = 1;
for (var i = 0; i < sortedIndices.Length; i++)
{
sortedIndices[i] = i;
}
IlutpElementSorter.SortDoubleIndicesDecreasing(0, sortedIndices.Length - 1, sortedIndices, values);
for (var i = 0; i < sortedIndices.Length; i++)
{
if (i == 0)
{
Assert.AreEqual(9, sortedIndices[i], "#03-" + i);
break;
}
}
values[0] = 1;
values[1] = 1;
values[2] = 1;
values[3] = 0;
values[4] = 1;
values[5] = 1;
values[6] = 1;
values[7] = 1;
values[8] = 1;
values[9] = 1;
for (var i = 0; i < sortedIndices.Length; i++)
{
sortedIndices[i] = i;
}
IlutpElementSorter.SortDoubleIndicesDecreasing(0, sortedIndices.Length - 1, sortedIndices, values);
for (var i = 0; i < sortedIndices.Length; i++)
{
if (i == 9)
{
Assert.AreEqual(3, sortedIndices[i], "#04-" + i);
break;
}
}
}
[Test]
[MultipleAsserts]
public void HeapSortWithIncreasingDoubleArrayWithLowerBound()
{
var sortedIndices = new int[10];
Vector values = new DenseVector(10);
values[0] = 0;
values[1] = 1;
values[2] = 2;
values[3] = 3;
values[4] = 4;
values[5] = 5;
values[6] = 6;
values[7] = 7;
values[8] = 8;
values[9] = 9;
for (var i = 0; i < sortedIndices.Length; i++)
{
sortedIndices[i] = i;
}
IlutpElementSorter.SortDoubleIndicesDecreasing(4, sortedIndices.Length - 1, sortedIndices, values);
for (var i = 0; i < sortedIndices.Length - 4; i++)
{
Assert.AreEqual(sortedIndices.Length - 1 - i, sortedIndices[i], "#01-" + i);
}
}
[Test]
[MultipleAsserts]
public void HeapSortWithIncreasingDoubleArrayWithUpperBound()
{
var sortedIndices = new int[10];
Vector values = new DenseVector(10);
values[0] = 0;
values[1] = 1;
values[2] = 2;
values[3] = 3;
values[4] = 4;
values[5] = 5;
values[6] = 6;
values[7] = 7;
values[8] = 8;
values[9] = 9;
for (var i = 0; i < sortedIndices.Length; i++)
{
sortedIndices[i] = i;
}
IlutpElementSorter.SortDoubleIndicesDecreasing(0, sortedIndices.Length - 5, sortedIndices, values);
for (var i = 0; i < sortedIndices.Length - 5; i++)
{
Assert.AreEqual(sortedIndices.Length - 5 - i, sortedIndices[i], "#01-" + i);
}
}
[Test]
[MultipleAsserts]
public void HeapSortWithIncreasingDoubleArrayWithLowerAndUpperBound()
{
var sortedIndices = new int[10];
Vector values = new DenseVector(10);
values[0] = 0;
values[1] = 1;
values[2] = 2;
values[3] = 3;
values[4] = 4;
values[5] = 5;
values[6] = 6;
values[7] = 7;
values[8] = 8;
values[9] = 9;
for (var i = 0; i < sortedIndices.Length; i++)
{
sortedIndices[i] = i;
}
IlutpElementSorter.SortDoubleIndicesDecreasing(2, sortedIndices.Length - 3, sortedIndices, values);
for (var i = 0; i < sortedIndices.Length - 4; i++)
{
Assert.AreEqual(sortedIndices.Length - 3 - i, sortedIndices[i], "#01-" + i);
}
}
}
}

254
src/UnitTests/LinearAlgebraTests/Complex/Solvers/Preconditioners/IlutpTest.cs

@ -0,0 +1,254 @@
namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex.Solvers.Preconditioners
{
using System;
using System.Numerics;
using System.Reflection;
using LinearAlgebra.Complex;
using LinearAlgebra.Complex.Solvers.Preconditioners;
using LinearAlgebra.Generic;
using LinearAlgebra.Generic.Solvers.Preconditioners;
using MbUnit.Framework;
[TestFixture]
public sealed class IlutpPreconditionerTest : PreconditionerTest
{
private double _dropTolerance = 0.1;
private double _fillLevel = 1.0;
private double _pivotTolerance = 1.0;
[SetUp]
public void Setup()
{
_dropTolerance = 0.1;
_fillLevel = 1.0;
_pivotTolerance = 1.0;
}
private static T GetMethod<T>(Ilutp ilutp, string methodName)
{
var type = ilutp.GetType();
var methodInfo = type.GetMethod(methodName,
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static,
null,
CallingConventions.Standard,
new Type[0],
null);
var obj = methodInfo.Invoke(ilutp, null);
return (T)obj;
}
private static SparseMatrix GetUpperTriangle(Ilutp ilutp)
{
return GetMethod<SparseMatrix>(ilutp, "UpperTriangle");
}
private static SparseMatrix GetLowerTriangle(Ilutp ilutp)
{
return GetMethod<SparseMatrix>(ilutp, "LowerTriangle");
}
private static int[] GetPivots(Ilutp ilutp)
{
return GetMethod<int[]>(ilutp, "Pivots");
}
private static SparseMatrix CreateReverseUnitMatrix(int size)
{
var matrix = new SparseMatrix(size);
for (var i = 0; i < size; i++)
{
matrix[i, size - 1 - i] = 2;
}
return matrix;
}
private Ilutp InternalCreatePreconditioner()
{
var result = new Ilutp
{
DropTolerance = _dropTolerance,
FillLevel = _fillLevel,
PivotTolerance = _pivotTolerance
};
return result;
}
internal override IPreConditioner<Complex> CreatePreconditioner()
{
_pivotTolerance = 0;
_dropTolerance = 0.0;
_fillLevel = 100;
return InternalCreatePreconditioner();
}
protected override void CheckResult(IPreConditioner<Complex> preconditioner, SparseMatrix matrix, Vector<Complex> vector, Vector<Complex> result)
{
Assert.AreEqual(typeof(Ilutp), preconditioner.GetType(), "#01");
// Compute M * result = product
// compare vector and product. Should be equal
Vector<Complex> product = new DenseVector(result.Count);
matrix.Multiply(result, product);
for (var i = 0; i < product.Count; i++)
{
Assert.IsTrue(vector[i].Real.AlmostEqual(product[i].Real, -Epsilon.Magnitude()), "#02-" + i);
Assert.IsTrue(vector[i].Imaginary.AlmostEqual(product[i].Imaginary, -Epsilon.Magnitude()), "#03-" + i);
}
}
[Test]
[MultipleAsserts]
public void SolveReturningOldVectorWithoutPivoting()
{
const int Size = 10;
var newMatrix = CreateUnitMatrix(Size);
var vector = CreateStandardBcVector(Size);
// set the pivot tolerance to zero so we don't pivot
_pivotTolerance = 0.0;
_dropTolerance = 0.0;
_fillLevel = 100;
var preconditioner = CreatePreconditioner();
preconditioner.Initialize(newMatrix);
Vector<Complex> result = new DenseVector(vector.Count);
preconditioner.Approximate(vector, result);
CheckResult(preconditioner, newMatrix, vector, result);
}
[Test]
[MultipleAsserts]
public void SolveReturningOldVectorWithPivoting()
{
const int Size = 10;
var newMatrix = CreateUnitMatrix(Size);
var vector = CreateStandardBcVector(Size);
// Set the pivot tolerance to 1 so we always pivot (if necessary)
_pivotTolerance = 1.0;
_dropTolerance = 0.0;
_fillLevel = 100;
var preconditioner = CreatePreconditioner();
preconditioner.Initialize(newMatrix);
Vector<Complex> result = new DenseVector(vector.Count);
preconditioner.Approximate(vector, result);
CheckResult(preconditioner, newMatrix, vector, result);
}
[Test]
[MultipleAsserts]
public void CompareWithOriginalDenseMatrixWithoutPivoting()
{
var sparseMatrix = new SparseMatrix(3);
sparseMatrix[0, 0] = -1;
sparseMatrix[0, 1] = 5;
sparseMatrix[0, 2] = 6;
sparseMatrix[1, 0] = 3;
sparseMatrix[1, 1] = -6;
sparseMatrix[1, 2] = 1;
sparseMatrix[2, 0] = 6;
sparseMatrix[2, 1] = 8;
sparseMatrix[2, 2] = 9;
var ilu = new Ilutp
{
PivotTolerance = 0.0,
DropTolerance = 0,
FillLevel = 10
};
ilu.Initialize(sparseMatrix);
var l = GetLowerTriangle(ilu);
// Assert l is lower triagonal
for (var i = 0; i < l.RowCount; i++)
{
for (var j = i + 1; j < l.RowCount; j++)
{
Assert.IsTrue(0.0.AlmostEqual(l[i,j].Magnitude, -Epsilon.Magnitude()), "#01-" + i + "-" + j);
}
}
var u = GetUpperTriangle(ilu);
// Assert u is upper triagonal
for (var i = 0; i < u.RowCount; i++)
{
for (var j = 0; j < i; j++)
{
Assert.IsTrue(0.0.AlmostEqual(u[i,j].Magnitude, -Epsilon.Magnitude()), "#02-" + i + "-" + j);
}
}
var original = l.Multiply(u);
for (var i = 0; i < sparseMatrix.RowCount; i++)
{
for (var j = 0; j < sparseMatrix.ColumnCount; j++)
{
Assert.IsTrue(sparseMatrix[i,j].Real.AlmostEqual(original[i, j].Real, -Epsilon.Magnitude()), "#03-" + i + "-" + j);
Assert.IsTrue(sparseMatrix[i, j].Imaginary.AlmostEqual(original[i, j].Imaginary, -Epsilon.Magnitude()), "#04-" + i + "-" + j);
}
}
}
[Test]
[MultipleAsserts]
public void CompareWithOriginalDenseMatrixWithPivoting()
{
var sparseMatrix = new SparseMatrix(3);
sparseMatrix[0, 0] = -1;
sparseMatrix[0, 1] = 5;
sparseMatrix[0, 2] = 6;
sparseMatrix[1, 0] = 3;
sparseMatrix[1, 1] = -6;
sparseMatrix[1, 2] = 1;
sparseMatrix[2, 0] = 6;
sparseMatrix[2, 1] = 8;
sparseMatrix[2, 2] = 9;
var ilu = new Ilutp
{
PivotTolerance = 1.0,
DropTolerance = 0,
FillLevel = 10
};
ilu.Initialize(sparseMatrix);
var l = GetLowerTriangle(ilu);
var u = GetUpperTriangle(ilu);
var pivots = GetPivots(ilu);
var p = new SparseMatrix(l.RowCount);
for (var i = 0; i < p.RowCount; i++)
{
p[i, pivots[i]] = 1.0;
}
var temp = l.Multiply(u);
var original = temp.Multiply(p);
for (var i = 0; i < sparseMatrix.RowCount; i++)
{
for (var j = 0; j < sparseMatrix.ColumnCount; j++)
{
Assert.IsTrue(sparseMatrix[i, j].Real.AlmostEqual(original[i, j].Real, -Epsilon.Magnitude()), "#01-" + i + "-" + j);
Assert.IsTrue(sparseMatrix[i, j].Imaginary.AlmostEqual(original[i, j].Imaginary, -Epsilon.Magnitude()), "#02-" + i + "-" + j);
}
}
}
[Test]
[MultipleAsserts]
public void SolveWithPivoting()
{
const int Size = 10;
var newMatrix = CreateReverseUnitMatrix(Size);
var vector = CreateStandardBcVector(Size);
var preconditioner = new Ilutp
{
PivotTolerance = 1.0,
DropTolerance = 0,
FillLevel = 10
};
preconditioner.Initialize(newMatrix);
Vector<Complex> result = new DenseVector(vector.Count);
preconditioner.Approximate(vector, result);
CheckResult(preconditioner, newMatrix, vector, result);
}
}
}

87
src/UnitTests/LinearAlgebraTests/Complex/Solvers/Preconditioners/IncompleteLUTest.cs

@ -0,0 +1,87 @@
namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex.Solvers.Preconditioners
{
using System;
using System.Numerics;
using System.Reflection;
using LinearAlgebra.Complex;
using LinearAlgebra.Complex.Solvers.Preconditioners;
using LinearAlgebra.Generic;
using LinearAlgebra.Generic.Solvers.Preconditioners;
using MbUnit.Framework;
[TestFixture]
public sealed class IncompleteLUFactorizationTest : PreconditionerTest
{
private static T GetMethod<T>(IncompleteLU ilu, string methodName)
{
var type = ilu.GetType();
var methodInfo = type.GetMethod(methodName,
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static,
null,
CallingConventions.Standard,
new Type[0],
null);
var obj = methodInfo.Invoke(ilu, null);
return (T)obj;
}
private static Matrix<Complex> GetUpperTriangle(IncompleteLU ilu)
{
return GetMethod<Matrix<Complex>>(ilu, "UpperTriangle");
}
private static Matrix<Complex> GetLowerTriangle(IncompleteLU ilu)
{
return GetMethod<Matrix<Complex>>(ilu, "LowerTriangle");
}
internal override IPreConditioner<Complex> CreatePreconditioner()
{
return new IncompleteLU();
}
protected override void CheckResult(IPreConditioner<Complex> preconditioner, SparseMatrix matrix, Vector<Complex> vector, Vector<Complex> result)
{
Assert.AreEqual(typeof(IncompleteLU), preconditioner.GetType(), "#01");
// Compute M * result = product
// compare vector and product. Should be equal
Vector<Complex> product = new DenseVector(result.Count);
matrix.Multiply(result, product);
for (var i = 0; i < product.Count; i++)
{
Assert.IsTrue(vector[i].Real.AlmostEqual(product[i].Real, -Epsilon.Magnitude()), "#02-" + i);
Assert.IsTrue(vector[i].Imaginary.AlmostEqual(product[i].Imaginary, -Epsilon.Magnitude()), "#03-" + i);
}
}
[Test]
[MultipleAsserts]
public void CompareWithOriginalDenseMatrix()
{
var sparseMatrix = new SparseMatrix(3);
sparseMatrix[0, 0] = -1;
sparseMatrix[0, 1] = 5;
sparseMatrix[0, 2] = 6;
sparseMatrix[1, 0] = 3;
sparseMatrix[1, 1] = -6;
sparseMatrix[1, 2] = 1;
sparseMatrix[2, 0] = 6;
sparseMatrix[2, 1] = 8;
sparseMatrix[2, 2] = 9;
var ilu = new IncompleteLU();
ilu.Initialize(sparseMatrix);
var original = GetLowerTriangle(ilu).Multiply(GetUpperTriangle(ilu));
for (var i = 0; i < sparseMatrix.RowCount; i++)
{
for (var j = 0; j < sparseMatrix.ColumnCount; j++)
{
Assert.IsTrue(sparseMatrix[i, j].Real.AlmostEqual(original[i, j].Real, -Epsilon.Magnitude()), "#01-" + i + "-" + j);
Assert.IsTrue(sparseMatrix[i, j].Imaginary.AlmostEqual(original[i, j].Imaginary, -Epsilon.Magnitude()), "#02-" + i + "-" + j);
}
}
}
}
}

128
src/UnitTests/LinearAlgebraTests/Complex/Solvers/Preconditioners/PreConditionerTest.cs

@ -0,0 +1,128 @@
namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex.Solvers.Preconditioners
{
using System.Numerics;
using LinearAlgebra.Complex;
using LinearAlgebra.Generic;
using LinearAlgebra.Generic.Solvers.Preconditioners;
using MbUnit.Framework;
public abstract class PreconditionerTest
{
protected const double Epsilon = 1e-10;
internal SparseMatrix CreateUnitMatrix(int size)
{
var matrix = new SparseMatrix(size);
for (var i = 0; i < size; i++)
{
matrix[i, i] = 2;
}
return matrix;
}
protected Vector<Complex> CreateStandardBcVector(int size)
{
Vector<Complex> vector = new DenseVector(size);
for (var i = 0; i < size; i++)
{
vector[i] = i + 1;
}
return vector;
}
internal abstract IPreConditioner<Complex> CreatePreconditioner();
protected abstract void CheckResult(IPreConditioner<Complex> preconditioner, SparseMatrix matrix, Vector<Complex> vector, Vector<Complex> result);
[Test]
[MultipleAsserts]
public void ApproximateWithUnitMatrixReturningNewVector()
{
const int Size = 10;
var newMatrix = CreateUnitMatrix(Size);
var vector = CreateStandardBcVector(Size);
var preconditioner = CreatePreconditioner();
preconditioner.Initialize(newMatrix);
var result = preconditioner.Approximate(vector);
CheckResult(preconditioner, newMatrix, vector, result);
}
[Test]
[MultipleAsserts]
public void ApproximateReturningOldVector()
{
const int Size = 10;
var newMatrix = CreateUnitMatrix(Size);
var vector = CreateStandardBcVector(Size);
var preconditioner = CreatePreconditioner();
preconditioner.Initialize(newMatrix);
Vector<Complex> result = new DenseVector(vector.Count);
preconditioner.Approximate(vector, result);
CheckResult(preconditioner, newMatrix, vector, result);
}
[Test]
[ExpectedArgumentException]
public void ApproximateWithVectorWithIncorrectLength()
{
const int Size = 10;
var newMatrix = CreateUnitMatrix(Size);
var vector = CreateStandardBcVector(Size);
var preconditioner = CreatePreconditioner();
preconditioner.Initialize(newMatrix);
Vector<Complex> result = new DenseVector(vector.Count + 10);
preconditioner.Approximate(vector, result);
}
[Test]
[ExpectedArgumentNullException]
public void ApproximateWithNullVector()
{
const int Size = 10;
var newMatrix = CreateUnitMatrix(Size);
var vector = CreateStandardBcVector(Size);
var preconditioner = CreatePreconditioner();
preconditioner.Initialize(newMatrix);
Vector<Complex> result = new DenseVector(vector.Count + 10);
preconditioner.Approximate(null, result);
}
[Test]
[ExpectedArgumentNullException]
public void ApproximateWithNullResultVector()
{
const int Size = 10;
var newMatrix = CreateUnitMatrix(Size);
var vector = CreateStandardBcVector(Size);
var preconditioner = CreatePreconditioner();
preconditioner.Initialize(newMatrix);
Vector<Complex> result = null;
preconditioner.Approximate(vector, result);
}
[Test]
[ExpectedArgumentException]
public void ApproximateWithNonInitializedPreconditioner()
{
const int Size = 10;
var vector = CreateStandardBcVector(Size);
var preconditioner = CreatePreconditioner();
preconditioner.Approximate(vector);
}
}
}

30
src/UnitTests/LinearAlgebraTests/Complex/Solvers/Preconditioners/UnitPreconditionerTest.cs

@ -0,0 +1,30 @@
namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex.Solvers.Preconditioners
{
using System.Numerics;
using LinearAlgebra.Complex;
using LinearAlgebra.Complex.Solvers.Preconditioners;
using LinearAlgebra.Generic;
using LinearAlgebra.Generic.Solvers.Preconditioners;
using MbUnit.Framework;
[TestFixture]
public sealed class UnitPreconditionerTest : PreconditionerTest
{
internal override IPreConditioner<Complex> CreatePreconditioner()
{
return new UnitPreconditioner();
}
protected override void CheckResult(IPreConditioner<Complex> preconditioner, SparseMatrix matrix, Vector<Complex> vector, Vector<Complex> result)
{
Assert.AreEqual(typeof(UnitPreconditioner), preconditioner.GetType(), "#01");
// Unit preconditioner is doing nothing. Vector and result should be equal
for (var i = 0; i < vector.Count; i++)
{
Assert.IsTrue(vector[i] == result[i], "#02-" + i);
}
}
}
}

234
src/UnitTests/LinearAlgebraTests/Complex/Solvers/StopCriterium/DivergenceStopCriteriumTest.cs

@ -0,0 +1,234 @@
namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex.Solvers.StopCriterium
{
using System.Numerics;
using LinearAlgebra.Complex;
using LinearAlgebra.Complex.Solvers.StopCriterium;
using LinearAlgebra.Generic.Solvers.Status;
using MbUnit.Framework;
[TestFixture]
public sealed class DivergenceStopCriteriumTest
{
[Test]
[ExpectedArgumentOutOfRangeException]
public void CreateWithNegativeMaximumIncrease()
{
new DivergenceStopCriterium(-0.1);
}
[Test]
[ExpectedArgumentOutOfRangeException]
public void CreateWithIllegalMinimumIterations()
{
new DivergenceStopCriterium(2);
}
[Test]
[MultipleAsserts]
public void Create()
{
var criterium = new DivergenceStopCriterium(0.1, 3);
Assert.IsNotNull(criterium, "There should be a criterium");
Assert.AreEqual(0.1, criterium.MaximumRelativeIncrease, "Incorrect maximum");
Assert.AreEqual(3, criterium.MinimumNumberOfIterations, "Incorrect iteration count");
}
[Test]
[MultipleAsserts]
public void ResetMaximumIncrease()
{
var criterium = new DivergenceStopCriterium(0.5, 3);
Assert.IsNotNull(criterium, "There should be a criterium");
Assert.AreEqual(0.5, criterium.MaximumRelativeIncrease, "Incorrect maximum");
criterium.ResetMaximumRelativeIncreaseToDefault();
Assert.AreEqual(DivergenceStopCriterium.DefaultMaximumRelativeIncrease, criterium.MaximumRelativeIncrease, "Incorrect value");
}
[Test]
[MultipleAsserts]
public void ResetMinimumIterationsBelowMaximum()
{
var criterium = new DivergenceStopCriterium(0.5, 15);
Assert.IsNotNull(criterium, "There should be a criterium");
Assert.AreEqual(15, criterium.MinimumNumberOfIterations, "Incorrect iteration count");
criterium.ResetNumberOfIterationsToDefault();
Assert.AreEqual(DivergenceStopCriterium.DefaultMinimumNumberOfIterations, criterium.MinimumNumberOfIterations, "Incorrect value");
}
[Test]
[ExpectedArgumentOutOfRangeException]
public void DetermineStatusWithIllegalIterationNumber()
{
var criterium = new DivergenceStopCriterium(0.5, 15);
criterium.DetermineStatus(-1,
new DenseVector(3, 4),
new DenseVector(3, 5),
new DenseVector(3, 6));
}
[Test]
[ExpectedArgumentNullException]
public void DetermineStatusWithNullResidualVector()
{
var criterium = new DivergenceStopCriterium(0.5, 15);
criterium.DetermineStatus(1,
new DenseVector(3, 4),
new DenseVector(3, 5),
null);
}
[Test]
[MultipleAsserts]
public void DetermineStatusWithTooFewIterations()
{
const double Increase = 0.5;
const int Iterations = 10;
var criterium = new DivergenceStopCriterium(Increase, Iterations);
// Add residuals. We should not diverge because we'll have to few iterations
for (var i = 0; i < Iterations - 1; i++)
{
criterium.DetermineStatus(i,
new DenseVector(new [] { new Complex(1.0, 0) }),
new DenseVector(new[] { new Complex(1.0, 0) }),
new DenseVector(new[] { new Complex((i + 1) * (Increase + 0.1), 0) }));
Assert.IsInstanceOfType(typeof(CalculationRunning), criterium.Status, "Status check fail.");
}
}
[Test]
[MultipleAsserts]
public void DetermineStatusWithNoDivergence()
{
const double Increase = 0.5;
const int Iterations = 10;
var criterium = new DivergenceStopCriterium(Increase, Iterations);
// Add residuals. We should not diverge because we won't have enough increase
for (var i = 0; i < Iterations * 2; i++)
{
criterium.DetermineStatus(i,
new DenseVector(new[] { new Complex(1.0, 0) }),
new DenseVector(new[] { new Complex(1.0, 0) }),
new DenseVector(new [] { new Complex((i + 1) * (Increase - 0.01), 0) }));
Assert.IsInstanceOfType(typeof(CalculationRunning), criterium.Status, "Status check fail.");
}
}
[Test]
[MultipleAsserts]
public void DetermineStatusWithDivergenceThroughNaN()
{
const double Increase = 0.5;
const int Iterations = 10;
var criterium = new DivergenceStopCriterium(Increase, Iterations);
// Add residuals. We should not diverge because we'll have to few iterations
for (var i = 0; i < Iterations - 5; i++)
{
criterium.DetermineStatus(i,
new DenseVector(new[] { new Complex(1.0, 0) }),
new DenseVector(new[] { new Complex(1.0, 0) }),
new DenseVector(new [] { new Complex((i + 1) * (Increase - 0.01), 0) }));
Assert.IsInstanceOfType(typeof(CalculationRunning), criterium.Status, "Status check fail.");
}
// Now make it fail by throwing in a NaN
criterium.DetermineStatus(Iterations,
new DenseVector(new[] { new Complex(1.0, 0) }),
new DenseVector(new[] { new Complex(1.0, 0) }),
new DenseVector(new [] { new Complex(double.NaN, 0) }));
Assert.IsInstanceOfType(typeof(CalculationDiverged), criterium.Status, "Status check fail.");
}
[Test]
[MultipleAsserts]
public void DetermineStatusWithDivergence()
{
const double Increase = 0.5;
const int Iterations = 10;
var criterium = new DivergenceStopCriterium(Increase, Iterations);
// Add residuals. We should not diverge because we'll have one to few iterations
double previous = 1;
for (var i = 0; i < Iterations - 1; i++)
{
previous *= (1 + Increase + 0.01);
criterium.DetermineStatus(i,
new DenseVector(new[] { new Complex(1.0, 0) }),
new DenseVector(new[] { new Complex(1.0, 0) }),
new DenseVector(new[] { new Complex(previous, 0) }));
Assert.IsInstanceOfType(typeof(CalculationRunning), criterium.Status, "Status check fail.");
}
// Add the final residual. Now we should have divergence
previous *= (1 + Increase + 0.01);
criterium.DetermineStatus(Iterations - 1,
new DenseVector(new[] { new Complex(1.0, 0) }),
new DenseVector(new[] { new Complex(1.0, 0) }),
new DenseVector(new[] { new Complex(previous, 0) }));
Assert.IsInstanceOfType(typeof(CalculationDiverged), criterium.Status, "Status check fail.");
}
[Test]
[MultipleAsserts]
public void ResetCalculationState()
{
const double Increase = 0.5;
const int Iterations = 10;
var criterium = new DivergenceStopCriterium(Increase, Iterations);
// Add residuals. Blow it up instantly
criterium.DetermineStatus(1,
new DenseVector(new[] { new Complex(1.0, 0) }),
new DenseVector(new[] { new Complex(1.0, 0) }),
new DenseVector(new[] { new Complex(double.NaN, 0) }));
Assert.IsInstanceOfType(typeof(CalculationDiverged), criterium.Status, "Status check fail.");
// Reset the state
criterium.ResetToPrecalculationState();
Assert.AreEqual(Increase, criterium.MaximumRelativeIncrease, "Incorrect maximum");
Assert.AreEqual(Iterations, criterium.MinimumNumberOfIterations, "Incorrect iteration count");
Assert.IsInstanceOfType(typeof(CalculationIndetermined), criterium.Status, "Status check fail.");
}
[Test]
[MultipleAsserts]
public void Clone()
{
const double Increase = 0.5;
const int Iterations = 10;
var criterium = new DivergenceStopCriterium(Increase, Iterations);
Assert.IsNotNull(criterium, "There should be a criterium");
var clone = criterium.Clone();
Assert.IsInstanceOfType(typeof(DivergenceStopCriterium), clone, "Wrong criterium type");
var clonedCriterium = clone as DivergenceStopCriterium;
Assert.IsNotNull(clonedCriterium);
// ReSharper disable PossibleNullReferenceException
Assert.AreEqual(criterium.MaximumRelativeIncrease, clonedCriterium.MaximumRelativeIncrease, "Incorrect maximum");
Assert.AreEqual(criterium.MinimumNumberOfIterations, clonedCriterium.MinimumNumberOfIterations, "Incorrect iteration count");
// ReSharper restore PossibleNullReferenceException
}
}
}

134
src/UnitTests/LinearAlgebraTests/Complex/Solvers/StopCriterium/FailureStopCriteriumTest.cs

@ -0,0 +1,134 @@
namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex.Solvers.StopCriterium
{
using System.Numerics;
using LinearAlgebra.Complex;
using LinearAlgebra.Complex.Solvers.StopCriterium;
using LinearAlgebra.Generic.Solvers.Status;
using MbUnit.Framework;
[TestFixture]
public sealed class FailureStopCriteriumTest
{
[Test]
[MultipleAsserts]
public void Create()
{
var criterium = new FailureStopCriterium();
Assert.IsNotNull(criterium, "Should have a criterium now");
}
[Test]
[ExpectedArgumentOutOfRangeException]
public void DetermineStatusWithIllegalIterationNumber()
{
var criterium = new FailureStopCriterium();
Assert.IsNotNull(criterium, "There should be a criterium");
criterium.DetermineStatus(-1, new DenseVector(3, 4), new DenseVector(3, 5), new DenseVector(3, 6));
}
[Test]
[ExpectedArgumentNullException]
public void DetermineStatusWithNullSolutionVector()
{
var criterium = new FailureStopCriterium();
Assert.IsNotNull(criterium, "There should be a criterium");
criterium.DetermineStatus(1, null, new DenseVector(3, 6), new DenseVector(4, 4));
}
[Test]
[ExpectedArgumentNullException]
public void DetermineStatusWithNullResidualVector()
{
var criterium = new FailureStopCriterium();
Assert.IsNotNull(criterium, "There should be a criterium");
criterium.DetermineStatus(1, new DenseVector(3, 4), new DenseVector(3, 6), null);
}
[Test]
[ExpectedArgumentException]
public void DetermineStatusWithNonMatchingVectors()
{
var criterium = new FailureStopCriterium();
Assert.IsNotNull(criterium, "There should be a criterium");
criterium.DetermineStatus(1, new DenseVector(3, 4), new DenseVector(3, 6), new DenseVector(4, 4));
}
[Test]
[MultipleAsserts]
public void DetermineStatusWithResidualNaN()
{
var criterium = new FailureStopCriterium();
Assert.IsNotNull(criterium, "There should be a criterium");
var solution = new DenseVector(new[] { new Complex(1.0, 0), new Complex(1.0, 0), new Complex(2.0, 0) });
var source = new DenseVector(new[] { new Complex(1001.0, 0), Complex.Zero, new Complex(2003.0, 0) });
var residual = new DenseVector(new[] { new Complex(1000, 0), new Complex(double.NaN, 0), new Complex(2001, 0) });
criterium.DetermineStatus(5, solution, source, residual);
Assert.IsInstanceOfType(typeof(CalculationFailure), criterium.Status, "Should be failed");
}
[Test]
[MultipleAsserts]
public void DetermineStatusWithSolutionNaN()
{
var criterium = new FailureStopCriterium();
Assert.IsNotNull(criterium, "There should be a criterium");
var solution = new DenseVector(new[] { new Complex(1.0, 0), new Complex(1.0, 0), new Complex(double.NaN, 0) });
var source = new DenseVector(new[] { new Complex(1001.0, 0), Complex.Zero, new Complex(2003.0, 0) });
var residual = new DenseVector(new[] { new Complex(1000, 0), new Complex(1000, 0), new Complex(2001, 0) });
criterium.DetermineStatus(5, solution, source, residual);
Assert.IsInstanceOfType(typeof(CalculationFailure), criterium.Status, "Should be failed");
}
[Test]
[MultipleAsserts]
public void DetermineStatus()
{
var criterium = new FailureStopCriterium();
Assert.IsNotNull(criterium, "There should be a criterium");
var solution = new DenseVector(new[] { new Complex(3.0, 0), new Complex(2.0, 0), new Complex(1, 0) });
var source = new DenseVector(new[] { new Complex(1001.0, 0), Complex.Zero, new Complex(2003.0, 0) });
var residual = new DenseVector(new[] { new Complex(1.0, 0), new Complex(2.0, 0), new Complex(3, 0) });
criterium.DetermineStatus(5, solution, source, residual);
Assert.IsInstanceOfType(typeof(CalculationRunning), criterium.Status, "Should be running");
}
[Test]
[MultipleAsserts]
public void ResetCalculationState()
{
var criterium = new FailureStopCriterium();
Assert.IsNotNull(criterium, "There should be a criterium");
var solution = new DenseVector(new[] { new Complex(1.0, 0), new Complex(1.0, 0), new Complex(2, 0) });
var source = new DenseVector(new[] { new Complex(1001.0, 0), Complex.Zero, new Complex(2003.0, 0) });
var residual = new DenseVector(new[] { new Complex(1000, 0), new Complex(1000, 0), new Complex(2001, 0) });
criterium.DetermineStatus(5, solution, source, residual);
Assert.IsInstanceOfType(typeof(CalculationRunning), criterium.Status, "Should be running");
criterium.ResetToPrecalculationState();
Assert.IsInstanceOfType(typeof(CalculationIndetermined), criterium.Status, "Should not have started");
}
[Test]
[MultipleAsserts]
public void Clone()
{
var criterium = new FailureStopCriterium();
Assert.IsNotNull(criterium, "There should be a criterium");
var clone = criterium.Clone();
Assert.IsInstanceOfType(typeof(FailureStopCriterium), clone, "Wrong criterium type");
}
}
}

96
src/UnitTests/LinearAlgebraTests/Complex/Solvers/StopCriterium/IterationCountStopCriteriumTest.cs

@ -0,0 +1,96 @@
namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex.Solvers.StopCriterium
{
using LinearAlgebra.Complex;
using LinearAlgebra.Complex.Solvers.StopCriterium;
using LinearAlgebra.Generic.Solvers.Status;
using MbUnit.Framework;
[TestFixture]
public sealed class IterationCountStopCriteriumTest
{
[Test]
[ExpectedArgumentOutOfRangeException]
public void CreateWithIllegalMinimumIterations()
{
new IterationCountStopCriterium(-1);
}
[Test]
[MultipleAsserts]
public void Create()
{
var criterium = new IterationCountStopCriterium(10);
Assert.IsNotNull(criterium, "A criterium should have been created");
}
[Test]
[MultipleAsserts]
public void ResetMaximumIterations()
{
var criterium = new IterationCountStopCriterium(10);
Assert.IsNotNull(criterium, "A criterium should have been created");
Assert.AreEqual(10, criterium.MaximumNumberOfIterations, "Incorrect maximum number of iterations");
criterium.ResetMaximumNumberOfIterationsToDefault();
Assert.AreNotEqual(10, criterium.MaximumNumberOfIterations, "Should have reset");
Assert.AreEqual(IterationCountStopCriterium.DefaultMaximumNumberOfIterations, criterium.MaximumNumberOfIterations, "Reset to the wrong value");
}
[Test]
[ExpectedArgumentOutOfRangeException]
public void DetermineStatusWithIllegalIterationNumber()
{
var criterium = new IterationCountStopCriterium(10);
Assert.IsNotNull(criterium, "A criterium should have been created");
criterium.DetermineStatus(-1, new DenseVector(3, 1), new DenseVector(3, 2), new DenseVector(3,3));
Assert.Fail();
}
[Test]
[MultipleAsserts]
public void DetermineStatus()
{
var criterium = new IterationCountStopCriterium(10);
Assert.IsNotNull(criterium, "A criterium should have been created");
criterium.DetermineStatus(5, new DenseVector(3, 1), new DenseVector(3, 2), new DenseVector(3, 3));
Assert.IsInstanceOfType(typeof(CalculationRunning), criterium.Status, "Should be running");
criterium.DetermineStatus(10, new DenseVector(3, 1), new DenseVector(3, 2), new DenseVector(3, 3));
Assert.IsInstanceOfType(typeof(CalculationStoppedWithoutConvergence), criterium.Status, "Should be finished");
}
[Test]
[MultipleAsserts]
public void ResetCalculationState()
{
var criterium = new IterationCountStopCriterium(10);
Assert.IsNotNull(criterium, "A criterium should have been created");
criterium.DetermineStatus(5, new DenseVector(3, 1), new DenseVector(3, 2), new DenseVector(3, 3));
Assert.IsInstanceOfType(typeof(CalculationRunning), criterium.Status, "Should be running");
criterium.ResetToPrecalculationState();
Assert.IsInstanceOfType(typeof(CalculationIndetermined), criterium.Status, "Should not have started");
}
[Test]
[MultipleAsserts]
public void Clone()
{
var criterium = new IterationCountStopCriterium(10);
Assert.IsNotNull(criterium, "A criterium should have been created");
Assert.AreEqual(10, criterium.MaximumNumberOfIterations, "Incorrect maximum");
var clone = criterium.Clone();
Assert.IsInstanceOfType(typeof(IterationCountStopCriterium), clone, "Wrong criterium type");
var clonedCriterium = clone as IterationCountStopCriterium;
Assert.IsNotNull(clonedCriterium);
// ReSharper disable PossibleNullReferenceException
Assert.AreEqual(criterium.MaximumNumberOfIterations, clonedCriterium.MaximumNumberOfIterations, "Clone failed");
// ReSharper restore PossibleNullReferenceException
}
}
}

262
src/UnitTests/LinearAlgebraTests/Complex/Solvers/StopCriterium/ResidualStopCriteriumTest.cs

@ -0,0 +1,262 @@
namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex.Solvers.StopCriterium
{
using System.Numerics;
using LinearAlgebra.Complex;
using LinearAlgebra.Complex.Solvers.StopCriterium;
using LinearAlgebra.Generic.Solvers.Status;
using MbUnit.Framework;
[TestFixture]
public sealed class ResidualStopCriteriumTest
{
[Test]
[ExpectedArgumentOutOfRangeException]
public void CreateWithNegativeMaximum()
{
new ResidualStopCriterium(-0.1);
Assert.Fail();
}
[Test]
[ExpectedArgumentOutOfRangeException]
public void CreateWithIllegalMinimumIterations()
{
new ResidualStopCriterium(-1);
Assert.Fail();
}
[Test]
[MultipleAsserts]
public void Create()
{
var criterium = new ResidualStopCriterium(1e-8, 50);
Assert.IsNotNull(criterium, "There should be a criterium");
Assert.AreEqual(1e-8, criterium.Maximum, "Incorrect maximum");
Assert.AreEqual(50, criterium.MinimumIterationsBelowMaximum, "Incorrect iteration count");
}
[Test]
[MultipleAsserts]
public void ResetMaximum()
{
var criterium = new ResidualStopCriterium(1e-8, 50);
Assert.IsNotNull(criterium, "There should be a criterium");
criterium.ResetMaximumResidualToDefault();
Assert.AreEqual(ResidualStopCriterium.DefaultMaximumResidual, criterium.Maximum, "Incorrect maximum");
}
[Test]
[MultipleAsserts]
public void ResetMinimumIterationsBelowMaximum()
{
var criterium = new ResidualStopCriterium(1e-8, 50);
Assert.IsNotNull(criterium, "There should be a criterium");
criterium.ResetMinimumIterationsBelowMaximumToDefault();
Assert.AreEqual(ResidualStopCriterium.DefaultMinimumIterationsBelowMaximum, criterium.MinimumIterationsBelowMaximum, "Incorrect iteration count");
}
[Test]
[ExpectedArgumentOutOfRangeException]
public void DetermineStatusWithIllegalIterationNumber()
{
var criterium = new ResidualStopCriterium(1e-8, 50);
Assert.IsNotNull(criterium, "There should be a criterium");
criterium.DetermineStatus(-1,
new DenseVector(3, 4),
new DenseVector(3, 5),
new DenseVector(3, 6));
Assert.Fail();
}
[Test]
[ExpectedArgumentNullException]
public void DetermineStatusWithNullSolutionVector()
{
var criterium = new ResidualStopCriterium(1e-8, 50);
Assert.IsNotNull(criterium, "There should be a criterium");
criterium.DetermineStatus(1,
null,
new DenseVector(3, 5),
new DenseVector(3, 6));
Assert.Fail();
}
[Test]
[ExpectedArgumentNullException]
public void DetermineStatusWithNullSourceVector()
{
var criterium = new ResidualStopCriterium(1e-8, 50);
Assert.IsNotNull(criterium, "There should be a criterium");
criterium.DetermineStatus(1,
new DenseVector(3, 4),
null,
new DenseVector(3, 6));
Assert.Fail();
}
[Test]
[ExpectedArgumentNullException]
public void DetermineStatusWithNullResidualVector()
{
var criterium = new ResidualStopCriterium(1e-8, 50);
Assert.IsNotNull(criterium, "There should be a criterium");
criterium.DetermineStatus(1,
new DenseVector(3, 4),
new DenseVector(3, 5),
null);
}
[Test]
[ExpectedArgumentException]
public void DetermineStatusWithNonMatchingSolutionVector()
{
var criterium = new ResidualStopCriterium(1e-8, 50);
Assert.IsNotNull(criterium, "There should be a criterium");
criterium.DetermineStatus(1,
new DenseVector(4, 4),
new DenseVector(3, 4),
new DenseVector(3, 4));
}
[Test]
[ExpectedArgumentException]
public void DetermineStatusWithNonMatchingSourceVector()
{
var criterium = new ResidualStopCriterium(1e-8, 50);
Assert.IsNotNull(criterium, "There should be a criterium");
criterium.DetermineStatus(1,
new DenseVector(3, 4),
new DenseVector(4, 4),
new DenseVector(3, 4));
}
[Test]
[ExpectedArgumentException]
public void DetermineStatusWithNonMatchingResidualVector()
{
var criterium = new ResidualStopCriterium(1e-8, 50);
Assert.IsNotNull(criterium, "There should be a criterium");
criterium.DetermineStatus(1,
new DenseVector(3, 4),
new DenseVector(3, 4),
new DenseVector(4, 4));
}
[Test]
[MultipleAsserts]
public void DetermineStatusWithSourceNaN()
{
var criterium = new ResidualStopCriterium(1e-3, 10);
Assert.IsNotNull(criterium, "There should be a criterium");
var solution = new DenseVector(new[] { new Complex(1.0, 1), new Complex(1.0, 1), new Complex(2.0, 1) });
var source = new DenseVector(new[] { new Complex(1.0, 1), new Complex(1.0, 1), new Complex(double.NaN, 1) });
var residual = new DenseVector(new[] { new Complex(1000.0, 1), new Complex(1000.0, 1), new Complex(2001.0, 1) });
criterium.DetermineStatus(5, solution, source, residual);
Assert.IsInstanceOfType(typeof(CalculationDiverged), criterium.Status, "Should be diverged");
}
[Test]
[MultipleAsserts]
public void DetermineStatusWithResidualNaN()
{
var criterium = new ResidualStopCriterium(1e-3, 10);
Assert.IsNotNull(criterium, "There should be a criterium");
var solution = new DenseVector(new[] { new Complex(1.0, 1), new Complex(1.0, 1), new Complex(2.0, 1) });
var source = new DenseVector(new[] { new Complex(1.0, 1), new Complex(1.0, 1), new Complex(2.0, 1) });
var residual = new DenseVector(new[] { new Complex(1000.0, 1), new Complex(double.NaN, 1), new Complex(2001.0, 1) });
criterium.DetermineStatus(5, solution, source, residual);
Assert.IsInstanceOfType(typeof(CalculationDiverged), criterium.Status, "Should be diverged");
}
// Bugfix: The unit tests for the BiCgStab solver run with a super simple matrix equation
// which converges at the first iteration. The default settings for the
// residual stop criterium should be able to handle this.
[Test]
[MultipleAsserts]
public void DetermineStatusWithConvergenceAtFirstIteration()
{
var criterium = new ResidualStopCriterium();
Assert.IsNotNull(criterium, "There should be a criterium");
var solution = new DenseVector(new[] { Complex.One, Complex.One, Complex.One });
var source = new DenseVector(new[] { Complex.One, Complex.One, Complex.One });
var residual = new DenseVector(new[] { Complex.Zero, Complex.Zero, Complex.Zero });
criterium.DetermineStatus(0, solution, source, residual);
Assert.IsInstanceOfType(typeof(CalculationConverged), criterium.Status, "Should be done");
}
[Test]
[MultipleAsserts]
public void DetermineStatus()
{
var criterium = new ResidualStopCriterium(1e-3, 10);
Assert.IsNotNull(criterium, "There should be a criterium");
// Note that the solution vector isn't actually being used so ...
var solution = new DenseVector(new[] { new Complex(double.NaN, double.NaN), new Complex(double.NaN, double.NaN), new Complex(double.NaN, double.NaN) });
// Set the source values
var source = new DenseVector(new[] { new Complex(1.000, 1), new Complex(1.000, 1), new Complex(2.001, 1) });
// Set the residual values
var residual = new DenseVector(new[] { new Complex(0.001, 0), new Complex(0.001, 0), new Complex(0.002, 0) });
criterium.DetermineStatus(5, solution, source, residual);
Assert.IsInstanceOfType(typeof(CalculationRunning), criterium.Status, "Should still be running");
criterium.DetermineStatus(16, solution, source, residual);
Assert.IsInstanceOfType(typeof(CalculationConverged), criterium.Status, "Should be done");
}
[Test]
[MultipleAsserts]
public void ResetCalculationState()
{
var criterium = new ResidualStopCriterium(1e-3, 10);
Assert.IsNotNull(criterium, "There should be a criterium");
var solution = new DenseVector(new[] { new Complex(0.001, 1), new Complex(0.001, 1), new Complex(0.002, 1) });
var source = new DenseVector(new[] { new Complex(0.001, 1), new Complex(0.001, 1), new Complex(0.002, 1) });
var residual = new DenseVector(new[] { new Complex(1.000, 0), new Complex(1.000, 0), new Complex(2.001, 0) });
criterium.DetermineStatus(5, solution, source, residual);
Assert.IsInstanceOfType(typeof(CalculationRunning), criterium.Status, "Should be running");
criterium.ResetToPrecalculationState();
Assert.IsInstanceOfType(typeof(CalculationIndetermined), criterium.Status, "Should not have started");
}
[Test]
[MultipleAsserts]
public void Clone()
{
var criterium = new ResidualStopCriterium(1e-3, 10);
Assert.IsNotNull(criterium, "There should be a criterium");
var clone = criterium.Clone();
Assert.IsInstanceOfType(typeof(ResidualStopCriterium), clone, "Wrong criterium type");
var clonedCriterium = clone as ResidualStopCriterium;
Assert.IsNotNull(clonedCriterium);
// ReSharper disable PossibleNullReferenceException
Assert.AreEqual(criterium.Maximum, clonedCriterium.Maximum, "Clone failed");
Assert.AreEqual(criterium.MinimumIterationsBelowMaximum, clonedCriterium.MinimumIterationsBelowMaximum, "Clone failed");
// ReSharper restore PossibleNullReferenceException
}
}
}

164
src/UnitTests/LinearAlgebraTests/Complex/SparseMatrixTests.cs

@ -0,0 +1,164 @@
// <copyright file="SparseMatrixTests.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>
namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex
{
using System.Collections.Generic;
using System.Numerics;
using LinearAlgebra.Generic;
using MbUnit.Framework;
using LinearAlgebra.Complex;
public class SparseMatrixTests : MatrixTests
{
protected override Matrix<Complex> CreateMatrix(int rows, int columns)
{
return new SparseMatrix(rows, columns);
}
protected override Matrix<Complex> CreateMatrix(Complex[,] data)
{
return new SparseMatrix(data);
}
protected override Vector<Complex> CreateVector(int size)
{
return new SparseVector(size);
}
protected override Vector<Complex> CreateVector(Complex[] data)
{
return new SparseVector(data);
}
[Test]
public void CanCreateMatrixFrom1DArray()
{
var testData = new Dictionary<string, Matrix<Complex>>
{
{ "Singular3x3", new SparseMatrix(3, 3, new[] { new Complex(1.0, 1), new Complex(1.0, 1), new Complex(1.0, 1), new Complex(1.0, 1), new Complex(1.0, 1), new Complex(1.0, 1), new Complex(2.0, 1), new Complex(2.0, 1), new Complex(2.0, 1) }) },
{ "Square3x3", new SparseMatrix(3, 3, new[] { new Complex(-1.1, 1), Complex.Zero, new Complex(-4.4, 1), new Complex(-2.2, 1), new Complex(1.1, 1), new Complex(5.5, 1), new Complex(-3.3, 1), new Complex(2.2, 1), new Complex(6.6, 1) }) },
{ "Square4x4", new SparseMatrix(4, 4, new[] { new Complex(-1.1, 1), Complex.Zero, new Complex(1.0, 1), new Complex(-4.4, 1), new Complex(-2.2, 1), new Complex(1.1, 1), new Complex(2.1, 1), new Complex(5.5, 1), new Complex(-3.3, 1), new Complex(2.2, 1), new Complex(6.2, 1), new Complex(6.6, 1), new Complex(-4.4, 1), new Complex(3.3, 1), new Complex(4.3, 1), new Complex(-7.7, 1) }) },
{ "Tall3x2", new SparseMatrix(3, 2, new[] { new Complex(-1.1, 1), Complex.Zero, new Complex(-4.4, 1), new Complex(-2.2, 1), new Complex(1.1, 1), new Complex(5.5, 1) }) },
{ "Wide2x3", new SparseMatrix(2, 3, new[] { new Complex(-1.1, 1), Complex.Zero, new Complex(-2.2, 1), new Complex(1.1, 1), new Complex(-3.3, 1), new Complex(2.2, 1) }) }
};
foreach (var name in testData.Keys)
{
Assert.AreEqual(TestMatrices[name], testData[name]);
}
}
[Test]
public void MatrixFrom2DArrayIsCopy()
{
var matrix = new SparseMatrix(TestData2D["Singular3x3"]);
matrix[0, 0] = 10.0;
AssertHelpers.AreEqual(new Complex(1.0, 1), TestData2D["Singular3x3"][0, 0]);
}
[Test]
[Row("Singular3x3")]
[Row("Singular3x3")]
[Row("Square3x3")]
[Row("Square4x4")]
[Row("Tall3x2")]
[Row("Wide2x3")]
public void CanCreateMatrixFrom2DArray(string name)
{
var matrix = new SparseMatrix(TestData2D[name]);
for (var i = 0; i < TestData2D[name].GetLength(0); i++)
{
for (var j = 0; j < TestData2D[name].GetLength(1); j++)
{
AssertHelpers.AreEqual(TestData2D[name][i, j], matrix[i, j]);
}
}
}
[Test]
public void CanCreateMatrixWithUniformValues()
{
var matrix = new SparseMatrix(10, 10, new Complex(10.0, 1));
for (var i = 0; i < matrix.RowCount; i++)
{
for (var j = 0; j < matrix.ColumnCount; j++)
{
AssertHelpers.AreEqual(matrix[i, j], new Complex(10.0, 1));
}
}
}
[Test]
public void CanCreateIdentity()
{
var matrix = SparseMatrix.Identity(5);
for (var i = 0; i < matrix.RowCount; i++)
{
for (var j = 0; j < matrix.ColumnCount; j++)
{
AssertHelpers.AreEqual(i == j ? Complex.One : Complex.Zero, matrix[i, j]);
}
}
}
[Test]
[Row(0)]
[Row(-1)]
[ExpectedArgumentException]
public void IdentityFailsWithZeroOrNegativeOrder(int order)
{
SparseMatrix.Identity(order);
}
[Test]
public void CanCreateHeavySparseMatrix()
{
var matrix = new SparseMatrix(500, 1000);
var nonzero = 0;
var rnd = new System.Random();
for (int i = 0; i < matrix.RowCount; i++)
{
for (int j = 0; j < matrix.ColumnCount; j++ )
{
var value = rnd.Next(10) * rnd.Next(10) * rnd.Next(10) * rnd.Next(10) * rnd.Next(10);
if (value != 0)
{
nonzero++;
}
matrix[i, j] = value;
}
}
Assert.AreEqual(matrix.NonZerosCount, nonzero);
}
}
}

99
src/UnitTests/LinearAlgebraTests/Complex/SparseVectorTest.TextHandling.cs

@ -0,0 +1,99 @@
// <copyright file="SparseVector.TextHandling.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>
namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex
{
using System;
using System.Globalization;
using LinearAlgebra.Complex;
using MbUnit.Framework;
public class SparseVectorTextHandlingTest
{
[Test]
[Row("2", "(2, 0)")]
[Row("(3)", "(3, 0)")]
[Row("[1,2,3]", "(1, 0),(2, 0),(3, 0)")]
[Row(" [ 1.1 , 2.1 , 3.1 ] ", "(1.1, 0),(2.1, 0),(3.1, 0)")]
[Row(" [ -1.1 , 2.1 , +3.1 ] ", "(-1.1, 0),(2.1, 0),(3.1, 0)")]
[Row(" [1.2,3.4 , 5.6] ", "(1.2, 0),(3.4, 0),(5.6, 0)")]
[Row("[1+1i,2+1i,3+1i]", "(1, 1),(2, 1),(3, 1)")]
[Row(" [ 1.1 + 1i , 2.1+1i , 3.1+1i ] ", "(1.1, 1),(2.1, 1),(3.1, 1)")]
[Row(" [ -1.1 + 1i , 2.1-1i , +3.1+1i ] ", "(-1.1, 1),(2.1, -1),(3.1, 1)")]
[Row(" [1.2+2.3i ,3.4+4.5i , 5.6+ 6.7i] ", "(1.2, 2.3),(3.4, 4.5),(5.6, 6.7)")]
public void CanParseComplexSparseVectorsWithInvariant(string stringToParse, string expectedToString)
{
var formatProvider = CultureInfo.InvariantCulture;
SparseVector vector = SparseVector.Parse(stringToParse, formatProvider);
Assert.AreEqual(expectedToString, vector.ToString(formatProvider));
}
[Test]
[Row(" 1.2 + 1i , 3.4 + 1i , 5.6 + 1i ", "(1.2, 1),(3.4, 1),(5.6, 1)", "en-US")]
[Row(" 1.2 + 1i ; 3.4 + 1i ; 5.6 + 1i ", "(1.2, 1);(3.4, 1);(5.6, 1)", "de-CH")]
[Row(" 1,2 + 1i ; 3,4 + 1i ; 5,6 + 1i ", "(1,2, 1);(3,4, 1);(5,6, 1)", "de-DE")]
public void CanParseComplexSparseVectorsWithCulture(string stringToParse, string expectedToString, string culture)
{
var formatProvider = CultureInfo.GetCultureInfo(culture);
SparseVector vector = SparseVector.Parse(stringToParse, formatProvider);
Assert.AreEqual(expectedToString, vector.ToString(formatProvider));
}
[Test]
[MultipleAsserts]
public void ParseThrowsFormatExceptionIfMissingClosingParen()
{
Assert.Throws<FormatException>(() => SparseVector.Parse("(1"));
Assert.Throws<FormatException>(() => SparseVector.Parse("[1"));
}
[Test]
[Row(null)]
[Row("")]
[Row(",")]
[Row("1,")]
[Row(",1")]
[Row(",1,2,")]
[Row("1,,2,,3")]
[Row("1e+")]
[Row("1e")]
[Row("()")]
[Row("[ ]")]
public void TryParseReturnsFalseWhenGivenBadValueWithInvariant(string str)
{
SparseVector vector;
var ret = SparseVector.TryParse(str, CultureInfo.InvariantCulture, out vector);
Assert.IsFalse(ret);
Assert.IsNull(vector);
}
}
}

415
src/UnitTests/LinearAlgebraTests/Complex/SparseVectorTest.cs

@ -0,0 +1,415 @@
// <copyright file="SparseVectorTest.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>
namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex
{
using System;
using System.Collections.Generic;
using System.Numerics;
using LinearAlgebra.Generic;
using MbUnit.Framework;
using LinearAlgebra.Complex;
public class SparseVectorTest : VectorTests
{
protected override Vector<Complex> CreateVector(int size)
{
return new SparseVector(size);
}
protected override Vector<Complex> CreateVector(IList<Complex> data)
{
var vector = new SparseVector(data.Count);
for (var index = 0; index < data.Count; index++)
{
vector[index] = data[index];
}
return vector;
}
#region Test similar to DenseVector
[Test]
[MultipleAsserts]
public void CanCreateSparseVectorFromArray()
{
var data = new Complex[Data.Length];
Array.Copy(Data, data, Data.Length);
var vector = new SparseVector(data);
for (var i = 0; i < data.Length; i++)
{
AssertHelpers.AreEqual(data[i], vector[i]);
}
}
[Test]
[MultipleAsserts]
public void CanCreateSparseVectorFromAnotherSparseVector()
{
var vector = new SparseVector(Data);
var other = new SparseVector(vector);
Assert.AreNotSame(vector, other);
for (var i = 0; i < Data.Length; i++)
{
AssertHelpers.AreEqual(vector[i], other[i]);
}
}
[Test]
[MultipleAsserts]
public void CanCreateSparseVectorFromAnotherVector()
{
var vector = (Vector<Complex>)new SparseVector(Data);
var other = new SparseVector(vector);
Assert.AreNotSame(vector, other);
for (var i = 0; i < Data.Length; i++)
{
AssertHelpers.AreEqual(vector[i], other[i]);
}
}
[Test]
[MultipleAsserts]
public void CanCreateSparseVectorFromUserDefinedVector()
{
var vector = new UserDefinedVector(Data);
var other = new SparseVector(vector);
for (var i = 0; i < Data.Length; i++)
{
AssertHelpers.AreEqual(vector[i], other[i]);
}
}
[Test]
public void CanCreateSparseVectorWithConstantValues()
{
var vector = new SparseVector(5, new Complex(5, 1));
Assert.ForAll(vector, value => value == new Complex(5,1));
}
[Test]
[MultipleAsserts]
public void CanCreateSparseMatrix()
{
var vector = new SparseVector(3);
var matrix = vector.CreateMatrix(2, 3);
Assert.AreEqual(2, matrix.RowCount);
Assert.AreEqual(3, matrix.ColumnCount);
}
[Test]
[MultipleAsserts]
public void CanConvertSparseVectorToArray()
{
var vector = new SparseVector(Data);
var array = vector.ToArray();
Assert.IsInstanceOfType(typeof(Complex[]), array);
Assert.AreElementsEqual(vector, array);
}
[Test]
[MultipleAsserts]
public void CanConvertArrayToSparseVector()
{
var array = new[] { Complex.Zero, new Complex(1.0, 1), new Complex(2.0, 1), new Complex(3.0, 1), new Complex(4.0, 1) };
var vector = new SparseVector(array);
Assert.IsInstanceOfType(typeof(SparseVector), vector);
Assert.AreElementsEqual(array, array);
}
[Test]
public void CanCallUnaryPlusOperatorOnSparseVector()
{
var vector = new SparseVector(Data);
var other = +vector;
for (var i = 0; i < Data.Length; i++)
{
AssertHelpers.AreEqual(vector[i], other[i]);
}
}
[Test]
[MultipleAsserts]
public void CanAddTwoSparseVectorsUsingOperator()
{
var vector = new SparseVector(Data);
var other = new SparseVector(Data);
var result = vector + other;
for (var i = 0; i < Data.Length; i++)
{
AssertHelpers.AreEqual(Data[i], vector[i]);
AssertHelpers.AreEqual(Data[i], other[i]);
AssertHelpers.AreEqual(Data[i] * 2.0, result[i]);
}
}
[Test]
public void CanCallUnaryNegationOperatorOnSparseVector()
{
var vector = new SparseVector(Data);
var other = -vector;
for (var i = 0; i < Data.Length; i++)
{
AssertHelpers.AreEqual(-Data[i], other[i]);
}
}
[Test]
[MultipleAsserts]
public void CanSubtractTwoSparseVectorsUsingOperator()
{
var vector = new SparseVector(Data);
var other = new SparseVector(Data);
var result = vector - other;
for (var i = 0; i < Data.Length; i++)
{
AssertHelpers.AreEqual(Data[i], vector[i]);
AssertHelpers.AreEqual(Data[i], other[i]);
AssertHelpers.AreEqual(Complex.Zero, result[i]);
}
}
[Test]
[MultipleAsserts]
public void CanMultiplySparseVectorByComplexUsingOperators()
{
var vector = new SparseVector(Data);
vector = vector * new Complex(2.0, 1);
for (var i = 0; i < Data.Length; i++)
{
AssertHelpers.AreEqual(Data[i] * new Complex(2.0, 1), vector[i]);
}
vector = vector * 1.0;
for (var i = 0; i < Data.Length; i++)
{
AssertHelpers.AreEqual(Data[i] * new Complex(2.0, 1), vector[i]);
}
vector = new SparseVector(Data);
vector = new Complex(2.0, 1) * vector;
for (var i = 0; i < Data.Length; i++)
{
AssertHelpers.AreEqual(Data[i] * new Complex(2.0, 1), vector[i]);
}
vector = 1.0 * vector;
for (var i = 0; i < Data.Length; i++)
{
AssertHelpers.AreEqual(Data[i] * new Complex(2.0, 1), vector[i]);
}
}
[Test]
[MultipleAsserts]
public void CanDivideSparseVectorByComplexUsingOperators()
{
var vector = new SparseVector(Data);
vector = vector / new Complex(2.0, 1);
for (var i = 0; i < Data.Length; i++)
{
AssertHelpers.AreEqual(Data[i] / new Complex(2.0, 1), vector[i]);
}
vector = vector / 1.0;
for (var i = 0; i < Data.Length; i++)
{
AssertHelpers.AreEqual(Data[i] / new Complex(2.0, 1), vector[i]);
}
}
[Test]
public void CanCalculateOuterProductForSparseVector()
{
var vector1 = CreateVector(Data);
var vector2 = CreateVector(Data);
Matrix<Complex> m = Vector<Complex>.OuterProduct(vector1, vector2);
for (var i = 0; i < vector1.Count; i++)
{
for (var j = 0; j < vector2.Count; j++)
{
AssertHelpers.AreEqual(m[i, j], vector1[i] * vector2[j]);
}
}
}
[Test]
[ExpectedArgumentNullException]
public void OuterProducForSparseVectortWithFirstParameterNullShouldThrowException()
{
SparseVector vector1 = null;
var vector2 = CreateVector(Data);
Vector<Complex>.OuterProduct(vector1, vector2);
}
[Test]
[ExpectedArgumentNullException]
public void OuterProductForSparseVectorWithSecondParameterNullShouldThrowException()
{
var vector1 = CreateVector(Data);
SparseVector vector2 = null;
Vector<Complex>.OuterProduct(vector1, vector2);
}
#endregion
[Test]
[MultipleAsserts]
public void CanCreateSparseVectorFromDenseVector()
{
var vector = (Vector<Complex>)new DenseVector(Data);
var other = new SparseVector(vector);
Assert.AreNotSame(vector, other);
for (var i = 0; i < Data.Length; i++)
{
AssertHelpers.AreEqual(vector[i], other[i]);
}
}
[Test]
[MultipleAsserts]
public void CheckSparseMechanismBySettingValues()
{
var vector = new SparseVector(10000);
//Add non-zero elements
vector[200] = 1.5;
AssertHelpers.AreEqual(1.5, vector[200]);
Assert.AreEqual(1, vector.NonZerosCount);
vector[500] = 3.5;
AssertHelpers.AreEqual(3.5, vector[500]);
Assert.AreEqual(2, vector.NonZerosCount);
vector[800] = 5.5;
AssertHelpers.AreEqual(5.5, vector[800]);
Assert.AreEqual(3, vector.NonZerosCount);
vector[0] = 7.5;
AssertHelpers.AreEqual(7.5, vector[0]);
Assert.AreEqual(4, vector.NonZerosCount);
//Remove non-zero elements
vector[200] = 0;
AssertHelpers.AreEqual(0, vector[200]);
Assert.AreEqual(3, vector.NonZerosCount);
vector[500] = 0;
AssertHelpers.AreEqual(0, vector[500]);
Assert.AreEqual(2, vector.NonZerosCount);
vector[800] = 0;
AssertHelpers.AreEqual(0, vector[800]);
Assert.AreEqual(1, vector.NonZerosCount);
vector[0] = 0;
AssertHelpers.AreEqual(0, vector[0]);
Assert.AreEqual(0, vector.NonZerosCount);
}
[Test]
[MultipleAsserts]
public void CheckSparseMechanismByZeroMultiply()
{
var vector = new SparseVector(10000);
//Add non-zero elements
vector[200] = 1.5;
vector[500] = 3.5;
vector[800] = 5.5;
vector[0] = 7.5;
//Multiply by 0
vector *= 0;
AssertHelpers.AreEqual(0, vector[200]);
AssertHelpers.AreEqual(0, vector[500]);
AssertHelpers.AreEqual(0, vector[800]);
AssertHelpers.AreEqual(0, vector[0]);
Assert.AreEqual(0, vector.NonZerosCount);
}
[Test]
public void CanDotProductOfTwoSparseVectors()
{
var vectorA = new SparseVector(10000);
vectorA[200] = 1;
vectorA[500] = 3;
vectorA[800] = 5;
vectorA[100] = 7;
vectorA[900] = 9;
var vectorB = new SparseVector(10000);
vectorB[300] = 3;
vectorB[500] = 5;
vectorB[800] = 7;
AssertHelpers.AreEqual(50.0, vectorA.DotProduct(vectorB));
}
[Test]
public void CreateHugeSparseVector()
{
var data = new Complex[1000000];
var rnd = new Random();
for (var i = 0; i < 1000000; i++)
data[i] = new Complex(rnd.Next(), rnd.Next());
var vector = new SparseVector(data);
}
[Test]
public void PointwiseMultiplySparseVector()
{
var zeroArray = new[] { Complex.Zero, new Complex(1.0, 1), Complex.Zero, new Complex(1.0, 1), Complex.Zero };
var vector1 = new SparseVector(Data);
var vector2 = new SparseVector(zeroArray);
var result = new SparseVector(vector1.Count);
vector1.PointwiseMultiply(vector2, result);
for (var i = 0; i < vector1.Count; i++)
{
AssertHelpers.AreEqual(Data[i] * zeroArray[i], result[i]);
}
Assert.AreEqual(2, result.NonZerosCount);
}
}
}

215
src/UnitTests/LinearAlgebraTests/Complex/UserDefinedMatrixTests.cs

@ -0,0 +1,215 @@
// <copyright file="UserDefinedMatrixTests.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>
namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex
{
using System;
using System.Numerics;
using Distributions;
using LinearAlgebra.Generic;
using Threading;
internal class UserDefinedMatrix : Matrix<Complex>
{
private readonly Complex[,] _data;
public UserDefinedMatrix(int order): base(order, order)
{
_data = new Complex[order, order];
}
public UserDefinedMatrix(int rows, int columns) : base(rows, columns)
{
_data = new Complex[rows, columns];
}
public UserDefinedMatrix(Complex[,] data) : base(data.GetLength(0), data.GetLength(1))
{
_data = (Complex[,])data.Clone();
}
public override Complex At(int row, int column)
{
return _data[row, column];
}
public override void At(int row, int column, Complex value)
{
_data[row, column] = value;
}
public override Matrix<Complex> CreateMatrix(int numberOfRows, int numberOfColumns)
{
return new UserDefinedMatrix(numberOfRows, numberOfColumns);
}
public override Vector<Complex> CreateVector(int size)
{
return new UserDefinedVector(size);
}
public static UserDefinedMatrix Identity(int order)
{
var m = new UserDefinedMatrix(order, order);
for (var i = 0; i < order; i++)
{
m[i, i] = 1.0;
}
return m;
}
public override void Negate()
{
Multiply(-Complex.One);
}
public override Matrix<Complex> Random(int numberOfRows, int numberOfColumns, IContinuousDistribution distribution)
{
if (numberOfRows < 1)
{
throw new ArgumentException("numberOfRows");
}
if (numberOfColumns < 1)
{
throw new ArgumentException("numberOfColumns");
}
var matrix = CreateMatrix(numberOfRows, numberOfColumns);
CommonParallel.For(
0,
ColumnCount,
j =>
{
for (var i = 0; i < matrix.RowCount; i++)
{
matrix[i, j] = new Complex(distribution.Sample(), distribution.Sample());
}
});
return matrix;
}
public override Matrix<Complex> Random(int numberOfRows, int numberOfColumns, IDiscreteDistribution distribution)
{
if (numberOfRows < 1)
{
throw new ArgumentException("numberOfRows");
}
if (numberOfColumns < 1)
{
throw new ArgumentException("numberOfColumns");
}
var matrix = CreateMatrix(numberOfRows, numberOfColumns);
CommonParallel.For(
0,
ColumnCount,
j =>
{
for (var i = 0; i < matrix.RowCount; i++)
{
matrix[i, j] = new Complex(distribution.Sample(), distribution.Sample());
}
});
return matrix;
}
protected sealed override Complex AddT(Complex val1, Complex val2)
{
return val1 + val2;
}
protected sealed override Complex SubtractT(Complex val1, Complex val2)
{
return val1 - val2;
}
protected sealed override Complex MultiplyT(Complex val1, Complex val2)
{
return val1 * val2;
}
protected sealed override Complex DivideT(Complex val1, Complex val2)
{
return val1 / val2;
}
protected sealed override bool IsOneT(Complex val1)
{
return Complex.One.AlmostEqual(val1);
}
protected sealed override double AbsoluteT(Complex val1)
{
return val1.Magnitude;
}
public override Matrix<Complex> ConjugateTranspose()
{
var ret = CreateMatrix(ColumnCount, RowCount);
for (var j = 0; j < ColumnCount; j++)
{
for (var i = 0; i < RowCount; i++)
{
ret.At(j, i, At(i, j).Conjugate());
}
}
return ret;
}
}
public class UserDefinedMatrixTests : MatrixTests
{
protected override Matrix<Complex> CreateMatrix(int rows, int columns)
{
return new UserDefinedMatrix(rows, columns);
}
protected override Matrix<Complex> CreateMatrix(Complex[,] data)
{
return new UserDefinedMatrix(data);
}
protected override Vector<Complex> CreateVector(int size)
{
return new UserDefinedVector(size);
}
protected override Vector<Complex> CreateVector(Complex[] data)
{
return new UserDefinedVector(data);
}
}
}

225
src/UnitTests/LinearAlgebraTests/Complex/UserDefinedVectorTests.cs

@ -0,0 +1,225 @@
// <copyright file="UserDefinedVectorTests.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>
namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex
{
using System;
using System.Numerics;
using System.Collections.Generic;
using Distributions;
using LinearAlgebra.Generic;
using Threading;
internal class UserDefinedVector : Vector<Complex>
{
private readonly Complex[] _data;
public UserDefinedVector(int size)
: base(size)
{
_data = new Complex[size];
}
public UserDefinedVector(Complex[] data)
: base(data.Length)
{
_data = (Complex[])data.Clone();
}
public override Complex this[int index]
{
get
{
return _data[index];
}
set
{
_data[index] = value;
}
}
public override Matrix<Complex> CreateMatrix(int rows, int columns)
{
return new UserDefinedMatrix(rows, columns);
}
public override Vector<Complex> CreateVector(int size)
{
return new UserDefinedVector(size);
}
public override Vector<Complex> Negate()
{
var result = new UserDefinedVector(Count);
CommonParallel.For(
0,
_data.Length,
index => result[index] = -_data[index]);
return result;
}
public override Vector<Complex> Random(int length, IContinuousDistribution randomDistribution)
{
if (length < 1)
{
throw new ArgumentException("length");
}
var v = (UserDefinedVector)CreateVector(length);
for (var index = 0; index < v._data.Length; index++)
{
v._data[index] = new Complex(randomDistribution.Sample(), randomDistribution.Sample());
}
return v;
}
public override Vector<Complex> Random(int length, IDiscreteDistribution randomDistribution)
{
if (length < 1)
{
throw new ArgumentException("length");
}
var v = (UserDefinedVector)CreateVector(length);
for (var index = 0; index < v._data.Length; index++)
{
v._data[index] = new Complex(randomDistribution.Sample(), randomDistribution.Sample());
}
return v;
}
public override int MinimumIndex()
{
throw new NotSupportedException();
}
public override int MaximumIndex()
{
throw new NotSupportedException();
}
public override Vector<Complex> Normalize(double p)
{
if (p < 0.0)
{
throw new ArgumentOutOfRangeException("p");
}
var norm = Norm(p);
var clone = Clone();
if (norm == 0.0)
{
return clone;
}
clone.Multiply(1.0 / norm, clone);
return clone;
}
protected sealed override Complex AddT(Complex val1, Complex val2)
{
return val1 + val2;
}
protected sealed override Complex SubtractT(Complex val1, Complex val2)
{
return val1 - val2;
}
protected sealed override Complex MultiplyT(Complex val1, Complex val2)
{
return val1 * val2;
}
protected sealed override Complex DivideT(Complex val1, Complex val2)
{
return val1 / val2;
}
protected sealed override bool IsOneT(Complex val1)
{
return Complex.One.AlmostEqual(val1);
}
protected sealed override double AbsoluteT(Complex val1)
{
return val1.Magnitude;
}
public override void Conjugate(Vector<Complex> target)
{
if (target == null)
{
throw new ArgumentNullException("target");
}
if (Count != target.Count)
{
throw new ArgumentException("target");
}
if (ReferenceEquals(this, target))
{
var tmp = CreateVector(Count);
Conjugate(tmp);
tmp.CopyTo(target);
}
CommonParallel.For(
0,
Count,
index => target[index] = this[index].Conjugate());
}
}
public class UserDefinedVectorTests : VectorTests
{
protected override Vector<Complex> CreateVector(int size)
{
return new UserDefinedVector(size);
}
protected override Vector<Complex> CreateVector(IList<Complex> data)
{
var vector = new UserDefinedVector(data.Count);
for (var index = 0; index < data.Count; index++)
{
vector[index] = data[index];
}
return vector;
}
}
}

902
src/UnitTests/LinearAlgebraTests/Complex/VectorTests.Arithmetic.cs

@ -0,0 +1,902 @@
// <copyright file="VectorTests.Arithmetic.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>
namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex
{
using System;
using System.Numerics;
using LinearAlgebra.Generic;
using MbUnit.Framework;
public abstract partial class VectorTests
{
[Test]
public void CanCallPlus()
{
var vector = CreateVector(Data);
var other = vector.Plus();
for (var i = 0; i < Data.Length; i++)
{
AssertHelpers.AreEqual(vector[i], other[i]);
}
}
[Test]
public void OperatorPlusThrowsArgumentNullExceptionWhenCallOnNullVector()
{
Vector<Complex> vector = null;
Vector<Complex> other = null;
Assert.Throws<ArgumentNullException>(() => other = +vector);
}
[Test]
public void CanCallUnaryPlusOperator()
{
var vector = CreateVector(Data);
var other = +vector;
for (var i = 0; i < Data.Length; i++)
{
AssertHelpers.AreEqual(vector[i], other[i]);
}
}
[Test]
[MultipleAsserts]
public void CanAddScalarToVector()
{
var copy = CreateVector(Data);
var vector = copy.Add(2.0);
for (var i = 0; i < Data.Length; i++)
{
AssertHelpers.AreEqual(Data[i] + 2.0, vector[i]);
}
vector.Add(0.0);
for (var i = 0; i < Data.Length; i++)
{
AssertHelpers.AreEqual(Data[i] + 2.0, vector[i]);
}
}
[Test]
[MultipleAsserts]
public void CanAddScalarToVectorUsingResultVector()
{
var vector = CreateVector(Data);
var result = CreateVector(Data.Length);
vector.Add(2.0, result);
for (var i = 0; i < Data.Length; i++)
{
AssertHelpers.AreEqual(Data[i], vector[i]);
AssertHelpers.AreEqual(Data[i] + 2.0, result[i]);
}
vector.Add(0.0, result);
for (var i = 0; i < Data.Length; i++)
{
AssertHelpers.AreEqual(Data[i], result[i]);
}
}
[Test]
public void ThrowsArgumentNullExceptionWhenAddingScalarWithNullResultVector()
{
var vector = CreateVector(Data.Length);
Assert.Throws<ArgumentNullException>(() => vector.Add(0.0, null));
}
[Test]
public void ThrowsArgumentExceptionWhenAddingScalarWithWrongSizeResultVector()
{
var vector = CreateVector(Data.Length);
var result = CreateVector(Data.Length + 1);
Assert.Throws<ArgumentException>(() => vector.Add(0.0, result));
}
[Test]
public void ThrowsArgumentNullExceptionWhenAddingTwoVectorsAndOneIsNull()
{
var vector = CreateVector(Data);
Assert.Throws<ArgumentNullException>(() => vector.Add(null));
}
[Test]
public void ThrowsArgumentExceptionWhenAddingTwoVectorsOfDifferingSize()
{
var vector = CreateVector(Data.Length);
var other = CreateVector(Data.Length + 1);
Assert.Throws<ArgumentException>(() => vector.Add(other));
}
[Test]
public void ThrowsArgumentNullExceptionWhenAddingTwoVectorsAndResultIsNull()
{
var vector = CreateVector(Data.Length);
var other = CreateVector(Data.Length + 1);
Assert.Throws<ArgumentNullException>(() => vector.Add(other, null));
}
[Test]
public void ThrowsArgumentExceptionWhenAddingTwoVectorsAndResultIsDifferentSize()
{
var vector = CreateVector(Data.Length);
var other = CreateVector(Data.Length);
var result = CreateVector(Data.Length + 1);
Assert.Throws<ArgumentException>(() => vector.Add(other, result));
}
[Test]
public void AdditionOperatorThrowsArgumentNullExpectionIfAVectorIsNull()
{
Vector<Complex> a = null;
var b = CreateVector(Data.Length);
Assert.Throws<ArgumentNullException>(() => a += b);
a = b;
b = null;
Assert.Throws<ArgumentNullException>(() => a += b);
}
[Test]
public void AdditionOperatorThrowsArgumentExpectionIfVectorsAreDifferentSize()
{
var a = CreateVector(Data.Length);
var b = CreateVector(Data.Length + 1);
Assert.Throws<ArgumentException>(() => a += b);
}
[Test]
public void CanAddTwoVectors()
{
var copy = CreateVector(Data);
var other = CreateVector(Data);
var vector = copy.Add(other);
for (var i = 0; i < Data.Length; i++)
{
AssertHelpers.AreEqual(Data[i] * 2.0, vector[i]);
}
}
[Test]
[MultipleAsserts]
public void CanAddTwoVectorsUsingResultVector()
{
var vector = CreateVector(Data);
var other = CreateVector(Data);
var result = CreateVector(Data.Length);
vector.Add(other, result);
for (var i = 0; i < Data.Length; i++)
{
AssertHelpers.AreEqual(Data[i], vector[i]);
AssertHelpers.AreEqual(Data[i], other[i]);
AssertHelpers.AreEqual(Data[i] * 2.0, result[i]);
}
}
[Test]
[MultipleAsserts]
public void CanAddTwoVectorsUsingOperator()
{
var vector = CreateVector(Data);
var other = CreateVector(Data);
var result = vector + other;
for (var i = 0; i < Data.Length; i++)
{
AssertHelpers.AreEqual(Data[i], vector[i]);
AssertHelpers.AreEqual(Data[i], other[i]);
AssertHelpers.AreEqual(Data[i] * 2.0, result[i]);
}
}
[Test]
public void CanAddVectorToItself()
{
var copy = CreateVector(Data);
var vector = copy.Add(copy);
for (var i = 0; i < Data.Length; i++)
{
AssertHelpers.AreEqual(Data[i] * 2.0, vector[i]);
}
}
[Test]
[MultipleAsserts]
public void CanAddVectorToItselfUsingResultVector()
{
var vector = CreateVector(Data);
var result = CreateVector(Data.Length);
vector.Add(vector, result);
for (var i = 0; i < Data.Length; i++)
{
AssertHelpers.AreEqual(Data[i], vector[i]);
AssertHelpers.AreEqual(Data[i] * 2.0, result[i]);
}
}
[Test]
[MultipleAsserts]
public void CanAddTwoVectorsUsingItselfAsResultVector()
{
var vector = CreateVector(Data);
var other = CreateVector(Data);
vector.Add(other, vector);
for (var i = 0; i < Data.Length; i++)
{
AssertHelpers.AreEqual(Data[i], other[i]);
AssertHelpers.AreEqual(Data[i] * 2.0, vector[i]);
}
}
[Test]
public void CanCallNegate()
{
var vector = CreateVector(Data);
var other = vector.Negate();
for (var i = 0; i < Data.Length; i++)
{
AssertHelpers.AreEqual(-Data[i], other[i]);
}
}
[Test]
public void OperatorNegateThrowsArgumentNullExceptionWhenCallOnNullVector()
{
Vector<Complex> vector = null;
Vector<Complex> other = null;
Assert.Throws<ArgumentNullException>(() => other = -vector);
}
[Test]
public void CanCallUnaryNegationOperator()
{
var vector = CreateVector(Data);
var other = -vector;
for (var i = 0; i < Data.Length; i++)
{
AssertHelpers.AreEqual(-Data[i], other[i]);
}
}
[Test]
[MultipleAsserts]
public void CanSubtractScalarFromVector()
{
var copy = CreateVector(Data);
var vector = copy.Subtract(2.0);
for (var i = 0; i < Data.Length; i++)
{
AssertHelpers.AreEqual(Data[i] - 2.0, vector[i]);
}
vector.Subtract(0.0);
for (var i = 0; i < Data.Length; i++)
{
AssertHelpers.AreEqual(Data[i] - 2.0, vector[i]);
}
}
[Test]
[MultipleAsserts]
public void CanSubtractScalarFromVectorUsingResultVector()
{
var vector = CreateVector(Data);
var result = CreateVector(Data.Length);
vector.Subtract(2.0, result);
for (var i = 0; i < Data.Length; i++)
{
AssertHelpers.AreEqual(Data[i], vector[i]);
AssertHelpers.AreEqual(Data[i] - 2.0, result[i]);
}
vector.Subtract(0.0, result);
for (var i = 0; i < Data.Length; i++)
{
AssertHelpers.AreEqual(Data[i], result[i]);
}
}
[Test]
public void ThrowsArgumentNullExceptionWhenSubtractingScalarWithNullResultVector()
{
var vector = CreateVector(Data.Length);
Assert.Throws<ArgumentNullException>(() => vector.Subtract(0.0, null));
}
[Test]
public void ThrowsArgumentExceptionWhenSubtractingScalarWithWrongSizeResultVector()
{
var vector = CreateVector(Data.Length);
var result = CreateVector(Data.Length + 1);
Assert.Throws<ArgumentException>(() => vector.Subtract(0.0, result));
}
[Test]
public void ThrowsArgumentNullExceptionWhenSubtractingTwoVectorsAndOneIsNull()
{
var vector = CreateVector(Data);
Assert.Throws<ArgumentNullException>(() => vector.Subtract(null));
}
[Test]
public void ThrowsArgumentExceptionWhenSubtractingTwoVectorsOfDifferingSize()
{
var vector = CreateVector(Data.Length);
var other = CreateVector(Data.Length + 1);
Assert.Throws<ArgumentException>(() => vector.Subtract(other));
}
[Test]
public void ThrowsArgumentNullExceptionWhenSubtractingTwoVectorsAndResultIsNull()
{
var vector = CreateVector(Data.Length);
var other = CreateVector(Data.Length + 1);
Assert.Throws<ArgumentNullException>(() => vector.Subtract(other, null));
}
[Test]
public void ThrowsArgumentExceptionWhenSubtractingTwoVectorsAndResultIsDifferentSize()
{
var vector = CreateVector(Data.Length);
var other = CreateVector(Data.Length);
var result = CreateVector(Data.Length + 1);
Assert.Throws<ArgumentException>(() => vector.Subtract(other, result));
}
[Test]
public void SubtractionOperatorThrowsArgumentNullExpectionIfAVectorIsNull()
{
Vector<Complex> a = null;
var b = CreateVector(Data.Length);
Assert.Throws<ArgumentNullException>(() => a -= b);
a = b;
b = null;
Assert.Throws<ArgumentNullException>(() => a -= b);
}
[Test]
public void SubtractionOperatorThrowsArgumentExpectionIfVectorsAreDifferentSize()
{
var a = CreateVector(Data.Length);
var b = CreateVector(Data.Length + 1);
Assert.Throws<ArgumentException>(() => a -= b);
}
[Test]
public void CanSubtractTwoVectors()
{
var copy = CreateVector(Data);
var other = CreateVector(Data);
var vector = copy.Subtract(other);
for (var i = 0; i < Data.Length; i++)
{
AssertHelpers.AreEqual(0.0, vector[i]);
}
}
[Test]
[MultipleAsserts]
public void CanSubtractTwoVectorsUsingResultVector()
{
var vector = CreateVector(Data);
var other = CreateVector(Data);
var result = CreateVector(Data.Length);
vector.Subtract(other, result);
for (var i = 0; i < Data.Length; i++)
{
AssertHelpers.AreEqual(Data[i], vector[i]);
AssertHelpers.AreEqual(Data[i], other[i]);
AssertHelpers.AreEqual(0.0, result[i]);
}
}
[Test]
[MultipleAsserts]
public void CanSubtractTwoVectorsUsingOperator()
{
var vector = CreateVector(Data);
var other = CreateVector(Data);
var result = vector - other;
for (var i = 0; i < Data.Length; i++)
{
AssertHelpers.AreEqual(Data[i], vector[i]);
AssertHelpers.AreEqual(Data[i], other[i]);
AssertHelpers.AreEqual(0.0, result[i]);
}
}
[Test]
public void CanSubtractVectorFromItself()
{
var copy = CreateVector(Data);
var vector = copy.Subtract(copy);
for (var i = 0; i < Data.Length; i++)
{
AssertHelpers.AreEqual(0.0, vector[i]);
}
}
[Test]
[MultipleAsserts]
public void CanSubtractVectorFromItselfUsingResultVector()
{
var vector = CreateVector(Data);
var result = CreateVector(Data.Length);
vector.Subtract(vector, result);
for (var i = 0; i < Data.Length; i++)
{
AssertHelpers.AreEqual(Data[i], vector[i]);
AssertHelpers.AreEqual(0.0, result[i]);
}
}
[Test]
[MultipleAsserts]
public void CanSubtractTwoVectorsUsingItselfAsResultVector()
{
var vector = CreateVector(Data);
var other = CreateVector(Data);
vector.Subtract(other, vector);
for (var i = 0; i < Data.Length; i++)
{
AssertHelpers.AreEqual(Data[i], other[i]);
AssertHelpers.AreEqual(0.0, vector[i]);
}
}
[Test]
[MultipleAsserts]
public void CanDivideVectorByScalar()
{
var copy = CreateVector(Data);
var vector = copy.Divide(2.0);
for (var i = 0; i < Data.Length; i++)
{
AssertHelpers.AreEqual(Data[i] / 2.0, vector[i]);
}
vector.Divide(1.0);
for (var i = 0; i < Data.Length; i++)
{
AssertHelpers.AreEqual(Data[i] / 2.0, vector[i]);
}
}
[Test]
[MultipleAsserts]
public void CanDivideVectorByScalarUsingResultVector()
{
var vector = CreateVector(Data);
var result = CreateVector(Data.Length);
vector.Divide(2.0, result);
for (var i = 0; i < Data.Length; i++)
{
AssertHelpers.AreEqual(Data[i], vector[i]);
AssertHelpers.AreEqual(Data[i] / 2.0, result[i]);
}
vector.Divide(1.0, result);
for (var i = 0; i < Data.Length; i++)
{
AssertHelpers.AreEqual(Data[i], result[i]);
}
}
[Test]
[MultipleAsserts]
public void CanMultiplyVectorByScalar()
{
var copy = CreateVector(Data);
var vector = copy.Multiply(2.0);
for (var i = 0; i < Data.Length; i++)
{
AssertHelpers.AreEqual(Data[i] * 2.0, vector[i]);
}
vector.Multiply(1.0);
for (var i = 0; i < Data.Length; i++)
{
AssertHelpers.AreEqual(Data[i] * 2.0, vector[i]);
}
}
[Test]
[MultipleAsserts]
public void CanMultiplyVectorByScalarUsingResultVector()
{
var vector = CreateVector(Data);
var result = CreateVector(Data.Length);
vector.Multiply(2.0, result);
for (var i = 0; i < Data.Length; i++)
{
AssertHelpers.AreEqual(Data[i], vector[i]);
AssertHelpers.AreEqual(Data[i] * 2.0, result[i]);
}
vector.Multiply(1.0, result);
for (var i = 0; i < Data.Length; i++)
{
AssertHelpers.AreEqual(Data[i], result[i]);
}
}
[Test]
public void ThrowsArgumentNullExceptionWhenMultiplyingScalarWithNullResultVector()
{
var vector = CreateVector(Data.Length);
Assert.Throws<ArgumentNullException>(() => vector.Multiply(1.0, null));
}
[Test]
public void ThrowsArgumentNullExceptionWhenDividingScalarWithNullResultVector()
{
var vector = CreateVector(Data.Length);
Assert.Throws<ArgumentNullException>(() => vector.Divide(1.0, null));
}
[Test]
public void ThrowsArgumentExceptionWhenMultiplyingScalarWithWrongSizeResultVector()
{
var vector = CreateVector(Data.Length);
var result = CreateVector(Data.Length + 1);
Assert.Throws<ArgumentException>(() => vector.Multiply(0.0, result));
}
[Test]
public void ThrowsArgumentExceptionWhenDividingScalarWithWrongSizeResultVector()
{
var vector = CreateVector(Data.Length);
var result = CreateVector(Data.Length + 1);
Assert.Throws<ArgumentException>(() => vector.Divide(0.0, result));
}
[Test]
[MultipleAsserts]
public void CanMultiplyVectorByScalarUsingOperators()
{
var vector = CreateVector(Data);
vector = vector * 2.0;
for (var i = 0; i < Data.Length; i++)
{
AssertHelpers.AreEqual(Data[i] * 2.0, vector[i]);
}
vector = vector * 1.0;
for (var i = 0; i < Data.Length; i++)
{
AssertHelpers.AreEqual(Data[i] * 2.0, vector[i]);
}
vector = CreateVector(Data);
vector = 2.0 * vector;
for (var i = 0; i < Data.Length; i++)
{
AssertHelpers.AreEqual(Data[i] * 2.0, vector[i]);
}
vector = 1.0 * vector;
for (var i = 0; i < Data.Length; i++)
{
AssertHelpers.AreEqual(Data[i] * 2.0, vector[i]);
}
}
[Test]
[MultipleAsserts]
public void CanDivideVectorByScalarUsingOperators()
{
var vector = CreateVector(Data);
vector = vector / 2.0;
for (var i = 0; i < Data.Length; i++)
{
AssertHelpers.AreEqual(Data[i] / 2.0, vector[i]);
}
vector = vector / 1.0;
for (var i = 0; i < Data.Length; i++)
{
AssertHelpers.AreEqual(Data[i] / 2.0, vector[i]);
}
}
[Test]
[MultipleAsserts]
public void OperatorMultiplyThrowsArgumentNullExceptionWhenVectorIsNull()
{
Vector<Complex> vector = null;
Vector<Complex> result = null;
Assert.Throws<ArgumentNullException>(() => result = vector * 2.0);
Assert.Throws<ArgumentNullException>(() => result = 2.0 * vector);
}
[Test]
public void OperatorDivideThrowsArgumentNullExceptionWhenVectorIsNull()
{
Vector<Complex> vector = null;
Assert.Throws<ArgumentNullException>(() => vector = vector / 2.0);
}
[Test]
public void CanDotProduct()
{
var dataA = CreateVector(Data);
var dataB = CreateVector(Data);
AssertHelpers.AreEqual(new Complex(50, 30), dataA.DotProduct(dataB));
}
[Test]
[ExpectedArgumentNullException]
public void DotProductThrowsExceptionWhenArgumentIsNull()
{
var dataA = CreateVector(Data);
Vector<Complex> dataB = null;
dataA.DotProduct(dataB);
}
[Test]
[ExpectedArgumentException]
public void DotProductThrowsExceptionWhenArgumentHasDifferentSize()
{
var dataA = CreateVector(Data);
var dataB = CreateVector(new[] { new Complex(1, 1), new Complex(2, 1), new Complex(3, 1), new Complex(4, 1), new Complex(5, 1), new Complex(6, 1) });
dataA.DotProduct(dataB);
}
[Test]
public void CanDotProductUsingOperator()
{
var dataA = CreateVector(Data);
var dataB = CreateVector(Data);
AssertHelpers.AreEqual(new Complex(50, 30), dataA * dataB);
}
[Test]
[ExpectedArgumentNullException]
public void OperatorDotProductThrowsExceptionWhenLeftArgumentIsNull()
{
var dataA = CreateVector(Data);
Vector<Complex> dataB = null;
var d = dataA * dataB;
}
[Test]
[ExpectedArgumentNullException]
public void OperatorDotProductThrowsExceptionWhenRightArgumentIsNull()
{
Vector<Complex> dataA = null;
var dataB = CreateVector(Data);
var d = dataA * dataB;
}
[Test]
[ExpectedArgumentException]
public void OperatorDotProductThrowsExceptionWhenArgumentHasDifferentSize()
{
var dataA = CreateVector(Data);
var dataB = CreateVector(new[] { new Complex(1, 1), new Complex(2, 1), new Complex(3, 1), new Complex(4, 1), new Complex(5, 1), new Complex(6, 1) });
var d = dataA * dataB;
}
[Test]
public void PointwiseMultiply()
{
var vector1 = CreateVector(Data);
var vector2 = vector1.Clone();
var result = vector1.PointwiseMultiply(vector2);
for (var i = 0; i < vector1.Count; i++)
{
AssertHelpers.AreEqual(Data[i] * Data[i], result[i]);
}
}
[Test]
[ExpectedArgumentNullException]
public void PointwiseMultiplyWithOtherNullShouldThrowException()
{
var vector1 = CreateVector(Data);
Vector<Complex> vector2 = null;
var result = CreateVector(vector1.Count);
vector1.PointwiseMultiply(vector2, result);
}
[Test]
[ExpectedArgumentNullException]
public void PointwiseMultiplyWithResultNullShouldThrowException()
{
var vector1 = CreateVector(Data);
var vector2 = vector1.Clone();
Vector<Complex> result = null;
vector1.PointwiseMultiply(vector2, result);
}
[Test]
[ExpectedArgumentException]
public void PointwiseMultiplyWithInvalidResultLengthShouldThrowException()
{
var vector1 = CreateVector(Data);
var vector2 = vector1.Clone();
var result = CreateVector(vector1.Count + 1);
vector1.PointwiseMultiply(vector2, result);
}
[Test]
public void PointwiseMultiplyUsingResultVector()
{
var vector1 = CreateVector(Data);
var vector2 = vector1.Clone();
var result = CreateVector(vector1.Count);
vector1.PointwiseMultiply(vector2, result);
for (var i = 0; i < vector1.Count; i++)
{
Assert.AreEqual(Data[i] * Data[i], result[i]);
}
}
[Test]
public void PointWiseDivide()
{
var vector1 = CreateVector(Data);
var vector2 = vector1.Clone();
var result = vector1.PointwiseDivide(vector2);
for (var i = 0; i < vector1.Count; i++)
{
AssertHelpers.AreEqual(Data[i] / Data[i], result[i]);
}
}
[Test]
[ExpectedArgumentNullException]
public void PointwiseDivideWithOtherNullShouldThrowException()
{
var vector1 = CreateVector(Data);
Vector<Complex> vector2 = null;
var result = CreateVector(vector1.Count);
vector1.PointwiseDivide(vector2, result);
}
[Test]
[ExpectedArgumentNullException]
public void PointwiseDivideWithResultNullShouldThrowException()
{
var vector1 = CreateVector(Data);
var vector2 = vector1.Clone();
Vector<Complex> result = null;
vector1.PointwiseDivide(vector2, result);
}
[Test]
[ExpectedArgumentException]
public void PointwiseDivideWithInvalidResultLengthShouldThrowException()
{
var vector1 = CreateVector(Data);
var vector2 = vector1.Clone();
var result = CreateVector(vector1.Count + 1);
vector1.PointwiseDivide(vector2, result);
}
[Test]
public void PointWiseDivideUsingResultVector()
{
var vector1 = CreateVector(Data);
var vector2 = vector1.Clone();
var result = CreateVector(vector1.Count);
vector1.PointwiseDivide(vector2, result);
for (var i = 0; i < vector1.Count; i++)
{
AssertHelpers.AreEqual(Data[i] / Data[i], result[i]);
}
}
[Test]
public void CanCalculateOuterProduct()
{
var vector1 = CreateVector(Data);
var vector2 = CreateVector(Data);
Matrix<Complex> m = Vector<Complex>.OuterProduct(vector1, vector2);
for (var i = 0; i < vector1.Count; i++)
{
for (var j = 0; j < vector2.Count; j++)
{
AssertHelpers.AreEqual(m[i, j], vector1[i] * vector2[j]);
}
}
}
[Test]
[ExpectedArgumentNullException]
public void OuterProductWithFirstParameterNullShouldThrowException()
{
Vector<Complex> vector1 = null;
var vector2 = CreateVector(Data);
Vector<Complex>.OuterProduct(vector1, vector2);
}
[Test]
[ExpectedArgumentNullException]
public void OutercProductWithSecondParameterNullShouldThrowException()
{
var vector1 = CreateVector(Data);
Vector<Complex> vector2 = null;
Vector<Complex>.OuterProduct(vector1, vector2);
}
[Test]
public void CanCalculateTensorMultiply()
{
var vector1 = CreateVector(Data);
var vector2 = CreateVector(Data);
var m = vector1.TensorMultiply(vector2);
for (var i = 0; i < vector1.Count; i++)
{
for (var j = 0; j < vector2.Count; j++)
{
AssertHelpers.AreEqual(m[i, j], vector1[i] * vector2[j]);
}
}
}
[Test]
[ExpectedArgumentNullException]
public void TensorMultiplyWithNullParameterNullShouldThrowException()
{
var vector1 = CreateVector(Data);
Vector<Complex> vector2 = null;
vector1.TensorMultiply(vector2);
}
}
}

91
src/UnitTests/LinearAlgebraTests/Complex/VectorTests.Norm.cs

@ -0,0 +1,91 @@
// <copyright file="VectorTests.Norm.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>
namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex
{
using System;
using System.Numerics;
using MbUnit.Framework;
public abstract partial class VectorTests
{
[Test]
public void CanComputeNorm()
{
var vector = CreateVector(Data);
AssertHelpers.AlmostEqual(7.74596669241483, vector.Norm(2), 15);
}
[Test]
public void CanComputeNorm1()
{
var vector = CreateVector(Data);
AssertHelpers.AlmostEqual(16.0346843392517, vector.Norm(1), 15);
}
[Test]
public void CanComputeSquareNorm()
{
var vector = CreateVector(Data);
AssertHelpers.AlmostEqual(60, vector.Norm(2) * vector.Norm(2), 15);
}
[Test]
[Row(1, 16.0346843392517)]
[Row(2, 7.74596669241483)]
[Row(3, 6.28528392332871)]
[Row(10, 5.1608912235454)]
public void CanComputeNormP(int p, double expected)
{
var vector = CreateVector(Data);
AssertHelpers.AlmostEqual(expected, vector.Norm(p), 15);
}
[Test]
public void CanComputeNormInfinity()
{
var vector = CreateVector(Data);
AssertHelpers.AlmostEqual(5.09901951359279, vector.Norm(Double.PositiveInfinity), 15);
}
[Test]
[MultipleAsserts]
public void CanNormalizeVector()
{
var vector = CreateVector(Data);
var result = vector.Normalize(2);
AssertHelpers.AlmostEqual(new Complex(0.129099444873581, 0.129099444873581), result[0], 14);
AssertHelpers.AlmostEqual(new Complex(0.258198889747161, 0.129099444873581), result[1], 14);
AssertHelpers.AlmostEqual(new Complex(0.387298334620742, 0.129099444873581), result[2], 14);
AssertHelpers.AlmostEqual(new Complex(0.516397779494322, 0.129099444873581), result[3], 14);
AssertHelpers.AlmostEqual(new Complex(0.645497224367903, 0.129099444873581), result[4], 14);
}
}
}

398
src/UnitTests/LinearAlgebraTests/Complex/VectorTests.cs

@ -0,0 +1,398 @@
// <copyright file="VectorTests.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>
namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex
{
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Numerics;
using Distributions;
using LinearAlgebra.Generic;
using MbUnit.Framework;
[TestFixture]
public abstract partial class VectorTests
{
protected readonly Complex[] Data = { new Complex(1, 1), new Complex(2, 1), new Complex(3, 1), new Complex(4, 1), new Complex(5, 1) };
[Test]
[MultipleAsserts]
public void CanCloneVector()
{
var vector = CreateVector(Data);
var clone = vector.Clone();
Assert.AreNotSame(vector, clone);
Assert.AreEqual(vector.Count, clone.Count);
for (var index = 0; index < Data.Length; index++)
{
AssertHelpers.AreEqual(vector[index], clone[index]);
}
}
[Test]
[MultipleAsserts]
public void CanCloneVectorUsingICloneable()
{
var vector = CreateVector(Data);
var clone = (Vector<Complex>)((ICloneable)vector).Clone();
Assert.AreNotSame(vector, clone);
Assert.AreEqual(vector.Count, clone.Count);
for (var index = 0; index < Data.Length; index++)
{
AssertHelpers.AreEqual(vector[index], clone[index]);
}
}
[Test]
public void CanConvertVectorToString()
{
var vector = CreateVector(Data);
var str = vector.ToString();
var sep = CultureInfo.CurrentCulture.TextInfo.ListSeparator;
var decsep = CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator;
Assert.AreEqual(string.Format("(1{0} 1){1}(2, 1){1}(3{0} 1){1}(4{0} 1){1}(5{0} 1)", decsep, sep), str);
}
[Test]
[MultipleAsserts]
public void CanCopyPartialVectorToAnother()
{
var vector = CreateVector(Data);
var other = CreateVector(Data.Length);
vector.CopyTo(other, 2, 2, 2);
AssertHelpers.AreEqual(Complex.Zero, other[0]);
AssertHelpers.AreEqual(Complex.Zero, other[1]);
AssertHelpers.AreEqual(new Complex(3, 1), other[2]);
AssertHelpers.AreEqual(new Complex(4, 1), other[3]);
AssertHelpers.AreEqual(Complex.Zero, other[4]);
}
[Test]
[MultipleAsserts]
public void CanCopyPartialVectorToSelf()
{
var vector = CreateVector(Data);
vector.CopyTo(vector, 0, 2, 2);
AssertHelpers.AreEqual(new Complex(1, 1), vector[0]);
AssertHelpers.AreEqual(new Complex(2, 1), vector[1]);
AssertHelpers.AreEqual(new Complex(1, 1), vector[2]);
AssertHelpers.AreEqual(new Complex(2, 1), vector[3]);
AssertHelpers.AreEqual(new Complex(5, 1), vector[4]);
}
[Test]
[MultipleAsserts]
public void CanCopyVectorToAnother()
{
var vector = CreateVector(Data);
var other = CreateVector(Data.Length);
vector.CopyTo(other);
for (var index = 0; index < Data.Length; index++)
{
AssertHelpers.AreEqual(vector[index], other[index]);
}
}
[Test]
[Ignore]
public void CanCreateMatrix()
{
}
[Test]
public void CanCreateVector()
{
var expected = CreateVector(5);
var actual = expected.CreateVector(5);
Assert.AreEqual(expected.GetType(), actual.GetType(), "vectors are same type.");
}
[Test]
public void CanEnumerateOverVector()
{
var vector = CreateVector(Data);
Assert.AreElementsEqual(Data, vector);
}
[Test]
[MultipleAsserts]
public void CanEnumerateOverVectorUsingIEnumerable()
{
var enumerable = (IEnumerable)CreateVector(Data);
var index = 0;
foreach (var element in enumerable)
{
AssertHelpers.AreEqual(Data[index++], (Complex)element);
}
}
[Test]
[MultipleAsserts]
public void CanEquateVectors()
{
var vector1 = CreateVector(Data);
var vector2 = CreateVector(Data);
var vector3 = CreateVector(4);
Assert.IsTrue(vector1.Equals(vector1));
Assert.IsTrue(vector1.Equals(vector2));
Assert.IsFalse(vector1.Equals(vector3));
Assert.IsFalse(vector1.Equals(null));
}
[Test]
[MultipleAsserts]
public void ThrowsArgumentExceptionIfSizeIsNotPositive()
{
Assert.Throws<ArgumentException>(() => CreateVector(-1));
Assert.Throws<ArgumentException>(() => CreateVector(0));
}
[Test]
public void TestingForEqualityWithNonVectorReturnsFalse()
{
var vector = CreateVector(Data);
Assert.IsFalse(vector.Equals(2));
}
[Test]
public void CanTestForEqualityUsingObjectEquals()
{
var vector1 = CreateVector(Data);
var vector2 = CreateVector(Data);
Assert.IsTrue(vector1.Equals((object)vector2));
}
[Test]
public void VectorGetHashCode()
{
var vector = CreateVector(new [] { new Complex(1, 1), new Complex(2, 1), new Complex(3, 1), new Complex(4, 1), new Complex(5, 1) });
Assert.AreEqual(1104007323, vector.GetHashCode());
}
[Test]
public void GetIndexedEnumerator()
{
var vector = CreateVector(Data);
foreach (var pair in vector.GetIndexedEnumerator())
{
AssertHelpers.AreEqual(Data[pair.Key], pair.Value);
}
}
[Test]
public void CanConvertVectorToArray()
{
var vector = CreateVector(Data);
var array = vector.ToArray();
Assert.AreElementsEqual(vector, array);
}
[Test]
[MultipleAsserts]
public void CanConvertVectorToColumnMatrix()
{
var vector = CreateVector(Data);
var matrix = vector.ToColumnMatrix();
Assert.AreEqual(vector.Count, matrix.RowCount);
Assert.AreEqual(1, matrix.ColumnCount);
for (var i = 0; i < vector.Count; i++)
{
AssertHelpers.AreEqual(vector[i], matrix[i, 0]);
}
}
[Test]
[MultipleAsserts]
public void CanConvertVectorToRowMatrix()
{
var vector = CreateVector(Data);
var matrix = vector.ToRowMatrix();
Assert.AreEqual(vector.Count, matrix.ColumnCount);
Assert.AreEqual(1, matrix.RowCount);
for (var i = 0; i < vector.Count; i++)
{
AssertHelpers.AreEqual(vector[i], matrix[0, i]);
}
}
[Test]
public void CanSetValues()
{
var vector = CreateVector(Data);
vector.SetValues(Data);
for (var i = 0; i < Data.Length; i++)
{
AssertHelpers.AreEqual(vector[i], Data[i]);
}
}
[Test]
[Row(0, 5)]
[Row(2, 2)]
[Row(1, 4)]
[Row(6, 10, ExpectedException = typeof(ArgumentOutOfRangeException))]
[Row(1, 10, ExpectedException = typeof(ArgumentOutOfRangeException))]
[Row(1, -10, ExpectedException = typeof(ArgumentOutOfRangeException))]
public void CanCalculateSubVector(int index, int length)
{
var vector = CreateVector(Data);
var sub = vector.SubVector(index, length);
Assert.AreEqual(length, sub.Count);
for (var i = 0; i < length; i++)
{
AssertHelpers.AreEqual(vector[i + index], sub[i]);
}
}
[Test]
public void CanFindAbsoluteMinimumIndex()
{
var source = CreateVector(Data);
const int Expected = 0;
var actual = source.AbsoluteMinimumIndex();
Assert.AreEqual(Expected, actual);
}
[Test]
public void CanFindAbsoluteMinimum()
{
var source = CreateVector(Data);
var expected = new Complex(1, 1).Magnitude;
var actual = source.AbsoluteMinimum();
AssertHelpers.AreEqual(expected, actual);
}
[Test]
public void CanFindAbsoluteMaximumIndex()
{
var source = CreateVector(Data);
const int Expected = 4;
var actual = source.AbsoluteMaximumIndex();
Assert.AreEqual(Expected, actual);
}
[Test]
public void CanFindAbsoluteMaximum()
{
var source = CreateVector(Data);
var expected = new Complex(5, 1).Magnitude;
var actual = source.AbsoluteMaximum();
AssertHelpers.AreEqual(expected, actual);
}
[Test]
[Row(0, 5)]
[Row(2, 2)]
[Row(1, 4)]
[Row(6, 10, ExpectedException = typeof(ArgumentOutOfRangeException))]
[Row(1, 10, ExpectedException = typeof(ArgumentOutOfRangeException))]
[Row(1, -10, ExpectedException = typeof(ArgumentOutOfRangeException))]
public void CanGetSubVector(int index, int length)
{
var vector = CreateVector(Data);
var sub = vector.SubVector(index, length);
Assert.AreEqual(length, sub.Count);
for (var i = 0; i < length; i++)
{
AssertHelpers.AreEqual(vector[i + index], sub[i]);
}
}
[Test]
public void CanSum()
{
Complex[] testData = { new Complex(-20, -1), new Complex(-10, -1), new Complex(10, 1), new Complex(20, 1), new Complex(30,1) };
var vector = CreateVector(testData);
var actual = vector.Sum();
var expected = new Complex(30, 1);
AssertHelpers.AreEqual(expected, actual);
}
[Test]
public void CanSumMagnitudes()
{
Complex[] testData = { new Complex(-20, -1), new Complex(-10, -1), new Complex(10, 1), new Complex(20, 1), new Complex(30, 1) };
var vector = CreateVector(testData);
var actual = vector.SumMagnitudes();
var expected = testData.Sum(complex => complex.Magnitude);
Assert.AreEqual(expected, actual);
}
[Test]
[ExpectedArgumentNullException]
public void CanSetValuesWithNullParameterShouldThrowException()
{
var vector = CreateVector(Data);
vector.SetValues(null);
}
[Test]
[ExpectedArgumentException]
public void CanSetValuesWithNonEqualDataLengthShouldThrowException()
{
var vector = CreateVector(Data.Length + 2);
vector.SetValues(Data);
}
[Test]
[ExpectedArgumentException]
public void RandomWithNumberOfElementsLessThanZeroShouldThrowException()
{
var vector = CreateVector(4);
vector.Random(-2, new ContinuousUniform());
}
[Test]
public void CanClearVector()
{
Complex[] testData = { new Complex(-20, -1), new Complex(-10, -1), new Complex(10, 1), new Complex(20, 1), new Complex(30, 1) };
var vector = CreateVector(testData);
vector.Clear();
foreach (var element in vector)
{
AssertHelpers.AreEqual(Complex.Zero, element);
}
}
protected abstract Vector<Complex> CreateVector(int size);
protected abstract Vector<Complex> CreateVector(IList<Complex> data);
}
}

2
src/UnitTests/LinearAlgebraTests/Double/Factorization/CholeskyTests.cs

@ -30,9 +30,9 @@
namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double.Factorization
{
using LinearAlgebra.Generic.Factorization;
using MbUnit.Framework;
using LinearAlgebra.Double;
using LinearAlgebra.Double.Factorization;
public class CholeskyTests
{

1
src/UnitTests/LinearAlgebraTests/Double/Factorization/GramSchmidtTests.cs

@ -30,6 +30,7 @@
namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double.Factorization
{
using LinearAlgebra.Generic.Factorization;
using MbUnit.Framework;
using LinearAlgebra.Double.Factorization;

4
src/UnitTests/LinearAlgebraTests/Double/Factorization/LUTests.cs

@ -30,10 +30,10 @@
namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double.Factorization
{
using LinearAlgebra.Generic.Factorization;
using MbUnit.Framework;
using LinearAlgebra.Double;
using LinearAlgebra.Double.Factorization;
public class LUTests
{
[Test]

1
src/UnitTests/LinearAlgebraTests/Double/Factorization/QRTests.cs

@ -30,6 +30,7 @@
namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double.Factorization
{
using LinearAlgebra.Generic.Factorization;
using MbUnit.Framework;
using LinearAlgebra.Double;
using LinearAlgebra.Double.Factorization;

1
src/UnitTests/LinearAlgebraTests/Double/Factorization/SvdTests.cs

@ -31,6 +31,7 @@
namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double.Factorization
{
using System;
using LinearAlgebra.Generic.Factorization;
using MbUnit.Framework;
using LinearAlgebra.Double;
using LinearAlgebra.Double.Factorization;

2
src/UnitTests/LinearAlgebraTests/Double/Factorization/UserCholeskyTests.cs

@ -30,8 +30,8 @@
namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double.Factorization
{
using LinearAlgebra.Generic.Factorization;
using MbUnit.Framework;
using LinearAlgebra.Double.Factorization;
public class UserCholeskyTests
{

2
src/UnitTests/LinearAlgebraTests/Double/Factorization/UserLUTests.cs

@ -30,8 +30,8 @@
namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double.Factorization
{
using LinearAlgebra.Generic.Factorization;
using MbUnit.Framework;
using LinearAlgebra.Double.Factorization;
public class UserLUTests
{

1
src/UnitTests/LinearAlgebraTests/Double/Factorization/UserQRTests.cs

@ -30,6 +30,7 @@
namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double.Factorization
{
using LinearAlgebra.Generic.Factorization;
using MbUnit.Framework;
using LinearAlgebra.Double.Factorization;

1
src/UnitTests/LinearAlgebraTests/Double/Factorization/UserSvdTests.cs

@ -31,6 +31,7 @@
namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double.Factorization
{
using System;
using LinearAlgebra.Generic.Factorization;
using MbUnit.Framework;
using LinearAlgebra.Double.Factorization;

6
src/UnitTests/LinearAlgebraTests/Double/UserDefinedMatrixTests.cs

@ -31,7 +31,6 @@
namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
{
using System;
using System.Text;
using Distributions;
using LinearAlgebra.Generic;
using Properties;
@ -175,6 +174,11 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
{
return Math.Abs(val1);
}
public override Matrix<double> ConjugateTranspose()
{
throw new NotSupportedException();
}
}
public class UserDefinedMatrixTests : MatrixTests

6
src/UnitTests/LinearAlgebraTests/Double/UserDefinedVectorTests.cs

@ -32,7 +32,6 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
{
using System;
using System.Collections.Generic;
using System.Text;
using Distributions;
using LinearAlgebra.Generic;
using Properties;
@ -200,6 +199,11 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
{
return Math.Abs(val1);
}
public override void Conjugate(Vector<double> target)
{
throw new NotSupportedException();
}
}
public class UserDefinedVectorTests : VectorTests

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save