diff --git a/src/MathNet.Numerics.5.0.ReSharper b/src/MathNet.Numerics.5.0.ReSharper
index bbe461a5..bce082e6 100644
--- a/src/MathNet.Numerics.5.0.ReSharper
+++ b/src/MathNet.Numerics.5.0.ReSharper
@@ -3,750 +3,6 @@
SOLUTION
-
-
-
-
-
- en-US
- false
- pointwise
-Frobenius
-indices
-ipiv
-blocksize
-Dont
-Cholesky
-Matlab
-Endian
-
-
-
- en-US
- en-US
- en-US
-
- en-US
-
-
- Public Protected ProtectedInternal
- Any
- Maybe
- Maybe
- Any
-
-
- false
- 80
-
-
-
-
- false
-
-
- Any
- Class
- Maybe
- Maybe
- Any
-
-
- Any
- Enum
-
- Maybe
- Maybe
- Any
-
-
- Any
- EnumerationMember
-
- Maybe
- Maybe
- Any
-
-
-
- Classes, Enumerations, Enumeration values should be named in Pascal
-
-
- _
- m_
-
- Pascal
-
-
-
-
- false
-
-
- Public Internal Protected ProtectedInternal
- Property
-
- Maybe
- Maybe
- Any
-
-
- Public Internal Protected ProtectedInternal
- Method
-
- Maybe
- Maybe
- Any
-
-
- Public Internal Protected ProtectedInternal
- Event
-
- Maybe
- Maybe
- Any
-
-
-
- Non private properties, methods, events should be in pascal.
-
-
- _
- m_
-
- Pascal
-
-
-
-
- true
-
-
- Private
- Property
-
- Maybe
- Maybe
- Any
-
-
- Private
- Method
-
- Maybe
- Maybe
- Any
-
-
- Private
- Event
-
- Maybe
- Maybe
- Any
-
-
-
- Private properties, methods, events should be in camel.
-
-
- _
- m_
-
- Camel
-
-
-
-
- false
-
-
- Any
- Field
-
- Maybe
- Maybe
- Any
-
-
-
- Fields should be underscore camel.
-
- _
-
-
- m_
-
- Camel
-
-
-
-
- false
-
-
- Any
- Variable
- Maybe
- Maybe
- Any
-
-
- Variable should be declared in camel.
-
-
- _
- m_
-
- Camel
-
-
-
-
- false
-
-
- Any
- Parameter
- Maybe
- Maybe
- Any
-
-
- Function parameters should be in camel.
-
-
- _
- m_
-
- Camel
-
-
-
-
- false
-
-
- Any
- Namespace
- Maybe
- Maybe
- Any
-
-
- Namespaces should be in Pascal.
-
-
- _
- m_
-
- Pascal
-
-
-
-
- false
-
-
- Any
- Constant
- Maybe
- Maybe
- Any
-
-
- Constants should be in capital.
-
-
- UpperCase
-
-
-
-
- false
-
-
- Any
- Any
- Maybe
- Maybe
- Any
-
-
-
-
- Any
- Constant
- Maybe
- Maybe
- Any
-
-
- Acronyms should not have more than 2 characters.
-
-
- MatchesRegex
-
-
- ^(?>(XML|SQL|[A-Z]{0,2})[A-Z]?([^A-Z]|$)|[^A-Z]+)*$
-
-
- false
-
-
- Any
- Any
- Maybe
- Maybe
- Any
-
-
-
-
- Any
- Constant
-
- Maybe
- Maybe
- Any
-
-
- Any
- Field
-
- Maybe
- Maybe
- Any
-
-
- Private
- Method
-
- Maybe
- Maybe
- Any
-
-
- None
- Namespace
- Maybe
- Maybe
- Any
-
-
- Names should not have underscore character
-
-
- NotMatchesRegex
-
-
- (?<remove>_)
-
-
- false
-
-
- Any
- Class
- NUnit.Framework.TestFixtureAttribute
- Maybe
- Maybe
- Any
-
-
- TestFixtures should end with Test.
-
-
- Test
-
- None
-
- Test
-
-
-
-
- false
-
-
- Any
- Method
- NUnit.Framework.TestAttribute
- Maybe
- Maybe
- Any
-
-
- Test methods should start with Test.
-
- Test
-
-
- None
-
-
- Test
-
-
-
- false
-
-
- Any
- Enum
- Maybe
- Maybe
- Any
-
-
- Enumerations should not end with Enum.
-
-
- None
-
-
- Enum
-
-
-
- false
-
-
- Any
- EnumerationMember
- Maybe
- Maybe
- Any
-
-
- Do not name enumerations reserved.
-
-
- NotMatchesRegex
-
-
- (?<remove>(reserved|Reserved))
-
-
- false
-
-
- Any
- Event
- Maybe
- Maybe
- Any
-
-
- Event should not have Before or After prefix.
-
-
- Before
- After
-
- None
-
-
-
-
- false
-
-
- Any
- Enum
- System.FlagsAttribute
- Maybe
- Maybe
- Any
-
-
- Flags enums should have plural names
-
-
- None
-
- s
-
-
-
-
- true
-
-
- Any
- Enum
- Maybe
- Maybe
- Any
-
-
-
-
- Any
- Enum
- System.FlagsAttribute
- Maybe
- Maybe
- Any
-
-
- Enums that are not flags should not have plural names
-
-
- None
-
-
- s
-
-
-
- false
-
-
- Any
- Class
- System.Attribute
- Maybe
- Maybe
- Any
-
-
- Attribute should end with Attribute.
-
-
- None
-
- Attribute
-
-
-
-
- false
-
-
- Any
- Class
- System.EventArgs
- Maybe
- Maybe
- Any
-
-
- EventArgs should end with EventArgs.
-
-
- None
-
- EventArgs
-
-
-
-
- false
-
-
- Any
- Class
- System.Exception
- Maybe
- Maybe
- Any
-
-
- Exceptions should end with Exception.
-
-
- None
-
- Exception
-
-
-
-
- false
-
-
- Any
- Class
- System.Collections.Stack
- Maybe
- Maybe
- Any
-
-
- Any
- Class
- System.Collections.Generic.Stack
-
- Maybe
- Maybe
- Any
-
-
-
- Stack should end with Collection or Stack.
-
-
- None
-
- Collection
- Stack
-
-
-
-
- false
-
-
- Any
- Class
- System.Collections.ICollection
- Maybe
- Maybe
- Any
-
-
- Any
- Class
- System.Collections.IEnumerable
- Maybe
- Maybe
- Any
-
-
- Any
- Class
- System.Collections.Generic.ICollection
- Maybe
- Maybe
- Any
-
-
-
-
- Any
- Class
- System.Collections.Stack
- Maybe
- Maybe
- Any
-
-
- Any
- Class
- System.Collections.Queue
- Maybe
- Maybe
- Any
-
-
- Any
- Class
- System.Collections.Generic.Stack
-
- Maybe
- Maybe
- Any
-
-
- Collections should end with Collection.
-
-
- None
-
- Collection
-
-
-
-
- false
-
-
- Any
- Class
- System.Collections.IDictionary
- Maybe
- Maybe
- Any
-
-
- Any
- Class
- System.Collections.Generic.IDictionary
- Maybe
- Maybe
- Any
-
-
- Dictionary should end with Dictionary.
-
-
- None
-
- Dictionary
-
-
-
-
- false
-
-
- Any
- Class
- System.Collections.Queue
- Maybe
- Maybe
- Any
-
-
- Queue should end with Collection or Queue.
-
-
- None
-
- Collection
- Queue
-
-
-
-
- false
-
-
- Any
- Interface
- Maybe
- Maybe
- Any
-
-
- Interfaces should be in Pascal and start with I.
-
- I
-
-
- Pascal
-
-
-
-
-
-
-
-
- Public Protected ProtectedInternal
- Any
- Maybe
- Maybe
- Any
-
-
-
- (?#email)\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*
- (?#url)http(s)?://([\w-]+\.)+[\w-]+(/[\w- ./?%&=]*)?
-
-
- NEXT_LINE
@@ -866,6 +122,7 @@ Endian
+
diff --git a/src/Numerics/Algorithms/LinearAlgebra/ManagedLinearAlgebraProvider.cs b/src/Numerics/Algorithms/LinearAlgebra/ManagedLinearAlgebraProvider.cs
index d7e3d74d..3c10d0f1 100644
--- a/src/Numerics/Algorithms/LinearAlgebra/ManagedLinearAlgebraProvider.cs
+++ b/src/Numerics/Algorithms/LinearAlgebra/ManagedLinearAlgebraProvider.cs
@@ -3650,7 +3650,7 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra
// TODO - For small matrices we should get rid of the parallelism because of startup costs.
// Perhaps the following implementations would be a good one
// http://blog.feradz.com/2009/01/cache-efficient-matrix-multiplication/
- MatrixMultiplyWithUpdate(Transpose.DontTranspose, Transpose.DontTranspose, 1.0f, x, xRows, xColumns, y, yRows, yColumns, 0.0f, result);
+ MatrixMultiplyWithUpdate(Transpose.DontTranspose, Transpose.DontTranspose, 1.0f, xdata, xRows, xColumns, ydata, yRows, yColumns, 0.0f, result);
}
///
@@ -4635,7 +4635,7 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra
// TODO - For small matrices we should get rid of the parallelism because of startup costs.
// Perhaps the following implementations would be a good one
// http://blog.feradz.com/2009/01/cache-efficient-matrix-multiplication/
- MatrixMultiplyWithUpdate(Transpose.DontTranspose, Transpose.DontTranspose, Complex.One, x, xRows, xColumns, y, yRows, yColumns, Complex.Zero, result);
+ MatrixMultiplyWithUpdate(Transpose.DontTranspose, Transpose.DontTranspose, Complex.One, xdata, xRows, xColumns, ydata, yRows, yColumns, Complex.Zero, result);
}
///
@@ -5585,7 +5585,7 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra
// TODO - For small matrices we should get rid of the parallelism because of startup costs.
// Perhaps the following implementations would be a good one
// http://blog.feradz.com/2009/01/cache-efficient-matrix-multiplication/
- MatrixMultiplyWithUpdate(Transpose.DontTranspose, Transpose.DontTranspose, Complex32.One, x, xRows, xColumns, y, yRows, yColumns, Complex32.Zero, result);
+ MatrixMultiplyWithUpdate(Transpose.DontTranspose, Transpose.DontTranspose, Complex32.One, xdata, xRows, xColumns, ydata, yRows, yColumns, Complex32.Zero, result);
}
///
diff --git a/src/Numerics/Constants.cs b/src/Numerics/Constants.cs
index 51d7216d..513d20ea 100644
--- a/src/Numerics/Constants.cs
+++ b/src/Numerics/Constants.cs
@@ -30,6 +30,9 @@
namespace MathNet.Numerics
{
+ using System.Numerics;
+ using System.Runtime.InteropServices;
+
///
/// A collection of frequently used mathematical constants.
///
diff --git a/src/Numerics/LinearAlgebra/Double/DenseMatrix.cs b/src/Numerics/LinearAlgebra/Double/DenseMatrix.cs
index 7e6e1829..6ad37f3d 100644
--- a/src/Numerics/LinearAlgebra/Double/DenseMatrix.cs
+++ b/src/Numerics/LinearAlgebra/Double/DenseMatrix.cs
@@ -215,6 +215,61 @@ namespace MathNet.Numerics.LinearAlgebra.Double
return ret;
}
+ /// Calculates the L1 norm.
+ /// The L1 norm of the matrix.
+ 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 += Math.Abs(Data[(j * RowCount) + i]);
+ }
+
+ norm = Math.Max(norm, s);
+ }
+
+ return norm;
+ }
+
+ /// Calculates the Frobenius norm of this matrix.
+ /// The Frobenius norm of this matrix.
+ public override double FrobeniusNorm()
+ {
+ var transpose = (DenseMatrix)Transpose();
+ var aat = this * transpose;
+
+ var norm = 0.0;
+ for (var i = 0; i < RowCount; i++)
+ {
+ norm += Math.Abs(aat.Data[(i * RowCount) + i]);
+ }
+
+ norm = Math.Sqrt(norm);
+ return norm;
+ }
+
+ /// Calculates the infinity norm of this matrix.
+ /// The infinity norm of this matrix.
+ 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 += Math.Abs(Data[(j * RowCount) + i]);
+ }
+
+ norm = Math.Max(norm, s);
+ }
+
+ return norm;
+ }
+
#region Elementary operations
///
@@ -387,6 +442,75 @@ namespace MathNet.Numerics.LinearAlgebra.Double
return result;
}
+ ///
+ /// Multiplies this dense matrix with transpose of another dense matrix and places the results into the result dense matrix.
+ ///
+ /// The matrix to multiply with.
+ /// The result of the multiplication.
+ /// If the other matrix is .
+ /// If the result matrix is .
+ /// If this.Columns != other.Rows.
+ /// If the result matrix's dimensions are not the this.Rows x other.Columns.
+ public override void TransposeAndMultiply(Matrix other, Matrix 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);
+ }
+
+ ///
+ /// Multiplies this matrix with transpose of another matrix and returns the result.
+ ///
+ /// The matrix to multiply with.
+ /// If this.Columns != other.Rows.
+ /// If the other matrix is .
+ /// The result of multiplication.
+ public override Matrix TransposeAndMultiply(Matrix 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;
+ }
+
///
/// Multiplies two dense matrices.
///
diff --git a/src/Numerics/LinearAlgebra/Double/DiagonalMatrix.cs b/src/Numerics/LinearAlgebra/Double/DiagonalMatrix.cs
new file mode 100644
index 00000000..4b558b04
--- /dev/null
+++ b/src/Numerics/LinearAlgebra/Double/DiagonalMatrix.cs
@@ -0,0 +1,1552 @@
+//
+// 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.
+//
+
+namespace MathNet.Numerics.LinearAlgebra.Double
+{
+ using System;
+ using System.Linq;
+ using Properties;
+ using Threading;
+
+ ///
+ /// A matrix type for diagonal matrices.
+ ///
+ ///
+ /// Diagonal matrices can be non-square matrices but the diagonal always starts
+ /// at element 0,0. A diagonal matrix will throw an exception if non diagonal
+ /// entries are set. The exception to this is when the off diagonal elements are
+ /// 0.0 or NaN; these settings will cause no change to the diagonal matrix.
+ ///
+ public class DiagonalMatrix : Matrix
+ {
+ ///
+ /// Initializes a new instance of the class. This matrix is square with a given size.
+ ///
+ /// the size of the square matrix.
+ ///
+ /// If is less than one.
+ ///
+ public DiagonalMatrix(int order) : base(order)
+ {
+ Data = new double[order * order];
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// The number of rows.
+ ///
+ ///
+ /// The number of columns.
+ ///
+ public DiagonalMatrix(int rows, int columns) : base(rows, columns)
+ {
+ Data = new double[Math.Min(rows, columns)];
+ }
+
+ ///
+ /// Initializes a new instance of the class with all entries set to a particular value.
+ ///
+ ///
+ /// The number of rows.
+ ///
+ ///
+ /// The number of columns.
+ ///
+ /// The value which we assign to each element of the matrix.
+ public DiagonalMatrix(int rows, int columns, double value) : base(rows, columns)
+ {
+ Data = new double[Math.Min(rows, columns)];
+ for (var i = 0; i < Data.Length; i++)
+ {
+ Data[i] = value;
+ }
+ }
+
+ ///
+ /// Initializes a new instance of the class from a one dimensional array with diagonal elements. This constructor
+ /// will reference the one dimensional array and not copy it.
+ ///
+ /// The number of rows.
+ /// The number of columns.
+ /// The one dimensional array which contain diagonal elements.
+ public DiagonalMatrix(int rows, int columns, double[] diagonalArray) : base(rows, columns)
+ {
+ Data = diagonalArray;
+ }
+
+ ///
+ /// Initializes a new instance of the class from a 2D array.
+ ///
+ /// The 2D array to create this matrix from.
+ /// When contains an off-diagonal element.
+ /// Depending on the implementation, an
+ /// may be thrown if one of the indices is outside the dimensions of the matrix.
+ public DiagonalMatrix(double[,] array) : this(array.GetLength(0), array.GetLength(1))
+ {
+ var rows = array.GetLength(0);
+ var columns = array.GetLength(1);
+
+ for (var i = 0; i < rows; i++)
+ {
+ for (var j = 0; j < columns; j++)
+ {
+ if (i == j)
+ {
+ Data[i] = array[i, j];
+ }
+ else if (array[i, j] != 0.0 && !Double.IsNaN(array[i, j]))
+ {
+ throw new IndexOutOfRangeException("Cannot set an off-diagonal element in a diagonal matrix.");
+ }
+ }
+ }
+ }
+
+ ///
+ /// Gets the matrix's data.
+ ///
+ /// The matrix's data.
+ internal double[] Data
+ {
+ get;
+ private set;
+ }
+
+ ///
+ /// Retrieves the requested element without range checking.
+ ///
+ ///
+ /// The row of the element.
+ ///
+ ///
+ /// The column of the element.
+ ///
+ ///
+ /// The requested element.
+ ///
+ /// Depending on the implementation, an
+ /// may be thrown if one of the indices is outside the dimensions of the matrix.
+ public override double At(int row, int column)
+ {
+ return row == column ? Data[row] : 0.0;
+ }
+
+ ///
+ /// Sets the value of the given element.
+ ///
+ ///
+ /// The row of the element.
+ ///
+ ///
+ /// The column of the element.
+ ///
+ ///
+ /// The value to set the element to.
+ ///
+ /// When trying to set an off diagonal element.
+ /// Depending on the implementation, an
+ /// may be thrown if one of the indices is outside the dimensions of the matrix.
+ public override void At(int row, int column, double value)
+ {
+ if (row == column)
+ {
+ Data[row] = value;
+ }
+ else if (value != 0.0 && !Double.IsNaN(value))
+ {
+ throw new IndexOutOfRangeException("Cannot set an off-diagonal element in a diagonal matrix.");
+ }
+ }
+
+ ///
+ /// Creates a DiagonalMatrix for the given number of rows and columns.
+ ///
+ ///
+ /// The number of rows.
+ ///
+ ///
+ /// The number of columns.
+ ///
+ ///
+ /// A DiagonalMatrix with the given dimensions.
+ ///
+ public override Matrix CreateMatrix(int numberOfRows, int numberOfColumns)
+ {
+ return new DiagonalMatrix(numberOfRows, numberOfColumns);
+ }
+
+ ///
+ /// Creates a with a the given dimension.
+ ///
+ /// The size of the vector.
+ ///
+ /// A with the given dimension.
+ ///
+ public override Vector CreateVector(int size)
+ {
+ return new SparseVector(size);
+ }
+
+ ///
+ /// Sets all values to zero.
+ ///
+ public override void Clear()
+ {
+ Array.Clear(Data, 0, Data.Length);
+ }
+
+ ///
+ /// Indicates whether the current object is equal to another object of the same type.
+ ///
+ ///
+ /// An object to compare with this object.
+ ///
+ ///
+ /// true if the current object is equal to the parameter; otherwise, false.
+ ///
+ public override bool Equals(object obj)
+ {
+ var diagonalMatrix = obj as DiagonalMatrix;
+
+ if (diagonalMatrix == null)
+ {
+ return base.Equals(obj);
+ }
+
+ // Accept if the argument is the same object as this
+ if (ReferenceEquals(this, diagonalMatrix))
+ {
+ return true;
+ }
+
+ if (diagonalMatrix.Data.Length != Data.Length)
+ {
+ return false;
+ }
+
+ // If all else fails, perform element wise comparison.
+ return !Data.Where((t, i) => t != diagonalMatrix.Data[i]).Any();
+ }
+
+ ///
+ /// Returns a hash code for this instance.
+ ///
+ ///
+ /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.
+ ///
+ public override int GetHashCode()
+ {
+ var hashNum = Math.Min(Data.Length, 25);
+ long hash = 0;
+ for (var i = 0; i < hashNum; i++)
+ {
+#if SILVERLIGHT
+ hash ^= Precision.DoubleToInt64Bits(Data[i]);
+#else
+ hash ^= BitConverter.DoubleToInt64Bits(Data[i]);
+#endif
+ }
+
+ return BitConverter.ToInt32(BitConverter.GetBytes(hash), 4);
+ }
+
+ #region Elementary operations
+ ///
+ /// Adds another matrix to this matrix. The result will be written into this matrix.
+ ///
+ /// The matrix to add to this matrix.
+ /// If the other matrix is .
+ /// If the two matrices don't have the same dimensions.
+ /// If is not .
+ public override void Add(Matrix other)
+ {
+ if (other == null)
+ {
+ throw new ArgumentNullException("other");
+ }
+
+ var m = other as DiagonalMatrix;
+ if (m == null)
+ {
+ throw new ArgumentException(Resources.ArgumentTypeMismatch);
+ }
+
+ Add(m);
+ }
+
+ ///
+ /// Adds another to this matrix. The result will be written into this matrix.
+ ///
+ /// The to add to this matrix.
+ /// If the other matrix is .
+ /// If the two matrices don't have the same dimensions.
+ public void Add(DiagonalMatrix 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);
+ }
+
+ ///
+ /// Subtracts another matrix from this matrix. The result will be written into this matrix.
+ ///
+ /// The matrix to subtract.
+ /// If the other matrix is .
+ /// If the two matrices don't have the same dimensions.
+ /// If is not .
+ public override void Subtract(Matrix other)
+ {
+ if (other == null)
+ {
+ throw new ArgumentNullException("other");
+ }
+
+ var m = other as DiagonalMatrix;
+ if (m == null)
+ {
+ throw new ArgumentException(Resources.ArgumentTypeMismatch);
+ }
+
+ Subtract(m);
+ }
+
+ ///
+ /// Subtracts another from this matrix. The result will be written into this matrix.
+ ///
+ /// The to subtract.
+ /// If the other matrix is .
+ /// If the two matrices don't have the same dimensions.
+ public void Subtract(DiagonalMatrix 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);
+ }
+
+ ///
+ /// Copies the values of the given array to the diagonal.
+ ///
+ /// The array to copy the values from. The length of the vector should be
+ /// Min(Rows, Columns).
+ /// If is .
+ /// If the length of does not
+ /// equal Min(Rows, Columns).
+ /// For non-square matrices, the elements of are copied to
+ /// this[i,i].
+ public override void SetDiagonal(double[] source)
+ {
+ if (source == null)
+ {
+ throw new ArgumentNullException("source");
+ }
+
+ if (source.Length != Data.Length)
+ {
+ throw new ArgumentException(Resources.ArgumentArraysSameLength, "source");
+ }
+
+ Buffer.BlockCopy(source, 0, Data, 0, source.Length * Constants.SizeOfDouble);
+ }
+
+ ///
+ /// Copies the values of the given to the diagonal.
+ ///
+ /// The vector to copy the values from. The length of the vector should be
+ /// Min(Rows, Columns).
+ /// If is .
+ /// If the length of does not
+ /// equal Min(Rows, Columns).
+ /// For non-square matrices, the elements of are copied to
+ /// this[i,i].
+ public override void SetDiagonal(Vector source)
+ {
+ var denseSource = source as DenseVector;
+ if (denseSource == null)
+ {
+ base.SetDiagonal(source);
+ return;
+ }
+
+ if (Data.Length != denseSource.Data.Length)
+ {
+ throw new ArgumentException(Resources.ArgumentVectorsSameLength, "source");
+ }
+
+ Buffer.BlockCopy(denseSource.Data, 0, Data, 0, denseSource.Data.Length * Constants.SizeOfDouble);
+ }
+
+ ///
+ /// Multiplies each element of this matrix with a scalar.
+ ///
+ /// The scalar to multiply with.
+ public override void Multiply(double scalar)
+ {
+ if (scalar == 0.0)
+ {
+ Clear();
+ return;
+ }
+
+ if (scalar == 1.0)
+ {
+ return;
+ }
+
+ Control.LinearAlgebraProvider.ScaleArray(scalar, Data);
+ }
+
+ ///
+ /// Multiplies this diagonal matrix with another diagonal matrix and places the results into the result diagonal matrix.
+ ///
+ /// The matrix to multiply with.
+ /// The result of the multiplication.
+ /// If the other matrix is .
+ /// If the result matrix is .
+ /// If this.Columns != other.Rows.
+ /// If the result matrix's dimensions are not the this.Rows x other.Columns.
+ public override void Multiply(Matrix other, Matrix 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 DiagonalMatrix;
+ var r = result as DiagonalMatrix;
+
+ if (m == null || r == null)
+ {
+ base.Multiply(other, result);
+ }
+ else
+ {
+ var thisDataCopy = new double[r.Data.Length];
+ var otherDataCopy = new double[r.Data.Length];
+ Buffer.BlockCopy(Data, 0, thisDataCopy, 0, (r.Data.Length > Data.Length) ? Data.Length * Constants.SizeOfDouble : r.Data.Length * Constants.SizeOfDouble);
+ Buffer.BlockCopy(m.Data, 0, otherDataCopy, 0, (r.Data.Length > m.Data.Length) ? m.Data.Length * Constants.SizeOfDouble : r.Data.Length * Constants.SizeOfDouble);
+
+ Control.LinearAlgebraProvider.PointWiseMultiplyArrays(thisDataCopy, otherDataCopy, r.Data);
+ }
+ }
+
+ ///
+ /// Multiplies this matrix with another matrix and returns the result.
+ ///
+ /// The matrix to multiply with.
+ /// If this.Columns != other.Rows.
+ /// If the other matrix is .
+ /// The result of multiplication.
+ public override Matrix Multiply(Matrix other)
+ {
+ if (other == null)
+ {
+ throw new ArgumentNullException("other");
+ }
+
+ if (ColumnCount != other.RowCount)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixDimensions);
+ }
+
+ var m = other as DiagonalMatrix;
+ if (m == null)
+ {
+ return base.Multiply(other);
+ }
+
+ var result = (DiagonalMatrix)CreateMatrix(RowCount, other.ColumnCount);
+ Multiply(other, result);
+ return result;
+ }
+
+ ///
+ /// Multiplies this matrix with a vector and places the results into the result matrix.
+ ///
+ /// The vector to multiply with.
+ /// The result of the multiplication.
+ /// If is .
+ /// If is .
+ /// If result.Count != this.RowCount.
+ /// If this.ColumnCount != .Count.
+ public override void Multiply(Vector rightSide, Vector result)
+ {
+ if (rightSide == null)
+ {
+ throw new ArgumentNullException("rightSide");
+ }
+
+ if (ColumnCount != rightSide.Count)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixDimensions, "rightSide");
+ }
+
+ if (result == null)
+ {
+ throw new ArgumentNullException("result");
+ }
+
+ if (RowCount != result.Count)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixDimensions, "result");
+ }
+
+ if (ReferenceEquals(rightSide, result))
+ {
+ var tmp = result.CreateVector(result.Count);
+ Multiply(rightSide, tmp);
+ tmp.CopyTo(result);
+ }
+ else
+ {
+ // Clear the result vector
+ result.Clear();
+
+ // Multiply the elements in the vector with the corresponding diagonal element in this.
+ for (var r = 0; r < Data.Length; r++)
+ {
+ result[r] = Data[r] * rightSide[r];
+ }
+ }
+ }
+
+ ///
+ /// Left multiply a matrix with a vector ( = vector * matrix ) and place the result in the result vector.
+ ///
+ /// The vector to multiply with.
+ /// The result of the multiplication.
+ /// If is .
+ /// If the result matrix is .
+ /// If result.Count != this.ColumnCount.
+ /// If this.RowCount != .Count.
+ public override void LeftMultiply(Vector leftSide, Vector result)
+ {
+ if (leftSide == null)
+ {
+ throw new ArgumentNullException("leftSide");
+ }
+
+ if (RowCount != leftSide.Count)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixDimensions, "leftSide");
+ }
+
+ if (result == null)
+ {
+ throw new ArgumentNullException("result");
+ }
+
+ if (ColumnCount != result.Count)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixDimensions, "result");
+ }
+
+ if (ReferenceEquals(leftSide, result))
+ {
+ var tmp = result.CreateVector(result.Count);
+ LeftMultiply(leftSide, tmp);
+ tmp.CopyTo(result);
+ }
+ else
+ {
+ // Clear the result vector
+ result.Clear();
+
+ // Multiply the elements in the vector with the corresponding diagonal element in this.
+ for (var r = 0; r < Data.Length; r++)
+ {
+ result[r] = Data[r] * leftSide[r];
+ }
+ }
+ }
+
+ ///
+ /// Computes the determinant of this matrix.
+ ///
+ /// The determinant of this matrix.
+ public override double Determinant()
+ {
+ if (RowCount != ColumnCount)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixSquare);
+ }
+
+ return Data.Aggregate(1.0, (current, t) => current * t);
+ }
+
+ ///
+ /// Returns the elements of the diagonal in a .
+ ///
+ /// The elements of the diagonal.
+ /// For non-square matrices, the method returns Min(Rows, Columns) elements where
+ /// i == j (i is the row index, and j is the column index).
+ public override Vector Diagonal()
+ {
+ // TODO: Should we return reference to array? In current implementation we return copy of array, so changes in DenseVector will
+ // not influence onto diagonal elements
+ return new DenseVector((double[])Data.Clone());
+ }
+
+ ///
+ /// Multiplies this diagonal matrix with transpose of another diagonal matrix and places the results into the result diagonal matrix.
+ ///
+ /// The matrix to multiply with.
+ /// The result of the multiplication.
+ /// If the other matrix is .
+ /// If the result matrix is .
+ /// If this.Columns != other.Rows.
+ /// If the result matrix's dimensions are not the this.Rows x other.Columns.
+ public override void TransposeAndMultiply(Matrix other, Matrix result)
+ {
+ var otherDiagonal = other as DiagonalMatrix;
+ var resultDiagonal = result as DiagonalMatrix;
+
+ if (otherDiagonal == null || resultDiagonal == null)
+ {
+ base.TransposeAndMultiply(other, result);
+ return;
+ }
+
+ Multiply(otherDiagonal.Transpose(), result);
+ }
+
+ ///
+ /// Multiplies this matrix with transpose of another matrix and returns the result.
+ ///
+ /// The matrix to multiply with.
+ /// If this.Columns != other.Rows.
+ /// If the other matrix is .
+ /// The result of multiplication.
+ public override Matrix TransposeAndMultiply(Matrix other)
+ {
+ var otherDiagonal = other as DiagonalMatrix;
+ if (otherDiagonal == null)
+ {
+ return base.TransposeAndMultiply(other);
+ }
+
+ if (ColumnCount != otherDiagonal.ColumnCount)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixDimensions);
+ }
+
+ var result = (DiagonalMatrix)CreateMatrix(RowCount, other.RowCount);
+ TransposeAndMultiply(other, result);
+ return result;
+ }
+
+ ///
+ /// Multiplies two diagonal matrices.
+ ///
+ /// The left matrix to multiply.
+ /// The right matrix to multiply.
+ /// The result of multiplication.
+ /// If or is .
+ /// If the dimensions of or don't conform.
+ public static DiagonalMatrix operator *(DiagonalMatrix leftSide, DiagonalMatrix 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 (DiagonalMatrix)leftSide.Multiply(rightSide);
+ }
+
+ #endregion
+
+ ///
+ /// Copies the elements of this matrix to the given matrix.
+ ///
+ ///
+ /// The matrix to copy values into.
+ ///
+ ///
+ /// If target is .
+ ///
+ ///
+ /// If this and the target matrix do not have the same dimensions..
+ ///
+ public override void CopyTo(Matrix target)
+ {
+ var diagonalTarget = target as DiagonalMatrix;
+
+ if (diagonalTarget == null)
+ {
+ base.CopyTo(target);
+ return;
+ }
+
+ if (ReferenceEquals(this, target))
+ {
+ return;
+ }
+
+ if (RowCount != target.RowCount || ColumnCount != target.ColumnCount)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixDimensions, "target");
+ }
+
+ Buffer.BlockCopy(Data, 0, diagonalTarget.Data, 0, Data.Length * Constants.SizeOfDouble);
+ }
+
+ ///
+ /// Returns the transpose of this matrix.
+ ///
+ /// The transpose of this matrix.
+ public override Matrix Transpose()
+ {
+ var ret = new DiagonalMatrix(ColumnCount, RowCount);
+ Buffer.BlockCopy(Data, 0, ret.Data, 0, Data.Length * Constants.SizeOfDouble);
+ return ret;
+ }
+
+ ///
+ /// Copies the requested column elements into the given vector.
+ ///
+ /// The column to copy elements from.
+ /// The row to start copying from.
+ /// The number of elements to copy.
+ /// The to copy the column into.
+ /// If the result is .
+ /// If is negative,
+ /// or greater than or equal to the number of columns.
+ /// If is negative,
+ /// or greater than or equal to the number of rows.
+ /// If +
+ /// is greater than or equal to the number of rows.
+ /// If is not positive.
+ /// If result.Count < length.
+ public override void Column(int columnIndex, int rowIndex, int length, Vector result)
+ {
+ if (result == null)
+ {
+ throw new ArgumentNullException("result");
+ }
+
+ if (columnIndex >= ColumnCount || columnIndex < 0)
+ {
+ throw new ArgumentOutOfRangeException("columnIndex");
+ }
+
+ if (rowIndex >= RowCount || rowIndex < 0)
+ {
+ throw new ArgumentOutOfRangeException("rowIndex");
+ }
+
+ if (rowIndex + length > RowCount)
+ {
+ throw new ArgumentOutOfRangeException("length");
+ }
+
+ if (length < 1)
+ {
+ throw new ArgumentException(Resources.ArgumentMustBePositive, "length");
+ }
+
+ if (result.Count < length)
+ {
+ throw new ArgumentException(Resources.ArgumentVectorsSameLength, "result");
+ }
+
+ // Clear the result and copy the diagonal entry.
+ result.Clear();
+ if (columnIndex >= rowIndex && columnIndex < rowIndex + length && columnIndex < Data.Length)
+ {
+ result[columnIndex - rowIndex] = Data[columnIndex];
+ }
+ }
+
+ ///
+ /// Copies the requested row elements into a new .
+ ///
+ /// The row to copy elements from.
+ /// The column to start copying from.
+ /// The number of elements to copy.
+ /// The to copy the column into.
+ /// If the result is .
+ /// If is negative,
+ /// or greater than or equal to the number of columns.
+ /// If is negative,
+ /// or greater than or equal to the number of rows.
+ /// If +
+ /// is greater than or equal to the number of rows.
+ /// If is not positive.
+ /// If result.Count < length.
+ public override void Row(int rowIndex, int columnIndex, int length, Vector result)
+ {
+ if (result == null)
+ {
+ throw new ArgumentNullException("result");
+ }
+
+ if (rowIndex >= RowCount || rowIndex < 0)
+ {
+ throw new ArgumentOutOfRangeException("rowIndex");
+ }
+
+ if (columnIndex >= ColumnCount || columnIndex < 0)
+ {
+ throw new ArgumentOutOfRangeException("columnIndex");
+ }
+
+ if (columnIndex + length > ColumnCount)
+ {
+ throw new ArgumentOutOfRangeException("length");
+ }
+
+ if (length < 1)
+ {
+ throw new ArgumentException(Resources.ArgumentMustBePositive, "length");
+ }
+
+ if (result.Count < length)
+ {
+ throw new ArgumentException(Resources.ArgumentVectorsSameLength, "result");
+ }
+
+ // Clear the result and copy the diagonal entry.
+ result.Clear();
+ if (rowIndex >= columnIndex && rowIndex < columnIndex + length && rowIndex < Data.Length)
+ {
+ result[rowIndex - columnIndex] = Data[rowIndex];
+ }
+ }
+
+ /// Calculates the L1 norm.
+ /// The L1 norm of the matrix.
+ public override double L1Norm()
+ {
+ return Data.Aggregate(double.NegativeInfinity, (current, t) => Math.Max(current, Math.Abs(t)));
+ }
+
+ /// Calculates the L2 norm.
+ /// The L2 norm of the matrix.
+ public override double L2Norm()
+ {
+ return Data.Aggregate(double.NegativeInfinity, (current, t) => Math.Max(current, Math.Abs(t)));
+ }
+
+ /// Calculates the Frobenius norm of this matrix.
+ /// The Frobenius norm of this matrix.
+ public override double FrobeniusNorm()
+ {
+ var norm = Data.Sum(t => t * t);
+ return Math.Sqrt(norm);
+ }
+
+ /// Calculates the infinity norm of this matrix.
+ /// The infinity norm of this matrix.
+ public override double InfinityNorm()
+ {
+ return L1Norm();
+ }
+
+ /// Calculates the condition number of this matrix.
+ /// The condition number of the matrix.
+ public override double ConditionNumber()
+ {
+ var maxSv = double.NegativeInfinity;
+ var minSv = double.PositiveInfinity;
+ for (var i = 0; i < Data.Length; i++)
+ {
+ maxSv = Math.Max(maxSv, Math.Abs(Data[i]));
+ minSv = Math.Min(minSv, Math.Abs(Data[i]));
+ }
+
+ return maxSv / minSv;
+ }
+
+ /// Computes the inverse of this matrix.
+ /// If is not a square matrix.
+ /// If is singular.
+ /// The inverse of this matrix.
+ public override Matrix Inverse()
+ {
+ if (RowCount != ColumnCount)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixSquare);
+ }
+
+ var inverse = (DiagonalMatrix)Clone();
+ for (var i = 0; i < Data.Length; i++)
+ {
+ if (Data[i] != 0.0)
+ {
+ inverse.Data[i] = 1.0 / Data[i];
+ }
+ else
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixNotSingular);
+ }
+ }
+
+ return inverse;
+ }
+
+ ///
+ /// Returns a new matrix containing the lower triangle of this matrix.
+ ///
+ /// The lower triangle of this matrix.
+ public override Matrix LowerTriangle()
+ {
+ return Clone();
+ }
+
+ ///
+ /// Puts the lower triangle of this matrix into the result matrix.
+ ///
+ /// Where to store the lower triangle.
+ /// If is .
+ /// If the result matrix's dimensions are not the same as this matrix.
+ public override void LowerTriangle(Matrix result)
+ {
+ if (result == null)
+ {
+ throw new ArgumentNullException("result");
+ }
+
+ if (result.RowCount != RowCount || result.ColumnCount != ColumnCount)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixDimensions, "result");
+ }
+
+ if (ReferenceEquals(this, result))
+ {
+ return;
+ }
+
+ result.Clear();
+ for (var i = 0; i < Data.Length; i++)
+ {
+ result[i, i] = Data[i];
+ }
+ }
+
+ ///
+ /// Returns a new matrix containing the lower triangle of this matrix. The new matrix
+ /// does not contain the diagonal elements of this matrix.
+ ///
+ /// The lower triangle of this matrix.
+ public override Matrix StrictlyLowerTriangle()
+ {
+ return new DiagonalMatrix(RowCount, ColumnCount);
+ }
+
+ ///
+ /// Puts the strictly lower triangle of this matrix into the result matrix.
+ ///
+ /// Where to store the lower triangle.
+ /// If is .
+ /// If the result matrix's dimensions are not the same as this matrix.
+ public override void StrictlyLowerTriangle(Matrix result)
+ {
+ if (result == null)
+ {
+ throw new ArgumentNullException("result");
+ }
+
+ if (result.RowCount != RowCount || result.ColumnCount != ColumnCount)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixDimensions, "result");
+ }
+
+ result.Clear();
+ }
+
+ ///
+ /// Returns a new matrix containing the upper triangle of this matrix.
+ ///
+ /// The upper triangle of this matrix.
+ public override Matrix UpperTriangle()
+ {
+ return Clone();
+ }
+
+ ///
+ /// Puts the upper triangle of this matrix into the result matrix.
+ ///
+ /// Where to store the lower triangle.
+ /// If is .
+ /// If the result matrix's dimensions are not the same as this matrix.
+ public override void UpperTriangle(Matrix result)
+ {
+ if (result == null)
+ {
+ throw new ArgumentNullException("result");
+ }
+
+ if (result.RowCount != RowCount || result.ColumnCount != ColumnCount)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixDimensions, "result");
+ }
+
+ result.Clear();
+ for (var i = 0; i < Data.Length; i++)
+ {
+ result[i, i] = Data[i];
+ }
+ }
+
+ ///
+ /// Returns a new matrix containing the upper triangle of this matrix. The new matrix
+ /// does not contain the diagonal elements of this matrix.
+ ///
+ /// The upper triangle of this matrix.
+ public override Matrix StrictlyUpperTriangle()
+ {
+ return new DiagonalMatrix(RowCount, ColumnCount);
+ }
+
+ ///
+ /// Puts the strictly upper triangle of this matrix into the result matrix.
+ ///
+ /// Where to store the lower triangle.
+ /// If is .
+ /// If the result matrix's dimensions are not the same as this matrix.
+ public override void StrictlyUpperTriangle(Matrix result)
+ {
+ if (result == null)
+ {
+ throw new ArgumentNullException("result");
+ }
+
+ if (result.RowCount != RowCount || result.ColumnCount != ColumnCount)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixDimensions, "result");
+ }
+
+ result.Clear();
+ }
+
+ ///
+ /// Creates a matrix that contains the values from the requested sub-matrix.
+ ///
+ /// The row to start copying from.
+ /// The number of rows to copy. Must be positive.
+ /// The column to start copying from.
+ /// The number of columns to copy. Must be positive.
+ /// The requested sub-matrix.
+ /// If: is
+ /// negative, or greater than or equal to the number of rows.
+ /// is negative, or greater than or equal to the number
+ /// of columns.
+ /// (columnIndex + columnLength) >= Columns
+ /// (rowIndex + rowLength) >= Rows
+ /// If or
+ /// is not positive.
+ public override Matrix SubMatrix(int rowIndex, int rowLength, int columnIndex, int columnLength)
+ {
+ if (rowIndex >= RowCount || rowIndex < 0)
+ {
+ throw new ArgumentOutOfRangeException("rowIndex");
+ }
+
+ if (columnIndex >= ColumnCount || columnIndex < 0)
+ {
+ throw new ArgumentOutOfRangeException("columnIndex");
+ }
+
+ if (rowLength < 1)
+ {
+ throw new ArgumentException(Resources.ArgumentMustBePositive, "rowLength");
+ }
+
+ if (columnLength < 1)
+ {
+ throw new ArgumentException(Resources.ArgumentMustBePositive, "columnLength");
+ }
+
+ var colMax = columnIndex + columnLength;
+ var rowMax = rowIndex + rowLength;
+
+ if (rowMax > RowCount)
+ {
+ throw new ArgumentOutOfRangeException("rowLength");
+ }
+
+ if (colMax > ColumnCount)
+ {
+ throw new ArgumentOutOfRangeException("columnLength");
+ }
+
+ var result = new SparseMatrix(rowLength, columnLength);
+
+ if (rowIndex > columnIndex && columnIndex + columnLength > rowIndex)
+ {
+ for (var i = 0; rowIndex - columnIndex + i < Math.Min(columnLength, rowLength); i++)
+ {
+ result[i, rowIndex - columnIndex + i] = Data[rowIndex + i];
+ }
+ }
+ else if (rowIndex < columnIndex && rowIndex + rowLength > columnIndex)
+ {
+ for (var i = 0; rowIndex - columnIndex + i < Math.Min(columnLength, rowLength); i++)
+ {
+ result[columnIndex - rowIndex + i, i] = Data[columnIndex + i];
+ }
+ }
+ else
+ {
+ for (var i = 0; i < Math.Min(columnLength, rowLength); i++)
+ {
+ result[i, i] = Data[rowIndex + i];
+ }
+ }
+
+ return result;
+ }
+
+ ///
+ /// Returns this matrix as a multidimensional array.
+ ///
+ /// A multidimensional containing the values of this matrix.
+ public override double[,] ToArray()
+ {
+ var result = new double[RowCount, ColumnCount];
+ for (var i = 0; i < Data.Length; i++)
+ {
+ result[i, i] = Data[i];
+ }
+
+ return result;
+ }
+
+ ///
+ /// Creates a new and inserts the given column at the given index.
+ ///
+ /// The index of where to insert the column.
+ /// The column to insert.
+ /// A new with the inserted column.
+ /// If is .
+ /// If is < zero or > the number of columns.
+ /// If the size of != the number of rows.
+ public override Matrix InsertColumn(int columnIndex, Vector column)
+ {
+ if (column == null)
+ {
+ throw new ArgumentNullException("column");
+ }
+
+ if (columnIndex < 0 || columnIndex > ColumnCount)
+ {
+ throw new ArgumentOutOfRangeException("columnIndex");
+ }
+
+ if (column.Count != RowCount)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixSameRowDimension, "column");
+ }
+
+ var result = new SparseMatrix(RowCount, ColumnCount + 1);
+
+ for (var i = 0; i < columnIndex; i++)
+ {
+ result.SetColumn(i, Column(i));
+ }
+
+ result.SetColumn(columnIndex, column);
+
+ for (var i = columnIndex + 1; i < ColumnCount + 1; i++)
+ {
+ result.SetColumn(i, Column(i - 1));
+ }
+
+ return result;
+ }
+
+ ///
+ /// Creates a new and inserts the given row at the given index.
+ ///
+ /// The index of where to insert the row.
+ /// The row to insert.
+ /// A new with the inserted column.
+ /// If is .
+ /// If is < zero or > the number of rows.
+ /// If the size of != the number of columns.
+ public override Matrix InsertRow(int rowIndex, Vector row)
+ {
+ if (row == null)
+ {
+ throw new ArgumentNullException("row");
+ }
+
+ if (rowIndex < 0 || rowIndex > RowCount)
+ {
+ throw new ArgumentOutOfRangeException("rowIndex");
+ }
+
+ if (row.Count != ColumnCount)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixSameRowDimension, "row");
+ }
+
+ var result = new SparseMatrix(RowCount + 1, ColumnCount);
+
+ for (var i = 0; i < rowIndex; i++)
+ {
+ result.SetRow(i, Row(i));
+ }
+
+ result.SetRow(rowIndex, row);
+
+ for (var i = rowIndex + 1; i < RowCount; i++)
+ {
+ result.SetRow(i, Row(i - 1));
+ }
+
+ return result;
+ }
+
+ ///
+ /// Stacks this matrix on top of the given matrix and places the result into the result .
+ ///
+ /// The matrix to stack this matrix upon.
+ /// The combined .
+ /// If lower is .
+ /// If upper.Columns != lower.Columns.
+ public override Matrix Stack(Matrix lower)
+ {
+ if (lower == null)
+ {
+ throw new ArgumentNullException("lower");
+ }
+
+ if (lower.ColumnCount != ColumnCount)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixSameColumnDimension, "lower");
+ }
+
+ var result = new SparseMatrix(RowCount + lower.RowCount, ColumnCount);
+ Stack(lower, result);
+ return result;
+ }
+
+ ///
+ /// Stacks this matrix on top of the given matrix and places the result into the result .
+ ///
+ /// The matrix to stack this matrix upon.
+ /// The combined .
+ /// If lower is .
+ /// If upper.Columns != lower.Columns.
+ public override void Stack(Matrix lower, Matrix result)
+ {
+ if (lower == null)
+ {
+ throw new ArgumentNullException("lower");
+ }
+
+ if (lower.ColumnCount != ColumnCount)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixSameColumnDimension, "lower");
+ }
+
+ if (result == null)
+ {
+ throw new ArgumentNullException("result");
+ }
+
+ if (result.RowCount != (RowCount + lower.RowCount) || result.ColumnCount != ColumnCount)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixDimensions, "result");
+ }
+
+ // Clear the result matrix
+ result.Clear();
+
+ // Copy the diagonal part into the result matrix.
+ for (var i = 0; i < Data.Length; i++)
+ {
+ result[i, i] = Data[i];
+ }
+
+ // Copy the lower matrix into the result matrix.
+ for (var i = 0; i < lower.RowCount; i++)
+ {
+ for (var j = 0; j < lower.ColumnCount; j++)
+ {
+ result[i + RowCount, j] = lower[i, j];
+ }
+ }
+ }
+
+ ///
+ /// Concatenates this matrix with the given matrix.
+ ///
+ /// The matrix to concatenate.
+ /// The combined .
+ public override Matrix Append(Matrix right)
+ {
+ if (right == null)
+ {
+ throw new ArgumentNullException("right");
+ }
+
+ if (right.RowCount != RowCount)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixSameRowDimension);
+ }
+
+ var result = new SparseMatrix(RowCount, ColumnCount + right.ColumnCount);
+ Append(right, result);
+ return result;
+ }
+
+ ///
+ /// Concatenates this matrix with the given matrix and places the result into the result .
+ ///
+ /// The matrix to concatenate.
+ /// The combined .
+ public override void Append(Matrix right, Matrix result)
+ {
+ if (right == null)
+ {
+ throw new ArgumentNullException("right");
+ }
+
+ if (right.RowCount != RowCount)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixSameRowDimension);
+ }
+
+ if (result == null)
+ {
+ throw new ArgumentNullException("result");
+ }
+
+ if (result.ColumnCount != (ColumnCount + right.ColumnCount) || result.RowCount != RowCount)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixSameColumnDimension);
+ }
+
+ // Clear the result matrix
+ result.Clear();
+
+ // Copy the diagonal part into the result matrix.
+ for (var i = 0; i < Data.Length; i++)
+ {
+ result[i, i] = Data[i];
+ }
+
+ // Copy the lower matrix into the result matrix.
+ for (var i = 0; i < right.RowCount; i++)
+ {
+ for (var j = 0; j < right.ColumnCount; j++)
+ {
+ result[i, j + RowCount] = right[i, j];
+ }
+ }
+ }
+
+ ///
+ /// Diagonally stacks his matrix on top of the given matrix. The new matrix is a M-by-N matrix,
+ /// where M = this.Rows + lower.Rows and N = this.Columns + lower.Columns.
+ /// The values of off the off diagonal matrices/blocks are set to zero.
+ ///
+ /// The lower, right matrix.
+ /// If lower is .
+ /// the combined matrix
+ public override Matrix DiagonalStack(Matrix lower)
+ {
+ if (lower == null)
+ {
+ throw new ArgumentNullException("lower");
+ }
+
+ var result = new SparseMatrix(RowCount + lower.RowCount, ColumnCount + lower.ColumnCount);
+ DiagonalStack(lower, result);
+ return result;
+ }
+
+ ///
+ /// Diagonally stacks his matrix on top of the given matrix and places the combined matrix into the result matrix.
+ ///
+ /// The lower, right matrix.
+ /// The combined matrix
+ /// If lower is .
+ /// If the result matrix is .
+ /// If the result matrix's dimensions are not (this.Rows + lower.rows) x (this.Columns + lower.Columns).
+ public override void DiagonalStack(Matrix lower, Matrix result)
+ {
+ if (lower == null)
+ {
+ throw new ArgumentNullException("lower");
+ }
+
+ if (result == null)
+ {
+ throw new ArgumentNullException("result");
+ }
+
+ if (result.RowCount != RowCount + lower.RowCount || result.ColumnCount != ColumnCount + lower.ColumnCount)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixDimensions, "result");
+ }
+
+ // Clear the result matrix
+ result.Clear();
+
+ // Copy the diagonal part into the result matrix.
+ for (var i = 0; i < Data.Length; i++)
+ {
+ result[i, i] = Data[i];
+ }
+
+ // Copy the lower matrix into the result matrix.
+ CommonParallel.For(0, lower.RowCount, i => CommonParallel.For(0, lower.ColumnCount, j => result.At(i + RowCount, j + ColumnCount, lower.At(i, j))));
+ }
+
+ ///
+ /// Pointwise multiplies this matrix with another matrix and stores the result into the result matrix.
+ ///
+ /// The matrix to pointwise multiply with this one.
+ /// The matrix to store the result of the pointwise multiplication.
+ /// If the other matrix is .
+ /// If the result matrix is .
+ /// If this matrix and are not the same size.
+ /// If this matrix and are not the same size.
+ public override void PointwiseMultiply(Matrix other, Matrix result)
+ {
+ if (other == null)
+ {
+ throw new ArgumentNullException("other");
+ }
+
+ if (result == null)
+ {
+ throw new ArgumentNullException("result");
+ }
+
+ if (ColumnCount != other.ColumnCount || RowCount != other.RowCount)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixDimensions, "result");
+ }
+
+ if (ColumnCount != result.ColumnCount || RowCount != result.RowCount)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixDimensions, "result");
+ }
+
+ var m = other as DiagonalMatrix;
+ var r = result as DiagonalMatrix;
+
+ if (m == null || r == null)
+ {
+ base.PointwiseMultiply(other, result);
+ }
+ else
+ {
+ Control.LinearAlgebraProvider.PointWiseMultiplyArrays(Data, m.Data, r.Data);
+ }
+ }
+
+ ///
+ /// Permute the columns of a matrix according to a permutation.
+ ///
+ /// The column permutation to apply to this matrix.
+ /// Always thrown
+ /// Permutation in diagonal matrix are senseless, because of matrix nature
+ public override void PermuteColumns(Permutation p)
+ {
+ throw new InvalidOperationException("Permutations in diagonal matrix are not allowed");
+ }
+
+ ///
+ /// Permute the rows of a matrix according to a permutation.
+ ///
+ /// The row permutation to apply to this matrix.
+ /// Always thrown
+ /// Permutation in diagonal matrix are senseless, because of matrix nature
+ public override void PermuteRows(Permutation p)
+ {
+ throw new InvalidOperationException("Permutations in diagonal matrix are not allowed");
+ }
+ #region Static constructors for special matrices.
+
+ ///
+ /// Initializes a square with all zero's except for ones on the diagonal.
+ ///
+ /// the size of the square matrix.
+ /// A diagonal identity matrix.
+ ///
+ /// If is less than one.
+ ///
+ public static DiagonalMatrix Identity(int order)
+ {
+ var m = new DiagonalMatrix(order);
+ for (var i = 0; i < order; i++)
+ {
+ m.Data[i] = 1.0;
+ }
+
+ return m;
+ }
+
+ #endregion
+ }
+}
diff --git a/src/Numerics/LinearAlgebra/Double/Factorization/DenseQR.cs b/src/Numerics/LinearAlgebra/Double/Factorization/DenseQR.cs
index e41f8755..31426538 100644
--- a/src/Numerics/LinearAlgebra/Double/Factorization/DenseQR.cs
+++ b/src/Numerics/LinearAlgebra/Double/Factorization/DenseQR.cs
@@ -50,6 +50,7 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Factorization
///
/// The matrix to factor.
/// If is null.
+ /// If row count is less then column count
public DenseQR(DenseMatrix matrix)
{
if (matrix == null)
diff --git a/src/Numerics/LinearAlgebra/Double/Factorization/ExtensionMethods.cs b/src/Numerics/LinearAlgebra/Double/Factorization/ExtensionMethods.cs
index efd621ea..3a9ccf36 100644
--- a/src/Numerics/LinearAlgebra/Double/Factorization/ExtensionMethods.cs
+++ b/src/Numerics/LinearAlgebra/Double/Factorization/ExtensionMethods.cs
@@ -65,6 +65,17 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Factorization
return Factorization.QR.Create(matrix);
}
+ ///
+ /// Computes the QR decomposition for a matrix using Modified Gram-Schmidt Orthogonalization.
+ ///
+ /// The matrix to factor.
+ /// The QR decomposition object.
+ public static GramSchmidt GramSchmidt(this Matrix matrix)
+ {
+ // NOTE: There is no factory for GramSchmidt. Use constructor of GramSchmidt class.
+ return new GramSchmidt(matrix);
+ }
+
///
/// Computes the SVD decomposition for a matrix.
///
diff --git a/src/Numerics/LinearAlgebra/Double/Factorization/GramSchmidt.cs b/src/Numerics/LinearAlgebra/Double/Factorization/GramSchmidt.cs
new file mode 100644
index 00000000..b1e306d1
--- /dev/null
+++ b/src/Numerics/LinearAlgebra/Double/Factorization/GramSchmidt.cs
@@ -0,0 +1,284 @@
+//
+// 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.
+//
+
+namespace MathNet.Numerics.LinearAlgebra.Double.Factorization
+{
+ using System;
+ using Properties;
+
+ ///
+ /// A class which encapsulates the functionality of the QR decomposition Modified Gram-Schmidt Orthogonalization.
+ /// Any real square matrix A may be decomposed as A = QR where Q is an orthogonal mxn matrix and R is an nxn upper triangular matrix.
+ ///
+ ///
+ /// The computation of the QR decomposition is done at construction time by modified Gram-Schmidt Orthogonalization.
+ ///
+ public class GramSchmidt : QR
+ {
+ ///
+ /// Initializes a new instance of the class. This object creates an orthogonal matrix
+ /// using the modified Gram-Schmidt method.
+ ///
+ /// The matrix to factor.
+ /// If is null.
+ /// If row count is less then column count
+ /// If is rank deficient
+ public GramSchmidt(Matrix 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 = MatrixQ.Column(k).DotProduct(MatrixQ.Column(j));
+ 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);
+ }
+ }
+ }
+ }
+
+ ///
+ /// Gets a value indicating whether the matrix is full rank or not.
+ ///
+ /// true if the matrix is full rank; otherwise false.
+ public override bool IsFullRank
+ {
+ get
+ {
+ return true;
+ }
+ }
+
+ ///
+ /// Gets the determinant of the matrix for which the QR matrix was computed.
+ ///
+ public override double Determinant
+ {
+ get
+ {
+ if (MatrixQ.RowCount != MatrixQ.ColumnCount)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixSquare);
+ }
+
+ var det = 1.0;
+ for (var i = 0; i < MatrixR.ColumnCount; i++)
+ {
+ det *= MatrixR.At(i, i);
+ if (Math.Abs(MatrixR.At(i, i)).AlmostEqualInDecimalPlaces(0.0, 15))
+ {
+ return 0;
+ }
+ }
+
+ return Math.Abs(det);
+ }
+ }
+
+ ///
+ /// Solves a system of linear equations, AX = B, with A QR factorized.
+ ///
+ /// The right hand side , B.
+ /// The left hand side , X.
+ public override void Solve(Matrix input, Matrix 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 double[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++)
+ {
+ double s = 0;
+ for (var k = 0; k < MatrixQ.RowCount; k++)
+ {
+ s += MatrixQ.At(k, i) * 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));
+ }
+ }
+ }
+
+ ///
+ /// Solves a system of linear equations, Ax = b, with A QR factorized.
+ ///
+ /// The right hand side vector, b.
+ /// The left hand side , x.
+ public override void Solve(Vector input, Vector 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 double[MatrixQ.RowCount];
+ for (var k = 0; k < MatrixQ.RowCount; k++)
+ {
+ column[k] = inputCopy[k];
+ }
+
+ for (var i = 0; i < MatrixQ.ColumnCount; i++)
+ {
+ double s = 0;
+ for (var k = 0; k < MatrixQ.RowCount; k++)
+ {
+ s += MatrixQ.At(k, i) * 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];
+ }
+ }
+ }
+}
diff --git a/src/Numerics/LinearAlgebra/Double/Factorization/SparseCholesky.cs b/src/Numerics/LinearAlgebra/Double/Factorization/SparseCholesky.cs
new file mode 100644
index 00000000..c0978f59
--- /dev/null
+++ b/src/Numerics/LinearAlgebra/Double/Factorization/SparseCholesky.cs
@@ -0,0 +1,223 @@
+//
+// 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.
+//
+
+namespace MathNet.Numerics.LinearAlgebra.Double.Factorization
+{
+ using System;
+ using Properties;
+
+ ///
+ /// A class which encapsulates the functionality of a Cholesky factorization for soarse matrices.
+ /// For a symmetric, positive definite matrix A, the Cholesky factorization
+ /// is an lower triangular matrix L so that A = L*L'.
+ ///
+ ///
+ /// 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.
+ ///
+ public class SparseCholesky : Cholesky
+ {
+ ///
+ /// Initializes a new instance of the class. This object will compute the
+ /// Cholesky factorization when the constructor is called and cache it's factorization.
+ ///
+ /// The matrix to factor.
+ /// If is null.
+ /// If is not a square matrix.
+ /// If is not positive definite.
+ public SparseCholesky(Matrix 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 j = 0; j < CholeskyFactor.RowCount; j++)
+ {
+ var d = 0.0;
+ for (var k = 0; k < j; k++)
+ {
+ var s = 0.0;
+ for (var i = 0; i < k; i++)
+ {
+ s += CholeskyFactor.At(k, i) * CholeskyFactor.At(j, i);
+ }
+
+ s = (matrix.At(j, k) - s) / CholeskyFactor.At(k, k);
+ CholeskyFactor.At(j, k, s);
+ d += s * s;
+ }
+
+ d = matrix.At(j, j) - d;
+ if (d <= 0.0)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixPositiveDefinite);
+ }
+
+ CholeskyFactor.At(j, j, Math.Sqrt(d));
+ for (var k = j + 1; k < CholeskyFactor.RowCount; k++)
+ {
+ CholeskyFactor.At(j, k, 0.0);
+ }
+ }
+ }
+
+
+ ///
+ /// Solves a system of linear equations, AX = B, with A Cholesky factorized.
+ ///
+ /// The right hand side , B.
+ /// The left hand side , X.
+ public override void Solve(Matrix input, Matrix 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;
+ double 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) * result.At(k, c);
+ }
+
+ result.At(i, c, sum / CholeskyFactor.At(i, i));
+ }
+ }
+ }
+
+ ///
+ /// Solves a system of linear equations, Ax = b, with A Cholesky factorized.
+ ///
+ /// The right hand side vector, b.
+ /// The left hand side , x.
+ public override void Solve(Vector input, Vector 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;
+ double 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) * result[k];
+ }
+
+ result[i] = sum / CholeskyFactor.At(i, i);
+ }
+ }
+ }
+}
diff --git a/src/Numerics/LinearAlgebra/Double/Factorization/SparseLU.cs b/src/Numerics/LinearAlgebra/Double/Factorization/SparseLU.cs
new file mode 100644
index 00000000..485e8e3d
--- /dev/null
+++ b/src/Numerics/LinearAlgebra/Double/Factorization/SparseLU.cs
@@ -0,0 +1,300 @@
+//
+// 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.
+//
+
+namespace MathNet.Numerics.LinearAlgebra.Double.Factorization
+{
+ using System;
+ using Properties;
+
+ ///
+ /// A class which encapsulates the functionality of an LU factorization.
+ /// 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.
+ ///
+ ///
+ /// The computation of the LU factorization is done at construction time.
+ ///
+ public class SparseLU : LU
+ {
+ ///
+ /// Initializes a new instance of the class. This object will compute the
+ /// LU factorization when the constructor is called and cache it's factorization.
+ ///
+ /// The matrix to factor.
+ /// If is null.
+ /// If is not a square matrix.
+ public SparseLU(Matrix 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 double[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 = 0.0;
+ 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 (Math.Abs(vectorLUcolj[i]) > Math.Abs(vectorLUcolj[p]))
+ {
+ 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)));
+ }
+ }
+ }
+ }
+
+ ///
+ /// Solves a system of linear equations, AX = B, with A LU factorized.
+ ///
+ /// The right hand side , B.
+ /// The left hand side , X.
+ public override void Solve(Matrix input, Matrix 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);
+ }
+ }
+ }
+ }
+
+ ///
+ /// Solves a system of linear equations, Ax = b, with A LU factorized.
+ ///
+ /// The right hand side vector, b.
+ /// The left hand side , x.
+ public override void Solve(Vector input, Vector 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);
+ }
+ }
+ }
+
+ ///
+ /// Returns the inverse of this matrix. The inverse is calculated using LU decomposition.
+ ///
+ /// The inverse of this matrix.
+ public override Matrix 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);
+ }
+ }
+}
diff --git a/src/Numerics/LinearAlgebra/Double/Factorization/SparseQR.cs b/src/Numerics/LinearAlgebra/Double/Factorization/SparseQR.cs
new file mode 100644
index 00000000..a7e419be
--- /dev/null
+++ b/src/Numerics/LinearAlgebra/Double/Factorization/SparseQR.cs
@@ -0,0 +1,332 @@
+//
+// 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.
+//
+
+namespace MathNet.Numerics.LinearAlgebra.Double.Factorization
+{
+ using System;
+ using System.Linq;
+ using Properties;
+
+ ///
+ /// A class which encapsulates the functionality of the QR decomposition.
+ /// 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).
+ ///
+ ///
+ /// The computation of the QR decomposition is done at construction time by Householder transformation.
+ ///
+ public class SparseQR : QR
+ {
+ ///
+ /// Initializes a new instance of the class. This object will compute the
+ /// QR factorization when the constructor is called and cache it's factorization.
+ ///
+ /// The matrix to factor.
+ /// If is null.
+ public SparseQR(Matrix 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 double[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);
+ }
+ }
+
+ ///
+ /// Generate column from initial matrix to work array
+ ///
+ /// Initial matrix
+ /// The firts row
+ /// The last row
+ /// Column index
+ /// Generated vector
+ private static double[] GenerateColumn(Matrix a, int rowStart, int rowEnd, int column)
+ {
+ var ru = rowEnd - rowStart + 1;
+ var u = new double[ru];
+
+ for (var i = rowStart; i <= rowEnd; i++)
+ {
+ u[i - rowStart] = a.At(i, rowStart);
+ a.At(i, rowStart, 0.0);
+ }
+
+ var norm = u.Sum(t => t * t);
+ norm = Math.Sqrt(norm);
+
+ if (rowStart == rowEnd || norm == 0)
+ {
+ a.At(rowStart, column, -u[0]);
+ u[0] = Math.Sqrt(2.0);
+ return u;
+ }
+
+ var scale = 1.0 / norm;
+ if (u[0] < 0.0)
+ {
+ scale *= -1.0;
+ }
+
+ a.At(rowStart, column, -1.0 / scale);
+
+ for (var i = 0; i < ru; i++)
+ {
+ u[i] *= scale;
+ }
+
+ u[0] += 1.0;
+ var s = Math.Sqrt(1.0 / u[0]);
+
+ for (var i = 0; i < ru; i++)
+ {
+ u[i] *= s;
+ }
+
+ return u;
+ }
+
+ ///
+ /// Perform calculation of Q or R
+ ///
+ /// Work array
+ /// Q or R matrices
+ /// The first row
+ /// The last row
+ /// The first column
+ /// The last column
+ private static void ComputeQR(double[] u, Matrix a, int rowStart, int rowEnd, int columnStart, int columnEnd)
+ {
+ if (rowEnd < rowStart || columnEnd < columnStart)
+ {
+ return;
+ }
+
+ var v = new double[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] * v[j - columnStart]));
+ }
+ }
+ }
+
+ ///
+ /// Solves a system of linear equations, AX = B, with A QR factorized.
+ ///
+ /// The right hand side , B.
+ /// The left hand side , X.
+ public override void Solve(Matrix input, Matrix 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 bn = inputCopy.ColumnCount;
+ var column = new double[MatrixR.RowCount];
+ for (var j = 0; j < bn; j++)
+ {
+ for (var k = 0; k < MatrixR.RowCount; k++)
+ {
+ column[k] = inputCopy.At(k, j);
+ }
+
+ for (var i = 0; i < MatrixR.RowCount; i++)
+ {
+ double s = 0;
+ for (var k = 0; k < MatrixR.RowCount; k++)
+ {
+ s += MatrixQ.At(k, i) * 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 < bn; 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 < bn; 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));
+ }
+ }
+ }
+
+ ///
+ /// Solves a system of linear equations, Ax = b, with A QR factorized.
+ ///
+ /// The right hand side vector, b.
+ /// The left hand side , x.
+ public override void Solve(Vector input, Vector 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 double[MatrixR.RowCount];
+ for (var k = 0; k < MatrixR.RowCount; k++)
+ {
+ column[k] = inputCopy[k];
+ }
+
+ for (var i = 0; i < MatrixR.RowCount; i++)
+ {
+ double s = 0;
+ for (var k = 0; k < MatrixR.RowCount; k++)
+ {
+ s += MatrixQ.At(k, i) * 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];
+ }
+ }
+ }
+}
diff --git a/src/Numerics/LinearAlgebra/Double/Factorization/SparseSvd.cs b/src/Numerics/LinearAlgebra/Double/Factorization/SparseSvd.cs
new file mode 100644
index 00000000..35df929e
--- /dev/null
+++ b/src/Numerics/LinearAlgebra/Double/Factorization/SparseSvd.cs
@@ -0,0 +1,923 @@
+//
+// 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.
+//
+namespace MathNet.Numerics.LinearAlgebra.Double.Factorization
+{
+ using System;
+ using Properties;
+
+ ///
+ /// A class which encapsulates the functionality of the singular value decomposition (SVD) for .
+ /// 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.
+ ///
+ ///
+ /// The computation of the singular value decomposition is done at construction time.
+ ///
+ public class SparseSvd : Svd
+ {
+ ///
+ /// Initializes a new instance of the class. This object will compute the
+ /// the singular value decomposition when the constructor is called and cache it's decomposition.
+ ///
+ /// The matrix to factor.
+ /// Compute the singular U and VT vectors or not.
+ /// If is null.
+ /// If SVD algorithm failed to converge with matrix .
+ public SparseSvd(Matrix 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 double[matrixCopy.ColumnCount];
+ var work = new double[matrixCopy.RowCount];
+
+ int i, j;
+ int l, lp1;
+ var cs = 0.0;
+ var sn = 0.0;
+ double 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].
+ var xnorm = Dnrm2Column(matrixCopy, matrixCopy.RowCount, l, l);
+ VectorS[l] = xnorm;
+ if (VectorS[l] != 0.0)
+ {
+ if (matrixCopy.At(l, l) != 0.0)
+ {
+ VectorS[l] = Dsign(VectorS[l], matrixCopy.At(l, l));
+ }
+
+ DscalColumn(matrixCopy, matrixCopy.RowCount, l, l, 1.0 / VectorS[l]);
+ matrixCopy.At(l, l, (1.0 + matrixCopy.At(l, l)));
+ }
+
+ VectorS[l] = -VectorS[l];
+ }
+
+ for (j = lp1; j < matrixCopy.ColumnCount; j++)
+ {
+ if (l < nct)
+ {
+ if (VectorS[l] != 0.0)
+ {
+ // Apply the transformation.
+ t = -Ddot(matrixCopy, matrixCopy.RowCount, l, j, l) / matrixCopy.At(l, l);
+ 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);
+ }
+
+ 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 = Dnrm2Vector(e, lp1);
+ e[l] = enorm;
+ if (e[l] != 0.0)
+ {
+ if (e[lp1] != 0.0)
+ {
+ e[l] = Dsign(e[l], e[lp1]);
+ }
+
+ DscalVector(e, lp1, 1.0 / e[l]);
+ e[lp1] = 1.0 + e[lp1];
+ }
+
+ e[l] = -e[l];
+ if (lp1 < matrixCopy.RowCount && e[l] != 0.0)
+ {
+ // Apply the transformation.
+ for (i = lp1; i < matrixCopy.RowCount; i++)
+ {
+ work[i] = 0.0;
+ }
+
+ for (j = lp1; j < matrixCopy.ColumnCount; j++)
+ {
+ 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];
+ 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] = 0.0;
+ }
+
+ if (nrtp1 < m)
+ {
+ e[nrtp1 - 1] = matrixCopy.At((nrtp1 - 1), (m - 1));
+ }
+
+ e[m - 1] = 0.0;
+
+ // If required, generate u.
+ if (ComputeVectors)
+ {
+ for (j = nctp1 - 1; j < ncu; j++)
+ {
+ for (i = 0; i < matrixCopy.RowCount; i++)
+ {
+ MatrixU.At(i, j, 0.0);
+ }
+
+ MatrixU.At(j, j, 1.0);
+ }
+
+ for (l = nct - 1; l >= 0; l--)
+ {
+ if (VectorS[l] != 0.0)
+ {
+ for (j = l + 1; j < ncu; j++)
+ {
+ t = -Ddot(MatrixU, matrixCopy.RowCount, l, j, l) / MatrixU.At(l, l);
+ for (var ii = l; ii < matrixCopy.RowCount; ii++)
+ {
+ MatrixU.At(ii, j, MatrixU.At(ii, j) + (t * MatrixU.At(ii, l)));
+ }
+ }
+
+ DscalColumn(MatrixU, matrixCopy.RowCount, l, l, -1.0);
+ MatrixU.At(l, l, 1.0 + MatrixU.At(l, l));
+ for (i = 0; i < l; i++)
+ {
+ MatrixU.At(i, l, 0.0);
+ }
+ }
+ else
+ {
+ for (i = 0; i < matrixCopy.RowCount; i++)
+ {
+ MatrixU.At(i, l, 0.0);
+ }
+
+ MatrixU.At(l, l, 1.0);
+ }
+ }
+ }
+
+ // 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] != 0.0)
+ {
+ for (j = lp1; j < matrixCopy.ColumnCount; j++)
+ {
+ t = -Ddot(MatrixVT, matrixCopy.ColumnCount, l, j, lp1) / MatrixVT.At(lp1, l);
+ 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, 0.0);
+ }
+
+ MatrixVT.At(l, l, 1.0);
+ }
+ }
+
+ // Transform s and e so that they are double .
+ for (i = 0; i < m; i++)
+ {
+ double r;
+ if (VectorS[i] != 0.0)
+ {
+ t = VectorS[i];
+ r = VectorS[i] / t;
+ VectorS[i] = t;
+ if (i < m - 1)
+ {
+ e[i] = e[i] / r;
+ }
+
+ if (ComputeVectors)
+ {
+ DscalColumn(MatrixU, matrixCopy.RowCount, i, 0, r);
+ }
+ }
+
+ // Exit
+ if (i == m - 1)
+ {
+ break;
+ }
+
+ if (e[i] != 0.0)
+ {
+ t = e[i];
+ r = t / e[i];
+ e[i] = t;
+ VectorS[i + 1] = VectorS[i + 1] * r;
+ if (ComputeVectors)
+ {
+ DscalColumn(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 = Math.Abs(VectorS[l]) + Math.Abs(VectorS[l + 1]);
+ ztest = test + Math.Abs(e[l]);
+ if (ztest.AlmostEqualInDecimalPlaces(test, 15))
+ {
+ e[l] = 0.0;
+ 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 + Math.Abs(e[ls]);
+ }
+
+ if (ls != l + 1)
+ {
+ test = test + Math.Abs(e[ls - 1]);
+ }
+
+ ztest = test + Math.Abs(VectorS[ls]);
+ if (ztest.AlmostEqualInDecimalPlaces(test, 15))
+ {
+ VectorS[ls] = 0.0;
+ 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];
+ e[m - 2] = 0.0;
+ double t1;
+ for (var kk = l; kk < m - 1; kk++)
+ {
+ k = m - 2 - kk + l;
+ t1 = VectorS[k];
+ Drotg(ref t1, ref f, ref cs, ref sn);
+ VectorS[k] = t1;
+ if (k != l)
+ {
+ f = -sn * e[k - 1];
+ e[k - 1] = cs * e[k - 1];
+ }
+
+ if (ComputeVectors)
+ {
+ Drot(MatrixVT, matrixCopy.ColumnCount, k, m - 1, cs, sn);
+ }
+ }
+
+ break;
+
+ // Split at negligible VectorS[l].
+ case 2:
+ f = e[l - 1];
+ e[l - 1] = 0.0;
+ for (k = l; k < m; k++)
+ {
+ t1 = VectorS[k];
+ Drotg(ref t1, ref f, ref cs, ref sn);
+ VectorS[k] = t1;
+ f = -sn * e[k];
+ e[k] = cs * e[k];
+ if (ComputeVectors)
+ {
+ Drot(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, Math.Abs(VectorS[m - 1]));
+ scale = Math.Max(scale, Math.Abs(VectorS[m - 2]));
+ scale = Math.Max(scale, Math.Abs(e[m - 2]));
+ scale = Math.Max(scale, Math.Abs(VectorS[l]));
+ scale = Math.Max(scale, Math.Abs(e[l]));
+ var sm = VectorS[m - 1] / scale;
+ var smm1 = VectorS[m - 2] / scale;
+ var emm1 = e[m - 2] / scale;
+ var sl = VectorS[l] / scale;
+ var el = e[l] / 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++)
+ {
+ Drotg(ref f, ref g, ref cs, ref sn);
+ if (k != l)
+ {
+ e[k - 1] = f;
+ }
+
+ f = (cs * VectorS[k]) + (sn * e[k]);
+ e[k] = (cs * e[k]) - (sn * VectorS[k]);
+ g = sn * VectorS[k + 1];
+ VectorS[k + 1] = cs * VectorS[k + 1];
+ if (ComputeVectors)
+ {
+ Drot(MatrixVT, matrixCopy.ColumnCount, k, k + 1, cs, sn);
+ }
+
+ Drotg(ref f, ref g, ref cs, ref sn);
+ VectorS[k] = f;
+ f = (cs * e[k]) + (sn * VectorS[k + 1]);
+ VectorS[k + 1] = (-sn * e[k]) + (cs * VectorS[k + 1]);
+ g = sn * e[k + 1];
+ e[k + 1] = cs * e[k + 1];
+ if (ComputeVectors && k < matrixCopy.RowCount)
+ {
+ Drot(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] < 0.0)
+ {
+ VectorS[l] = -VectorS[l];
+ if (ComputeVectors)
+ {
+ DscalColumn(MatrixVT, matrixCopy.ColumnCount, l, 0, -1.0);
+ }
+ }
+
+ // Order the singular value.
+ while (l != mn - 1)
+ {
+ if (VectorS[l] >= VectorS[l + 1])
+ {
+ break;
+ }
+
+ t = VectorS[l];
+ VectorS[l] = VectorS[l + 1];
+ VectorS[l + 1] = t;
+ if (ComputeVectors && l < matrixCopy.ColumnCount)
+ {
+ Dswap(MatrixVT, matrixCopy.ColumnCount, l, l + 1);
+ }
+
+ if (ComputeVectors && l < matrixCopy.RowCount)
+ {
+ Dswap(MatrixU, matrixCopy.RowCount, l, l + 1);
+ }
+
+ l = l + 1;
+ }
+
+ iter = 0;
+ m = m - 1;
+ break;
+ }
+ }
+
+ if (ComputeVectors)
+ {
+ MatrixVT = MatrixVT.Transpose();
+ }
+
+ // 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;
+ }
+ }
+
+ ///
+ /// Calculates absolute value of multiplied on signum function of
+ ///
+ /// Double value z1
+ /// Double value z2
+ /// Result multiplication of signum function and absolute value
+ private static double Dsign(double z1, double z2)
+ {
+ return Math.Abs(z1) * (z2 / Math.Abs(z2));
+ }
+
+ ///
+ /// Swap column and
+ ///
+ /// Source matrix
+ /// The number of rows in
+ /// Column A index to swap
+ /// Column B index to swap
+ private static void Dswap(Matrix 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);
+ }
+ }
+
+ ///
+ /// Scale column by starting from row
+ ///
+ /// Source matrix
+ /// The number of rows in
+ /// Column to scale
+ /// Row to scale from
+ /// Scale value
+ private static void DscalColumn(Matrix a, int rowCount, int column, int rowStart, double z)
+ {
+ for (var i = rowStart; i < rowCount; i++)
+ {
+ a.At(i, column, a.At(i, column) * z);
+ }
+ }
+
+ ///
+ /// Scale vector by starting from index
+ ///
+ /// Source vector
+ /// Row to scale from
+ /// Scale value
+ private static void DscalVector(double[] a, int start, double z)
+ {
+ for (var i = start; i < a.Length; i++)
+ {
+ a[i] = a[i] * z;
+ }
+ }
+
+ ///
+ /// 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.
+ ///
+ /// Provides the x-coordinate of the point p. On exit contains the parameter r associated with the Givens rotation
+ /// Provides the y-coordinate of the point p. On exit contains the parameter z associated with the Givens rotation
+ /// Contains the parameter c associated with the Givens rotation
+ /// Contains the parameter s associated with the Givens rotation
+ /// This is equivalent to the DROTG LAPACK routine.
+ private static void Drotg(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;
+ }
+
+ /// dded
+ /// Calculate Norm 2 of the column in matrix starting from row
+ ///
+ /// Source matrix
+ /// The number of rows in
+ /// Column index
+ /// Start row index
+ /// Norm2 (Euclidean norm) of trhe column
+ private static double Dnrm2Column(Matrix a, int rowCount, int column, int rowStart)
+ {
+ double s = 0;
+ for (var i = rowStart; i < rowCount; i++)
+ {
+ s += a.At(i, column) * a.At(i, column);
+ }
+
+ return Math.Sqrt(s);
+ }
+
+ ///
+ /// Calculate Norm 2 of the vector starting from index
+ ///
+ /// Source vector
+ /// Start index
+ /// Norm2 (Euclidean norm) of the vector
+ private static double Dnrm2Vector(double[] a, int rowStart)
+ {
+ double s = 0;
+ for (var i = rowStart; i < a.Length; i++)
+ {
+ s += a[i] * a[i];
+ }
+
+ return Math.Sqrt(s);
+ }
+
+ ///
+ /// Calculate dot product of and
+ ///
+ /// Source matrix
+ /// The number of rows in
+ /// Index of column A
+ /// Index of column B
+ /// Starting row index
+ /// Dot product value
+ private static double Ddot(Matrix a, int rowCount, int columnA, int columnB, int rowStart)
+ {
+ var z = 0.0;
+ for (var i = rowStart; i < rowCount; i++)
+ {
+ z += a.At(i, columnB) * a.At(i, columnA);
+ }
+
+ return z;
+ }
+
+ ///
+ /// Performs rotation of points in the plane. Given two vectors x and y ,
+ /// 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)
+ ///
+ /// Source matrix
+ /// The number of rows in
+ /// Index of column A
+ /// Index of column B
+ /// Scalar "c" value
+ /// Scalar "s" value
+ private static void Drot(Matrix 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);
+ }
+ }
+
+ ///
+ /// Solves a system of linear equations, AX = B, with A SVD factorized.
+ ///
+ /// The right hand side , B.
+ /// The left hand side , X.
+ public override void Solve(Matrix input, Matrix 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 double[MatrixVT.ColumnCount];
+
+ for (var k = 0; k < bn; k++)
+ {
+ for (var j = 0; j < MatrixVT.ColumnCount; j++)
+ {
+ double value = 0;
+ if (j < mn)
+ {
+ for (var i = 0; i < MatrixU.RowCount; i++)
+ {
+ value += MatrixU.At(i, j) * input.At(i, k);
+ }
+
+ value /= VectorS[j];
+ }
+
+ tmp[j] = value;
+ }
+
+ for (var j = 0; j < MatrixVT.ColumnCount; j++)
+ {
+ double value = 0;
+ for (var i = 0; i < MatrixVT.ColumnCount; i++)
+ {
+ value += MatrixVT.At(i, j) * tmp[i];
+ }
+
+ result[j, k] = value;
+ }
+ }
+ }
+
+ ///
+ /// Solves a system of linear equations, Ax = b, with A SVD factorized.
+ ///
+ /// The right hand side vector, b.
+ /// The left hand side , x.
+ public override void Solve(Vector input, Vector 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 double[MatrixVT.ColumnCount];
+ double value;
+ for (var j = 0; j < MatrixVT.ColumnCount; j++)
+ {
+ value = 0;
+ if (j < mn)
+ {
+ for (var i = 0; i < MatrixU.RowCount; i++)
+ {
+ value += MatrixU.At(i, j) * input[i];
+ }
+
+ value /= VectorS[j];
+ }
+
+ tmp[j] = value;
+ }
+
+ for (var j = 0; j < MatrixVT.ColumnCount; j++)
+ {
+ value = 0;
+ for (int i = 0; i < MatrixVT.ColumnCount; i++)
+ {
+ value += MatrixVT.At(i, j) * tmp[i];
+ }
+
+ result[j] = value;
+ }
+ }
+ }
+}
diff --git a/src/Numerics/LinearAlgebra/Double/Factorization/UserQR.cs b/src/Numerics/LinearAlgebra/Double/Factorization/UserQR.cs
index 2595fadb..01581f91 100644
--- a/src/Numerics/LinearAlgebra/Double/Factorization/UserQR.cs
+++ b/src/Numerics/LinearAlgebra/Double/Factorization/UserQR.cs
@@ -216,9 +216,8 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Factorization
var inputCopy = input.Clone();
// Compute Y = transpose(Q)*B
- var bn = inputCopy.ColumnCount;
var column = new double[MatrixR.RowCount];
- for (var j = 0; j < bn; j++)
+ for (var j = 0; j < input.ColumnCount; j++)
{
for (var k = 0; k < MatrixR.RowCount; k++)
{
@@ -240,14 +239,14 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Factorization
// Solve R*X = Y;
for (var k = MatrixR.ColumnCount - 1; k >= 0; k--)
{
- for (var j = 0; j < bn; j++)
+ 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 < bn; j++)
+ for (var j = 0; j < input.ColumnCount; j++)
{
inputCopy.At(i, j, inputCopy.At(i, j) - (inputCopy.At(k, j) * MatrixR.At(i, k)));
}
diff --git a/src/Numerics/LinearAlgebra/Double/Matrix.Arithmetic.cs b/src/Numerics/LinearAlgebra/Double/Matrix.Arithmetic.cs
index d67e700d..7aaec76c 100644
--- a/src/Numerics/LinearAlgebra/Double/Matrix.Arithmetic.cs
+++ b/src/Numerics/LinearAlgebra/Double/Matrix.Arithmetic.cs
@@ -163,7 +163,7 @@ namespace MathNet.Numerics.LinearAlgebra.Double
}
///
- /// Multiplies this matrix with a vector and places the results into the result matrix.
+ /// Multiplies this matrix with a vector and places the results into the result vactor.
///
/// The vector to multiply with.
/// The result of the multiplication.
@@ -368,6 +368,88 @@ namespace MathNet.Numerics.LinearAlgebra.Double
return result;
}
+ ///
+ /// Multiplies this matrix with transpose of another matrix and places the results into the result matrix.
+ ///
+ /// The matrix to multiply with.
+ /// The result of the multiplication.
+ /// If the other matrix is .
+ /// If the result matrix is .
+ /// If this.Columns != other.ColumnCount.
+ /// If the result matrix's dimensions are not the this.RowCount x other.RowCount.
+ public virtual void TransposeAndMultiply(Matrix other, Matrix result)
+ {
+ if (other == null)
+ {
+ throw new ArgumentNullException("other");
+ }
+
+ if (result == null)
+ {
+ throw new ArgumentNullException("result");
+ }
+
+ if (ColumnCount != other.ColumnCount)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixDimensions);
+ }
+
+ if ((result.RowCount != RowCount) || (result.ColumnCount != other.RowCount))
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixDimensions);
+ }
+
+ if (ReferenceEquals(this, result) || ReferenceEquals(other, result))
+ {
+ var tmp = result.CreateMatrix(result.RowCount, result.ColumnCount);
+ TransposeAndMultiply(other, tmp);
+ tmp.CopyTo(result);
+ }
+ else
+ {
+ CommonParallel.For(
+ 0,
+ RowCount,
+ j =>
+ {
+ for (var i = 0; i < RowCount; i++)
+ {
+ double s = 0;
+ for (var l = 0; l < ColumnCount; l++)
+ {
+ s += At(i, l) * other.At(j, l);
+ }
+
+ result.At(i, j, s + result.At(i, j));
+ }
+ });
+ }
+ }
+
+ ///
+ /// Multiplies this matrix with transpose of another matrix and returns the result.
+ ///
+ /// The matrix to multiply with.
+ /// If this.Columns != other.ColumnCount.
+ /// If the other matrix is .
+ /// The result of the multiplication.
+ public virtual Matrix TransposeAndMultiply(Matrix other)
+ {
+ if (other == null)
+ {
+ throw new ArgumentNullException("other");
+ }
+
+ if (ColumnCount != other.ColumnCount)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixDimensions);
+ }
+
+ var result = CreateMatrix(RowCount, other.RowCount);
+ TransposeAndMultiply(other, result);
+ return result;
+ }
+
///
/// Negates each element of this matrix.
///
@@ -971,18 +1053,33 @@ namespace MathNet.Numerics.LinearAlgebra.Double
}
var ret = Clone();
+
+ // BUG: Seems that commented-out implementation is wrong.
+ // CommonParallel.For(
+ // 0,
+ // ColumnCount,
+ // j =>
+ // {
+ // var rowj = Row(j);
+ // var norm = rowj.Norm(p);
+ // for (var i = 0; i < RowCount; i++)
+ // {
+ // ret[i, j] = rowj[j] / norm;
+ // }
+ // });
CommonParallel.For(
- 0,
- ColumnCount,
- j =>
+ 0,
+ RowCount,
+ i =>
{
- var rowj = Row(j);
- var norm = rowj.Norm(p);
- for (var i = 0; i < RowCount; i++)
+ var rowi = Row(i);
+ var norm = rowi.Norm(p);
+ for (var j = 0; j < ColumnCount; j++)
{
- ret[i, j] = rowj[j] / norm;
+ ret[i, j] = rowi[j] / norm;
}
});
+
return ret;
}
}
diff --git a/src/Numerics/LinearAlgebra/Double/Solvers/IIterativeSolver.cs b/src/Numerics/LinearAlgebra/Double/Solvers/IIterativeSolver.cs
new file mode 100644
index 00000000..af2573f4
--- /dev/null
+++ b/src/Numerics/LinearAlgebra/Double/Solvers/IIterativeSolver.cs
@@ -0,0 +1,96 @@
+//
+// 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.
+//
+
+namespace MathNet.Numerics.LinearAlgebra.Double.Solvers
+{
+ using Status;
+
+ ///
+ /// Defines the interface for classes that solve the matrix equation Ax = b in
+ /// an iterative manner.
+ ///
+ public interface IIterativeSolver
+ {
+ ///
+ /// Stops the solve process.
+ ///
+ ///
+ /// Note that it may take an indetermined amount of time for the solver to actually stop the process.
+ ///
+ void StopSolve();
+
+ ///
+ /// Sets the that will be used to track the iterative process.
+ ///
+ /// The iterator.
+ void SetIterator(IIterator iterator);
+
+ ///
+ /// Gets the status of the iteration once the calculation is finished.
+ ///
+ ICalculationStatus IterationResult { get; }
+
+ ///
+ /// Solves the matrix equation Ax = b, where A is the coefficient matrix, b is the
+ /// solution vector and x is the unknown vector.
+ ///
+ /// The coefficient matrix, A.
+ /// The solution vector, b.
+ /// The result vector, x.
+ Vector Solve(Matrix matrix, Vector vector);
+
+ ///
+ /// Solves the matrix equation Ax = b, where A is the coefficient matrix, b is the
+ /// solution vector and x is the unknown vector.
+ ///
+ /// The coefficient matrix, A.
+ /// The solution vector, b
+ /// The result vector, x
+ void Solve(Matrix matrix, Vector input, Vector result);
+
+ ///
+ /// Solves the matrix equation AX = B, where A is the coefficient matrix, B is the
+ /// solution matrix and X is the unknown matrix.
+ ///
+ /// The coefficient matrix, A.
+ /// The solution matrix, B.
+ /// The result matrix, X.
+ Matrix Solve(Matrix matrix, Matrix input);
+
+ ///
+ /// Solves the matrix equation AX = B, where A is the coefficient matrix, B is the
+ /// solution matrix and X is the unknown matrix.
+ ///
+ /// The coefficient matrix, A.
+ /// The solution matrix, B.
+ /// The result matrix, X
+ void Solve(Matrix matrix, Matrix input, Matrix result);
+ }
+}
diff --git a/src/Numerics/LinearAlgebra/Double/Solvers/IIterativeSolverSetup.cs b/src/Numerics/LinearAlgebra/Double/Solvers/IIterativeSolverSetup.cs
new file mode 100644
index 00000000..86ec59f4
--- /dev/null
+++ b/src/Numerics/LinearAlgebra/Double/Solvers/IIterativeSolverSetup.cs
@@ -0,0 +1,71 @@
+//
+// 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.
+//
+
+namespace MathNet.Numerics.LinearAlgebra.Double.Solvers
+{
+ using System;
+
+ ///
+ /// Defines the interface for objects that can create an iterative solver with
+ /// specific settings. This interface is used to pass iterative solver creation
+ /// setup information around.
+ ///
+ public interface IIterativeSolverSetup
+ {
+ ///
+ /// Gets the type of the solver that will be created by this setup object.
+ ///
+ Type SolverType { get; }
+
+ ///
+ /// Gets type of preconditioner, if any, that will be created by this setup object.
+ ///
+ Type PreconditionerType { get; }
+
+ ///
+ /// Creates a fully functional iterative solver with the default settings
+ /// given by this setup.
+ ///
+ /// A new .
+ IIterativeSolver CreateNew();
+
+ ///
+ /// Gets the relative speed of the solver.
+ ///
+ /// Returns a value between 0 and 1, inclusive.
+ double SolutionSpeed { get; }
+
+ ///
+ /// Gets the relative reliability of the solver.
+ ///
+ /// Returns a value between 0 and 1 inclusive.
+ double Reliability { get; }
+ }
+}
\ No newline at end of file
diff --git a/src/Numerics/LinearAlgebra/Double/Solvers/IIterator.cs b/src/Numerics/LinearAlgebra/Double/Solvers/IIterator.cs
new file mode 100644
index 00000000..4ccfaa3e
--- /dev/null
+++ b/src/Numerics/LinearAlgebra/Double/Solvers/IIterator.cs
@@ -0,0 +1,108 @@
+//
+// 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.
+//
+
+namespace MathNet.Numerics.LinearAlgebra.Double.Solvers
+{
+ using System;
+ using Status;
+ using StopCriterium;
+
+ ///
+ /// Defines the base interface for iterators that help control an iterative calculation.
+ ///
+ public interface IIterator
+#if !SILVERLIGHT
+ : ICloneable
+#endif
+ {
+ ///
+ /// Adds an to the internal collection of stop-criteria. Only a
+ /// single stop criterium of each type can be stored.
+ ///
+ /// The stop criterium to add.
+ /// Thrown if is .
+ /// Thrown if is of the same type as an already stored criterium.
+ void Add(IIterationStopCriterium stopCriterium);
+
+ ///
+ /// Removes the from the internal collection.
+ ///
+ /// The stop criterium that must be removed.
+ void Remove(IIterationStopCriterium stopCriterium);
+
+ ///
+ /// Indicates if the specific stop criterium is stored by the .
+ ///
+ /// The stop criterium.
+ /// true if the contains the stop criterium; otherwise false.
+ bool Contains(IIterationStopCriterium stopCriterium);
+
+ ///
+ /// Indicates to the iterator that the iterative process has been cancelled.
+ ///
+ /// Does not reset the stop-criteria.
+ void IterationCancelled();
+
+ ///
+ /// Determines the status of the iterative calculation based on the stop criteria stored
+ /// by the current . Status is set to Status field of current object.
+ ///
+ /// The number of iterations that have passed so far.
+ /// The vector containing the current solution values.
+ /// The right hand side vector.
+ /// The vector containing the current residual vectors.
+ ///
+ /// 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.
+ ///
+ void DetermineStatus(int iterationNumber, Vector solutionVector, Vector sourceVector, Vector residualVector);
+
+ ///
+ /// Gets the current calculation status.
+ ///
+ /// is not a legal value. Status should be set in implementation..
+ ICalculationStatus Status { get; }
+
+ ///
+ /// Resets the IIterator to the pre-calculation state.
+ ///
+ ///
+ /// Note to implementers: Invoking this method should not clear the user defined
+ /// property values, only the state that is used to track the progress of the
+ /// calculation.
+ ///
+ void ResetToPrecalculationState();
+
+#if SILVERLIGHT
+ IIterator Clone();
+#endif
+ }
+}
diff --git a/src/Numerics/LinearAlgebra/Double/Solvers/Iterative/BiCgStab.cs b/src/Numerics/LinearAlgebra/Double/Solvers/Iterative/BiCgStab.cs
new file mode 100644
index 00000000..c3279cbc
--- /dev/null
+++ b/src/Numerics/LinearAlgebra/Double/Solvers/Iterative/BiCgStab.cs
@@ -0,0 +1,524 @@
+//
+// 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.
+//
+
+namespace MathNet.Numerics.LinearAlgebra.Double.Solvers.Iterative
+{
+ using System;
+ using Preconditioners;
+ using Properties;
+ using Status;
+
+ ///
+ /// A Bi-Conjugate Gradient stabilized iterative matrix solver.
+ ///
+ ///
+ ///
+ /// 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.
+ /// Note that much of the success of the solver depends on the selection of the
+ /// proper preconditioner.
+ ///
+ ///
+ /// The Bi-CGSTAB algorithm was taken from:
+ /// Templates for the solution of linear systems: Building blocks
+ /// for iterative methods
+ ///
+ /// 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
+ ///
+ /// Url: http://www.netlib.org/templates/Templates.html
+ ///
+ /// Algorithm is described in Chapter 2, section 2.3.8, page 27
+ ///
+ ///
+ /// The example code below provides an indication of the possible use of the
+ /// solver.
+ ///
+ ///
+ public sealed class BiCgStab : IIterativeSolver
+ {
+ ///
+ /// The status used if there is no status, i.e. the solver hasn't run yet and there is no
+ /// iterator.
+ ///
+ private static readonly ICalculationStatus DefaultStatus = new CalculationIndetermined();
+
+ ///
+ /// The preconditioner that will be used. Can be set to , in which case the default
+ /// pre-conditioner will be used.
+ ///
+ private IPreConditioner _preconditioner;
+
+ ///
+ /// The iterative process controller.
+ ///
+ private IIterator _iterator;
+
+ ///
+ /// Indicates if the user has stopped the solver.
+ ///
+ private bool _hasBeenStopped;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// When using this constructor the solver will use the with
+ /// the standard settings and a default preconditioner.
+ ///
+ public BiCgStab() : this(null, null)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ ///
+ /// When using this constructor the solver will use a default preconditioner.
+ ///
+ ///
+ /// The main advantages of using a user defined are:
+ ///
+ /// It is possible to set the desired convergence limits.
+ ///
+ /// It is possible to check the reason for which the solver finished
+ /// the iterative procedure by calling the property.
+ ///
+ ///
+ ///
+ ///
+ /// The that will be used to monitor the iterative process.
+ public BiCgStab(IIterator iterator) : this(null, iterator)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// When using this constructor the solver will use the with
+ /// the standard settings.
+ ///
+ /// The that will be used to precondition the matrix equation.
+ public BiCgStab(IPreConditioner preconditioner) : this(preconditioner, null)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ ///
+ /// The main advantages of using a user defined are:
+ ///
+ /// It is possible to set the desired convergence limits.
+ ///
+ /// It is possible to check the reason for which the solver finished
+ /// the iterative procedure by calling the property.
+ ///
+ ///
+ ///
+ ///
+ /// The that will be used to precondition the matrix equation.
+ /// The that will be used to monitor the iterative process.
+ public BiCgStab(IPreConditioner preconditioner, IIterator iterator)
+ {
+ _iterator = iterator;
+ _preconditioner = preconditioner;
+ }
+
+ ///
+ /// Sets the that will be used to precondition the iterative process.
+ ///
+ /// The preconditioner.
+ public void SetPreconditioner(IPreConditioner preconditioner)
+ {
+ _preconditioner = preconditioner;
+ }
+
+ ///
+ /// Sets the that will be used to track the iterative process.
+ ///
+ /// The iterator.
+ public void SetIterator(IIterator iterator)
+ {
+ _iterator = iterator;
+ }
+
+ ///
+ /// Gets the status of the iteration once the calculation is finished.
+ ///
+ public ICalculationStatus IterationResult
+ {
+ get
+ {
+ return (_iterator != null) ? _iterator.Status : DefaultStatus;
+ }
+ }
+
+ ///
+ /// Stops the solve process.
+ ///
+ ///
+ /// Note that it may take an indetermined amount of time for the solver to actually stop the process.
+ ///
+ public void StopSolve()
+ {
+ _hasBeenStopped = true;
+ }
+
+ ///
+ /// Solves the matrix equation Ax = b, where A is the coefficient matrix, b is the
+ /// solution vector and x is the unknown vector.
+ ///
+ /// The coefficient , A.
+ /// The solution , b.
+ /// The result , x.
+ public Vector Solve(Matrix matrix, Vector vector)
+ {
+ if (vector == null)
+ {
+ throw new ArgumentNullException();
+ }
+
+ Vector result = new DenseVector(matrix.RowCount);
+ Solve(matrix, vector, result);
+ return result;
+ }
+
+ ///
+ /// Solves the matrix equation Ax = b, where A is the coefficient matrix, b is the
+ /// solution vector and x is the unknown vector.
+ ///
+ /// The coefficient , A.
+ /// The solution , b.
+ /// The result , x.
+ public void Solve(Matrix matrix, Vector input, Vector result)
+ {
+ // If we were stopped before, we are no longer
+ // We're doing this at the start of the method to ensure
+ // that we can use these fields immediately.
+ _hasBeenStopped = false;
+
+ // Parameters checks
+ if (matrix == null)
+ {
+ throw new ArgumentNullException("matrix");
+ }
+
+ if (matrix.RowCount != matrix.ColumnCount)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixSquare, "matrix");
+ }
+
+ if (input == null)
+ {
+ throw new ArgumentNullException("input");
+ }
+
+ if (result == null)
+ {
+ throw new ArgumentNullException("result");
+ }
+
+ if (result.Count != input.Count)
+ {
+ throw new ArgumentException(Resources.ArgumentVectorsSameLength);
+ }
+
+ // Initialize the solver fields
+ // Set the convergence monitor
+ if (_iterator == null)
+ {
+ _iterator = Iterator.CreateDefault();
+ }
+
+ if (_preconditioner == null)
+ {
+ _preconditioner = new UnitPreconditioner();
+ }
+
+ _preconditioner.Initialize(matrix);
+
+ // Compute r_0 = b - Ax_0 for some initial guess x_0
+ // In this case we take x_0 = vector
+ // This is basically a SAXPY so it could be made a lot faster
+ Vector residuals = new DenseVector(matrix.RowCount);
+ CalculateTrueResidual(matrix, residuals, result, input);
+
+ // Choose r~ (for example, r~ = r_0)
+ var tempResiduals = residuals.Clone();
+ 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 vecP = new DenseVector(residuals.Count);
+ Vector vecPdash = new DenseVector(residuals.Count);
+ Vector nu = new DenseVector(residuals.Count);
+ Vector vecS = new DenseVector(residuals.Count);
+ Vector vecSdash = new DenseVector(residuals.Count);
+ Vector t = new DenseVector(residuals.Count);
+
+ Vector mult = new DenseVector(residuals.Count);
+
+ // create some temporary double variables that are needed
+ // to hold values in between iterations
+ double currentRho = 0;
+ double alpha = 0;
+ double omega = 0;
+
+ var iterationNumber = 0;
+ while (ShouldContinue(iterationNumber, result, input, residuals))
+ {
+ // rho_(i-1) = r~^T r_(i-1) // dotproduct r~ and r_(i-1)
+ var oldRho = currentRho;
+ currentRho = tempResiduals.DotProduct(residuals);
+
+ // if (rho_(i-1) == 0) // METHOD FAILS
+ // If rho is only 1 ULP from zero then we fail.
+ if (currentRho.AlmostEqual(0, 1))
+ {
+ // Rho-type breakdown
+ throw new Exception("Iterative solver experience a numerical break down");
+ }
+
+ if (iterationNumber != 0)
+ {
+ // beta_(i-1) = (rho_(i-1)/rho_(i-2))(alpha_(i-1)/omega(i-1))
+ var beta = (currentRho / oldRho) * (alpha / omega);
+
+ // p_i = r_(i-1) + beta_(i-1)(p_(i-1) - omega_(i-1) * nu_(i-1))
+ nu.Multiply(-omega, mult);
+ vecP.Add(mult);
+
+ vecP.Multiply(beta);
+ vecP.Add(residuals);
+ }
+ else
+ {
+ // p_i = r_(i-1)
+ residuals.CopyTo(vecP);
+ }
+
+ // SOLVE Mp~ = p_i // M = preconditioner
+ _preconditioner.Approximate(vecP, vecPdash);
+
+ // nu_i = Ap~
+ matrix.Multiply(vecPdash, nu);
+
+ // alpha_i = rho_(i-1)/ (r~^T nu_i) = rho / dotproduct(r~ and nu_i)
+ alpha = currentRho * 1 / tempResiduals.DotProduct(nu);
+
+ // s = r_(i-1) - alpha_i nu_i
+ nu.Multiply(-alpha, mult);
+ residuals.Add(mult, 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.Add(result);
+
+ // 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^
+ vecSdash.Multiply(omega, mult);
+ result.Add(mult);
+
+ vecPdash.Multiply(alpha, mult);
+ result.Add(mult);
+
+ t.Multiply(-omega, residuals);
+ residuals.Add(vecS);
+
+ // for continuation it is necessary that omega_i != 0.0
+ // If omega is only 1 ULP from zero then we fail.
+ if (omega.AlmostEqual(0, 1))
+ {
+ // Omega-type breakdown
+ throw new Exception("Iterative solver experience a numerical break down");
+ }
+
+ if (!ShouldContinue(iterationNumber, result, input, residuals))
+ {
+ // Recalculate the residuals and go round again. This is done to ensure that
+ // we have the proper residuals.
+ // The residual calculation based on omega_i * s can be off by a factor 10. So here
+ // we calculate the real residual (which can be expensive) but we only do it if we're
+ // sufficiently close to the finish.
+ CalculateTrueResidual(matrix, residuals, result, input);
+ }
+
+ iterationNumber++;
+ }
+ }
+
+ ///
+ /// Calculates the true residual of the matrix equation Ax = b according to: residual = b - Ax
+ ///
+ /// Instance of the A.
+ /// Residual values in .
+ /// Instance of the x.
+ /// Instance of the b.
+ private static void CalculateTrueResidual(Matrix matrix, Vector residual, Vector x, Vector b)
+ {
+ // -Ax = residual
+ matrix.Multiply(x, residual);
+
+ // Do not use residual = residual.Negate() because it creates another object
+ residual.Multiply(-1);
+
+ // residual + b
+ residual.Add(b);
+ }
+
+ ///
+ /// Determine if calculation should continue
+ ///
+ /// Number of iterations passed
+ /// Result .
+ /// Source .
+ /// Residual .
+ /// true if continue, otherwise false
+ private bool ShouldContinue(int iterationNumber, Vector result, Vector source, Vector residuals)
+ {
+ if (_hasBeenStopped)
+ {
+ _iterator.IterationCancelled();
+ return true;
+ }
+
+ _iterator.DetermineStatus(iterationNumber, result, source, residuals);
+ var status = _iterator.Status;
+
+ // We stop if either:
+ // - the user has stopped the calculation
+ // - the calculation needs to be stopped from a numerical point of view (divergence, convergence etc.)
+ return (!status.TerminatesCalculation) && (!_hasBeenStopped);
+ }
+
+ ///
+ /// Solves the matrix equation AX = B, where A is the coefficient matrix, B is the
+ /// solution matrix and X is the unknown matrix.
+ ///
+ /// The coefficient , A.
+ /// The solution , B.
+ /// The result , X.
+ public Matrix Solve(Matrix matrix, Matrix 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;
+ }
+
+ ///
+ /// Solves the matrix equation AX = B, where A is the coefficient matrix, B is the
+ /// solution matrix and X is the unknown matrix.
+ ///
+ /// The coefficient , A.
+ /// The solution , B.
+ /// The result , X
+ public void Solve(Matrix matrix, Matrix input, Matrix result)
+ {
+ if (matrix == null)
+ {
+ throw new ArgumentNullException("matrix");
+ }
+
+ if (input == null)
+ {
+ throw new ArgumentNullException("input");
+ }
+
+ if (result == null)
+ {
+ throw new ArgumentNullException("result");
+ }
+
+ if (matrix.RowCount != input.RowCount || input.RowCount != result.RowCount || input.ColumnCount != result.ColumnCount)
+ {
+ throw 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);
+ }
+ }
+ }
+ }
+}
diff --git a/src/Numerics/LinearAlgebra/Double/Solvers/Iterative/CompositeSolver.cs b/src/Numerics/LinearAlgebra/Double/Solvers/Iterative/CompositeSolver.cs
new file mode 100644
index 00000000..378e571e
--- /dev/null
+++ b/src/Numerics/LinearAlgebra/Double/Solvers/Iterative/CompositeSolver.cs
@@ -0,0 +1,627 @@
+//
+// 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.
+//
+
+namespace MathNet.Numerics.LinearAlgebra.Double.Solvers.Iterative
+{
+ using System;
+ using System.Collections.Generic;
+ using System.IO;
+ using System.Linq;
+ using System.Reflection;
+ using Properties;
+ using Status;
+
+ ///
+ /// A composite matrix solver. The actual solver is made by a sequence of
+ /// matrix solvers.
+ ///
+ ///
+ ///
+ /// Solver based on:
+ /// Faster PDE-based simulations using robust composite linear solvers
+ /// S. Bhowmicka, P. Raghavan a,*, L. McInnes b, B. Norris
+ /// Future Generation Computer Systems, Vol 20, 2004, pp 373–387
+ ///
+ ///
+ /// Note that if an iterator is passed to this solver it will be used for all the sub-solvers.
+ ///
+ ///
+ public sealed class CompositeSolver : IIterativeSolver
+ {
+ #region Internal class - DoubleComparer
+ ///
+ /// An IComparer used to compare double precision floating points.
+ ///
+ /// NOTE: The instance of this class is used only in . 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 constructor
+ public sealed class DoubleComparer : IComparer
+ {
+ ///
+ /// Compares two double values based on the selected comparison method.
+ ///
+ /// The first double to compare.
+ /// The second double to compare.
+ ///
+ /// 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.
+ ///
+ public int Compare(double x, double y)
+ {
+ return x.CompareTo(y, 1);
+ }
+ }
+ #endregion
+
+ ///
+ /// The default status used if the solver is not running.
+ ///
+ private static readonly ICalculationStatus NonRunningStatus = new CalculationIndetermined();
+
+ ///
+ /// The default status used if the solver is running.
+ ///
+ private static readonly ICalculationStatus RunningStatus = new CalculationRunning();
+
+#if SILVERLIGHT
+ private static readonly Dictionary> SolverSetups = new Dictionary>();
+#else
+ ///
+ /// The collection of iterative solver setups. Stored based on the
+ /// ratio between the relative speed and relative accuracy.
+ ///
+ private static readonly SortedList> SolverSetups = new SortedList>(new DoubleComparer());
+#endif
+
+ #region Solver information loading methods
+
+ ///
+ /// Loads all the available objects from the MathNet.Numerics assembly.
+ ///
+ public static void LoadSolverInformation()
+ {
+ LoadSolverInformation(new Type[0]);
+ }
+
+ ///
+ /// Loads the available objects from the MathNet.Numerics assembly.
+ ///
+ /// The types that should not be loaded.
+ public static void LoadSolverInformation(Type[] typesToExclude)
+ {
+ LoadSolverInformationFromAssembly(Assembly.GetExecutingAssembly(), typesToExclude);
+ }
+
+ ///
+ /// Loads the available objects from the assembly specified by the file location.
+ ///
+ /// The fully qualified path to the assembly.
+ public static void LoadSolverInformationFromAssembly(string assemblyLocation)
+ {
+ LoadSolverInformationFromAssembly(assemblyLocation, new Type[0]);
+ }
+
+ ///
+ /// Loads the available objects from the assembly specified by the file location.
+ ///
+ /// The fully qualified path to the assembly.
+ /// The types that should not be loaded.
+ 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);
+
+ // 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);
+ }
+
+ ///
+ /// Loads the available objects from the assembly specified by the assembly name.
+ ///
+ /// The of the assembly that should be searched for setup objects.
+ public static void LoadSolverInformationFromAssembly(AssemblyName assemblyName)
+ {
+ LoadSolverInformationFromAssembly(assemblyName, new Type[0]);
+ }
+
+ ///
+ /// Loads the available objects from the assembly specified by the assembly name.
+ ///
+ /// The of the assembly that should be searched for setup objects.
+ /// The types that should not be loaded.
+ 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);
+ }
+
+ ///
+ /// Loads the available objects from the assembly specified by the type.
+ ///
+ /// The type in the assembly which should be searched for setup objects.
+ public static void LoadSolverInformationFromAssembly(Type typeInAssembly)
+ {
+ LoadSolverInformationFromAssembly(typeInAssembly, new Type[0]);
+ }
+
+ ///
+ /// Loads the available objects from the assembly specified by the type.
+ ///
+ /// The type in the assembly which should be searched for setup objects.
+ /// The types that should not be loaded.
+ public static void LoadSolverInformationFromAssembly(Type typeInAssembly, params Type[] typesToExclude)
+ {
+ if (typeInAssembly == null)
+ {
+ throw new ArgumentNullException("typeInAssembly");
+ }
+
+ LoadSolverInformationFromAssembly(typeInAssembly.Assembly, typesToExclude);
+ }
+
+ ///
+ /// Loads the available objects from the specified assembly.
+ ///
+ /// The assembly which will be searched for setup objects.
+ public static void LoadSolverInformationFromAssembly(Assembly assembly)
+ {
+ LoadSolverInformationFromAssembly(assembly, new Type[0]);
+ }
+
+ ///
+ /// Loads the available objects from the specified assembly.
+ ///
+ /// The assembly which will be searched for setup objects.
+ /// The types that should not be loaded.
+ 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(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();
+ 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).IsAssignableFrom(match)))
+ {
+ continue;
+ }
+
+ // See if we actually want this type of iterative solver
+ IIterativeSolverSetup 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)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());
+ }
+
+ var list = SolverSetups[ratio];
+ list.Add(setup);
+ }
+ }
+
+ #endregion
+
+ ///
+ /// The collection of solvers that will be used to
+ ///
+ private readonly List _solvers = new List();
+
+ ///
+ /// The status of the calculation.
+ ///
+ private ICalculationStatus _status = NonRunningStatus;
+
+ ///
+ /// The iterator that is used to control the iteration process.
+ ///
+ private IIterator _iterator;
+
+ ///
+ /// A flag indicating if the solver has been stopped or not.
+ ///
+ private bool _hasBeenStopped;
+
+ ///
+ /// The solver that is currently running. Reference is used to be able to stop the
+ /// solver if the user cancels the solve process.
+ ///
+ private IIterativeSolver _currentSolver;
+
+ ///
+ /// Initializes a new instance of the class with the default iterator.
+ ///
+ public CompositeSolver() : this(null)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class with the specified iterator.
+ ///
+ /// The iterator that will be used to control the iteration process.
+ public CompositeSolver(IIterator iterator)
+ {
+ _iterator = iterator;
+ }
+
+ ///
+ /// Sets the IIterator that will be used to track the iterative process.
+ ///
+ /// The iterator.
+ public void SetIterator(IIterator iterator)
+ {
+ _iterator = iterator;
+ }
+
+ ///
+ /// Gets the status of the iteration once the calculation is finished.
+ ///
+ public ICalculationStatus IterationResult
+ {
+ get
+ {
+ return _status;
+ }
+ }
+
+ ///
+ /// Stops the solve process.
+ ///
+ ///
+ /// Note that it may take an indetermined amount of time for the solver to actually stop the process.
+ ///
+ public void StopSolve()
+ {
+ _hasBeenStopped = true;
+ if (_currentSolver != null)
+ {
+ _currentSolver.StopSolve();
+ }
+ }
+
+ ///
+ /// Solves the matrix equation Ax = b, where A is the coefficient matrix, b is the
+ /// solution vector and x is the unknown vector.
+ ///
+ /// The coefficient matrix, A.
+ /// The solution vector, b.
+ /// The result vector, x.
+ public Vector Solve(Matrix matrix, Vector vector)
+ {
+ if (vector == null)
+ {
+ throw new ArgumentNullException();
+ }
+
+ Vector result = new DenseVector(matrix.RowCount);
+ Solve(matrix, vector, result);
+ return result;
+ }
+
+ ///
+ /// Solves the matrix equation Ax = b, where A is the coefficient matrix, b is the
+ /// solution vector and x is the unknown vector.
+ ///
+ /// The coefficient matrix, A.
+ /// The solution vector, b
+ /// The result vector, x
+ public void Solve(Matrix matrix, Vector input, Vector result)
+ {
+ // If we were stopped before, we are no longer
+ // We're doing this at the start of the method to ensure
+ // that we can use these fields immediately.
+ _hasBeenStopped = false;
+ _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;
+ }
+
+ ///
+ /// Load solvers
+ ///
+ 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());
+ }
+ }
+
+ ///
+ /// Solves the matrix equation AX = B, where A is the coefficient matrix, B is the
+ /// solution matrix and X is the unknown matrix.
+ ///
+ /// The coefficient matrix, A.
+ /// The solution matrix, B.
+ /// The result matrix, X.
+ public Matrix Solve(Matrix matrix, Matrix 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;
+ }
+
+ ///
+ /// Solves the matrix equation AX = B, where A is the coefficient matrix, B is the
+ /// solution matrix and X is the unknown matrix.
+ ///
+ /// The coefficient matrix, A.
+ /// The solution matrix, B.
+ /// The result matrix, X
+ public void Solve(Matrix matrix, Matrix input, Matrix result)
+ {
+ if (matrix == null)
+ {
+ throw new ArgumentNullException("matrix");
+ }
+
+ if (input == null)
+ {
+ throw new ArgumentNullException("input");
+ }
+
+ if (result == null)
+ {
+ throw new ArgumentNullException("result");
+ }
+
+ if (matrix.RowCount != input.RowCount || input.RowCount != result.RowCount || input.ColumnCount != result.ColumnCount)
+ {
+ throw 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);
+ }
+ }
+ }
+ }
+}
diff --git a/src/Numerics/LinearAlgebra/Double/Solvers/Iterative/GpBiCg.cs b/src/Numerics/LinearAlgebra/Double/Solvers/Iterative/GpBiCg.cs
new file mode 100644
index 00000000..d981caf6
--- /dev/null
+++ b/src/Numerics/LinearAlgebra/Double/Solvers/Iterative/GpBiCg.cs
@@ -0,0 +1,634 @@
+//
+// 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.
+//
+
+namespace MathNet.Numerics.LinearAlgebra.Double.Solvers.Iterative
+{
+ using System;
+ using Preconditioners;
+ using Properties;
+ using Status;
+
+ ///
+ /// A Generalized Product Bi-Conjugate Gradient iterative matrix solver.
+ ///
+ ///
+ ///
+ /// 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.
+ /// Note that much of the success of the solver depends on the selection of the
+ /// proper preconditioner.
+ ///
+ ///
+ /// The GPBiCG algorithm was taken from:
+ /// GPBiCG(m,l): A hybrid of BiCGSTAB and GPBiCG methods with
+ /// efficiency and robustness
+ ///
+ /// S. Fujino
+ ///
+ /// Applied Numerical Mathematics, Volume 41, 2002, pp 107 - 117
+ ///
+ ///
+ ///
+ /// The example code below provides an indication of the possible use of the
+ /// solver.
+ ///
+ ///
+ public sealed class GpBiCg : IIterativeSolver
+ {
+ ///
+ /// The status used if there is no status, i.e. the solver hasn't run yet and there is no
+ /// iterator.
+ ///
+ private static readonly ICalculationStatus DefaultStatus = new CalculationIndetermined();
+
+ ///
+ /// The preconditioner that will be used. Can be set to null, in which case the default
+ /// pre-conditioner will be used.
+ ///
+ private IPreConditioner _preconditioner;
+
+ ///
+ /// The iterative process controller.
+ ///
+ private IIterator _iterator;
+
+ ///
+ /// Indicates the number of BiCGStab steps should be taken
+ /// before switching.
+ ///
+ private int _numberOfBiCgStabSteps = 1;
+
+ ///
+ /// Indicates the number of GPBiCG steps should be taken
+ /// before switching.
+ ///
+ private int _numberOfGpbiCgSteps = 4;
+
+ ///
+ /// Indicates if the user has stopped the solver.
+ ///
+ private bool _hasBeenStopped;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// When using this constructor the solver will use the with
+ /// the standard settings and a default preconditioner.
+ ///
+ public GpBiCg() : this(null, null)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ ///
+ /// When using this constructor the solver will use a default preconditioner.
+ ///
+ ///
+ /// The main advantages of using a user defined are:
+ ///
+ /// It is possible to set the desired convergence limits.
+ ///
+ /// It is possible to check the reason for which the solver finished
+ /// the iterative procedure by calling the property.
+ ///
+ ///
+ ///
+ ///
+ /// The that will be used to monitor the iterative process.
+ public GpBiCg(IIterator iterator) : this(null, iterator)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// When using this constructor the solver will use the with
+ /// the standard settings.
+ ///
+ /// The that will be used to precondition the matrix equation.
+ public GpBiCg(IPreConditioner preconditioner) : this(preconditioner, null)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ ///
+ /// The main advantages of using a user defined are:
+ ///
+ /// It is possible to set the desired convergence limits.
+ ///
+ /// It is possible to check the reason for which the solver finished
+ /// the iterative procedure by calling the property.
+ ///
+ ///
+ ///
+ ///
+ /// The that will be used to precondition the matrix equation.
+ /// The that will be used to monitor the iterative process.
+ public GpBiCg(IPreConditioner preconditioner, IIterator iterator)
+ {
+ _iterator = iterator;
+ _preconditioner = preconditioner;
+ }
+
+ ///
+ /// Gets or sets the number of steps taken with the BiCgStab algorithm
+ /// before switching over to the GPBiCG algorithm.
+ ///
+ public int NumberOfBiCgStabSteps
+ {
+ get
+ {
+ return _numberOfBiCgStabSteps;
+ }
+
+ set
+ {
+ if (value < 0)
+ {
+ throw new ArgumentOutOfRangeException("value");
+ }
+
+ _numberOfBiCgStabSteps = value;
+ }
+ }
+
+ ///
+ /// Gets or sets the number of steps taken with the GPBiCG algorithm
+ /// before switching over to the BiCgStab algorithm.
+ ///
+ public int NumberOfGpBiCgSteps
+ {
+ get
+ {
+ return _numberOfGpbiCgSteps;
+ }
+
+ set
+ {
+ if (value < 0)
+ {
+ throw new ArgumentOutOfRangeException("value");
+ }
+
+ _numberOfGpbiCgSteps = value;
+ }
+ }
+
+ ///
+ /// Sets the that will be used to precondition the iterative process.
+ ///
+ /// The preconditioner.
+ public void SetPreconditioner(IPreConditioner preconditioner)
+ {
+ _preconditioner = preconditioner;
+ }
+
+ ///
+ /// Sets the that will be used to track the iterative process.
+ ///
+ /// The iterator.
+ public void SetIterator(IIterator iterator)
+ {
+ _iterator = iterator;
+ }
+
+ ///
+ /// Gets the status of the iteration once the calculation is finished.
+ ///
+ public ICalculationStatus IterationResult
+ {
+ get
+ {
+ return (_iterator != null) ? _iterator.Status : DefaultStatus;
+ }
+ }
+
+ ///
+ /// Stops the solve process.
+ ///
+ ///
+ /// Note that it may take an indetermined amount of time for the solver to actually
+ /// stop the process.
+ ///
+ public void StopSolve()
+ {
+ _hasBeenStopped = true;
+ }
+
+ ///
+ /// Solves the matrix equation Ax = b, where A is the coefficient matrix, b is the
+ /// solution vector and x is the unknown vector.
+ ///
+ /// The coefficient matrix, A.
+ /// The solution vector, b.
+ /// The result vector, x.
+ public Vector Solve(Matrix matrix, Vector vector)
+ {
+ if (vector == null)
+ {
+ throw new ArgumentNullException();
+ }
+
+ Vector result = new DenseVector(matrix.RowCount);
+ Solve(matrix, vector, result);
+ return result;
+ }
+
+ ///
+ /// Solves the matrix equation Ax = b, where A is the coefficient matrix, b is the
+ /// solution vector and x is the unknown vector.
+ ///
+ /// The coefficient matrix, A.
+ /// The solution vector, b
+ /// The result vector, x
+ public void Solve(Matrix matrix, Vector input, Vector result)
+ {
+ // If we were stopped before, we are no longer
+ // We're doing this at the start of the method to ensure
+ // that we can use these fields immediately.
+ _hasBeenStopped = false;
+
+ // Error checks
+ if (matrix == null)
+ {
+ throw new ArgumentNullException("matrix");
+ }
+
+ if (matrix.RowCount != matrix.ColumnCount)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixSquare, "matrix");
+ }
+
+ if (input == null)
+ {
+ throw new ArgumentNullException("input");
+ }
+
+ if (result == null)
+ {
+ throw new ArgumentNullException("result");
+ }
+
+ if (result.Count != input.Count)
+ {
+ throw new ArgumentException(Resources.ArgumentVectorsSameLength);
+ }
+
+ // Initialize the solver fields
+
+ // Set the convergence monitor
+ if (_iterator == null)
+ {
+ _iterator = Iterator.CreateDefault();
+ }
+
+ if (_preconditioner == null)
+ {
+ _preconditioner = new UnitPreconditioner();
+ }
+
+ _preconditioner.Initialize(matrix);
+
+ // x_0 is initial guess
+ // Take x_0 = 0
+ Vector xtemp = new DenseVector(input.Count);
+
+ // r_0 = b - Ax_0
+ // This is basically a SAXPY so it could be made a lot faster
+ Vector residuals = new DenseVector(matrix.RowCount);
+ CalculateTrueResidual(matrix, residuals, xtemp, input);
+
+ // Define the temporary scalars
+ double beta = 0;
+ double sigma;
+
+ // Define the temporary vectors
+ // rDash_0 = r_0
+ Vector rdash = new DenseVector(residuals);
+
+ // t_-1 = 0
+ Vector t = new DenseVector(residuals.Count);
+ Vector t0 = new DenseVector(residuals.Count);
+
+ // w_-1 = 0
+ Vector w = new DenseVector(residuals.Count);
+
+ // Define the remaining temporary vectors
+ Vector c = new DenseVector(residuals.Count);
+ Vector p = new DenseVector(residuals.Count);
+ Vector s = new DenseVector(residuals.Count);
+ Vector u = new DenseVector(residuals.Count);
+ Vector y = new DenseVector(residuals.Count);
+ Vector z = new DenseVector(residuals.Count);
+
+ Vector temp = new DenseVector(residuals.Count);
+ Vector mult = new DenseVector(residuals.Count);
+
+ // for (k = 0, 1, .... )
+ var iterationNumber = 0;
+ while (ShouldContinue(iterationNumber, xtemp, input, residuals))
+ {
+ // p_k = r_k + beta_(k-1) * (p_(k-1) - u_(k-1))
+ p.Subtract(u, temp);
+
+ temp.Multiply(beta, mult);
+ residuals.Add(mult, p);
+
+ // Solve M b_k = p_k
+ _preconditioner.Approximate(p, temp);
+
+ // s_k = A b_k
+ matrix.Multiply(temp, s);
+
+ // alpha_k = (r*_0 * r_k) / (r*_0 * s_k)
+ var alpha = rdash.DotProduct(residuals) / rdash.DotProduct(s);
+
+ // y_k = t_(k-1) - r_k - alpha_k * w_(k-1) + alpha_k s_k
+ s.Subtract(w, temp);
+ t.Subtract(residuals, y);
+
+ temp.Multiply(alpha, mult);
+ y.Add(mult);
+
+ // Store the old value of t in t0
+ t.CopyTo(t0);
+
+ // t_k = r_k - alpha_k s_k
+ s.Multiply(-alpha, mult);
+ residuals.Add(mult, t);
+
+ // Solve M d_k = t_k
+ _preconditioner.Approximate(t, temp);
+
+ // c_k = A d_k
+ matrix.Multiply(temp, c);
+ var cdot = c.DotProduct(c);
+
+ // cDot can only be zero if c is a zero vector
+ // We'll set cDot to 1 if it is zero to prevent NaN's
+ // Note that the calculation should continue fine because
+ // c.DotProduct(t) will be zero and so will c.DotProduct(y)
+ if (cdot.AlmostEqual(0, 1))
+ {
+ cdot = 1.0;
+ }
+
+ // Even if we don't want to do any BiCGStab steps we'll still have
+ // to do at least one at the start to initialize the
+ // system, but we'll only have to take special measures
+ // if we don't do any so ...
+ var ctdot = c.DotProduct(t);
+ double eta;
+ if (((_numberOfBiCgStabSteps == 0) && (iterationNumber == 0)) || ShouldRunBiCgStabSteps(iterationNumber))
+ {
+ // sigma_k = (c_k * t_k) / (c_k * c_k)
+ sigma = ctdot / cdot;
+
+ // eta_k = 0
+ eta = 0;
+ }
+ else
+ {
+ var ydot = y.DotProduct(y);
+
+ // yDot can only be zero if y is a zero vector
+ // We'll set yDot to 1 if it is zero to prevent NaN's
+ // Note that the calculation should continue fine because
+ // y.DotProduct(t) will be zero and so will c.DotProduct(y)
+ if (ydot.AlmostEqual(0, 1))
+ {
+ ydot = 1.0;
+ }
+
+ var ytdot = y.DotProduct(t);
+ var cydot = c.DotProduct(y);
+
+ var denom = (cdot * ydot) - (cydot * cydot);
+
+ // sigma_k = ((y_k * y_k)(c_k * t_k) - (y_k * t_k)(c_k * y_k)) / ((c_k * c_k)(y_k * y_k) - (y_k * c_k)(c_k * y_k))
+ sigma = ((ydot * ctdot) - (ytdot * cydot)) / denom;
+
+ // eta_k = ((c_k * c_k)(y_k * t_k) - (y_k * c_k)(c_k * t_k)) / ((c_k * c_k)(y_k * y_k) - (y_k * c_k)(c_k * y_k))
+ eta = ((cdot * ytdot) - (cydot * ctdot)) / denom;
+ }
+
+ // u_k = sigma_k s_k + eta_k (t_(k-1) - r_k + beta_(k-1) u_(k-1))
+ u.Multiply(beta, mult);
+ t0.Add(mult, temp);
+
+ temp.Subtract(residuals);
+ temp.Multiply(eta);
+
+ s.Multiply(sigma, mult);
+ temp.Add(mult, u);
+
+ // z_k = sigma_k r_k +_ eta_k z_(k-1) - alpha_k u_k
+ z.Multiply(eta);
+
+ u.Multiply(-alpha, mult);
+ z.Add(mult);
+
+ residuals.Multiply(sigma, mult);
+ z.Add(mult);
+
+ // x_(k+1) = x_k + alpha_k p_k + z_k
+ p.Multiply(alpha, mult);
+ xtemp.Add(mult);
+
+ xtemp.Add(z);
+
+ // r_(k+1) = t_k - eta_k y_k - sigma_k c_k
+ // Copy the old residuals to a temp vector because we'll
+ // need those in the next step
+ residuals.CopyTo(t0);
+
+ y.Multiply(-eta, mult);
+ t.Add(mult, residuals);
+
+ c.Multiply(-sigma, mult);
+ residuals.Add(mult);
+
+ // beta_k = alpha_k / sigma_k * (r*_0 * r_(k+1)) / (r*_0 * r_k)
+ // But first we check if there is a possible NaN. If so just reset beta to zero.
+ beta = (!sigma.AlmostEqual(0, 1)) ? alpha / sigma * rdash.DotProduct(residuals) / rdash.DotProduct(t0) : 0;
+
+ // w_k = c_k + beta_k s_k
+ s.Multiply(beta, mult);
+ c.Add(mult, 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++;
+ }
+ }
+
+ ///
+ /// Calculates the true residual of the matrix equation Ax = b according to: residual = b - Ax
+ ///
+ /// Instance of the A.
+ /// Residual values in .
+ /// Instance of the x.
+ /// Instance of the b.
+ private static void CalculateTrueResidual(Matrix matrix, Vector residual, Vector x, Vector b)
+ {
+ // -Ax = residual
+ matrix.Multiply(x, residual);
+ residual.Multiply(-1);
+
+ // residual + b
+ residual.Add(b);
+ }
+
+ ///
+ /// Determine if calculation should continue
+ ///
+ /// Number of iterations passed
+ /// Result .
+ /// Source .
+ /// Residual .
+ /// true if continue, otherwise false
+ private bool ShouldContinue(int iterationNumber, Vector result, Vector source, Vector residuals)
+ {
+ if (_hasBeenStopped)
+ {
+ _iterator.IterationCancelled();
+ return true;
+ }
+
+ _iterator.DetermineStatus(iterationNumber, result, source, residuals);
+ var status = _iterator.Status;
+
+ // We stop if either:
+ // - the user has stopped the calculation
+ // - the calculation needs to be stopped from a numerical point of view (divergence, convergence etc.)
+ return (!status.TerminatesCalculation) && (!_hasBeenStopped);
+ }
+
+ ///
+ /// Decide if to do steps with BiCgStab
+ ///
+ /// Number of iteration
+ /// true if yes, otherwise false
+ 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);
+ }
+
+ ///
+ /// Solves the matrix equation AX = B, where A is the coefficient matrix, B is the
+ /// solution matrix and X is the unknown matrix.
+ ///
+ /// The coefficient matrix, A.
+ /// The solution matrix, B.
+ /// The result matrix, X.
+ public Matrix Solve(Matrix matrix, Matrix 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;
+ }
+
+ ///
+ /// Solves the matrix equation AX = B, where A is the coefficient matrix, B is the
+ /// solution matrix and X is the unknown matrix.
+ ///
+ /// The coefficient matrix, A.
+ /// The solution matrix, B.
+ /// The result matrix, X
+ public void Solve(Matrix matrix, Matrix input, Matrix result)
+ {
+ if (matrix == null)
+ {
+ throw new ArgumentNullException("matrix");
+ }
+
+ if (input == null)
+ {
+ throw new ArgumentNullException("input");
+ }
+
+ if (result == null)
+ {
+ throw new ArgumentNullException("result");
+ }
+
+ if (matrix.RowCount != input.RowCount || input.RowCount != result.RowCount || input.ColumnCount != result.ColumnCount)
+ {
+ throw 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);
+ }
+ }
+ }
+ }
+}
diff --git a/src/Numerics/LinearAlgebra/Double/Solvers/Iterative/MlkBiCgStab.cs b/src/Numerics/LinearAlgebra/Double/Solvers/Iterative/MlkBiCgStab.cs
new file mode 100644
index 00000000..cd9f5d29
--- /dev/null
+++ b/src/Numerics/LinearAlgebra/Double/Solvers/Iterative/MlkBiCgStab.cs
@@ -0,0 +1,788 @@
+//
+// 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.
+//
+
+namespace MathNet.Numerics.LinearAlgebra.Double.Solvers.Iterative
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics;
+ using System.Linq;
+ using Distributions;
+ using Factorization;
+ using Preconditioners;
+ using Properties;
+ using Status;
+
+ ///
+ /// A Multiple-Lanczos Bi-Conjugate Gradient stabilized iterative matrix solver.
+ ///
+ ///
+ ///
+ /// The Multiple-Lanczos Bi-Conjugate Gradient stabilized (ML(k)-BiCGStab) solver is an 'improvement'
+ /// of the standard BiCgStab solver.
+ ///
+ ///
+ /// The algorithm was taken from:
+ /// ML(k)BiCGSTAB: A BiCGSTAB variant based on multiple Lanczos starting vectors
+ ///
+ /// Man-chung Yeung and Tony F. Chan
+ ///
+ /// SIAM Journal of Scientific Computing
+ ///
+ /// Volume 21, Number 4, pp. 1263 - 1290
+ ///
+ ///
+ /// The example code below provides an indication of the possible use of the
+ /// solver.
+ ///
+ ///
+ public sealed class MlkBiCgStab : IIterativeSolver
+ {
+ ///
+ /// The default number of starting vectors.
+ ///
+ private const int DefaultNumberOfStartingVectors = 50;
+
+ ///
+ /// The status used if there is no status, i.e. the solver hasn't run yet and there is no
+ /// iterator.
+ ///
+ private static readonly ICalculationStatus DefaultStatus = new CalculationIndetermined();
+
+ ///
+ /// The preconditioner that will be used. Can be set to , in which case the default
+ /// pre-conditioner will be used.
+ ///
+ private IPreConditioner _preconditioner;
+
+ ///
+ /// The iterative process controller.
+ ///
+ private IIterator _iterator;
+
+ ///
+ /// The collection of starting vectors which are used as the basis for the Krylov sub-space.
+ ///
+ private IList _startingVectors;
+
+ ///
+ /// The number of starting vectors used by the algorithm
+ ///
+ private int _numberOfStartingVectors = DefaultNumberOfStartingVectors;
+
+ ///
+ /// Indicates if the user has stopped the solver.
+ ///
+ private bool _hasBeenStopped;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// When using this constructor the solver will use the with
+ /// the standard settings and a default preconditioner.
+ ///
+ public MlkBiCgStab() : this(null, null)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ ///
+ /// When using this constructor the solver will use a default preconditioner.
+ ///
+ ///
+ /// The main advantages of using a user defined are:
+ ///
+ /// It is possible to set the desired convergence limits.
+ ///
+ /// It is possible to check the reason for which the solver finished
+ /// the iterative procedure by calling the property.
+ ///
+ ///
+ ///
+ ///
+ /// The that will be used to monitor the iterative process.
+ public MlkBiCgStab(IIterator iterator) : this(null, iterator)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// When using this constructor the solver will use the with
+ /// the standard settings.
+ ///
+ /// The that will be used to precondition the matrix equation.
+ public MlkBiCgStab(IPreConditioner preconditioner) : this(preconditioner, null)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ ///
+ /// The main advantages of using a user defined are:
+ ///
+ /// It is possible to set the desired convergence limits.
+ ///
+ /// It is possible to check the reason for which the solver finished
+ /// the iterative procedure by calling the property.
+ ///
+ ///
+ ///
+ ///
+ /// The that will be used to precondition the matrix equation.
+ /// The that will be used to monitor the iterative process.
+ public MlkBiCgStab(IPreConditioner preconditioner, IIterator iterator)
+ {
+ _iterator = iterator;
+ _preconditioner = preconditioner;
+ }
+
+ ///
+ /// Gets or sets the number of starting vectors.
+ ///
+ ///
+ /// Must be larger than 1 and smaller than the number of variables in the matrix that
+ /// for which this solver will be used.
+ ///
+ public int NumberOfStartingVectors
+ {
+ [DebuggerStepThrough]
+ get
+ {
+ return _numberOfStartingVectors;
+ }
+
+ [DebuggerStepThrough]
+ set
+ {
+ if (value <= 1)
+ {
+ throw new ArgumentOutOfRangeException("value");
+ }
+
+ _numberOfStartingVectors = value;
+ }
+ }
+
+ ///
+ /// Resets the number of starting vectors to the default value.
+ ///
+ public void ResetNumberOfStartingVectors()
+ {
+ _numberOfStartingVectors = DefaultNumberOfStartingVectors;
+ }
+
+ ///
+ /// Sets the that will be used to precondition the iterative process.
+ ///
+ /// The preconditioner.
+ public void SetPreconditioner(IPreConditioner preconditioner)
+ {
+ _preconditioner = preconditioner;
+ }
+
+ ///
+ /// Sets the that will be used to track the iterative process.
+ ///
+ /// The iterator.
+ public void SetIterator(IIterator iterator)
+ {
+ _iterator = iterator;
+ }
+
+ ///
+ /// Gets or sets a series of orthonormal vectors which will be used as basis for the
+ /// Krylov sub-space.
+ ///
+ public IList StartingVectors
+ {
+ [DebuggerStepThrough]
+ get
+ {
+ return _startingVectors;
+ }
+
+ [DebuggerStepThrough]
+ set
+ {
+ if ((value == null) || (value.Count == 0))
+ {
+ _startingVectors = null;
+ }
+ else
+ {
+ _startingVectors = value;
+ }
+ }
+ }
+
+ ///
+ /// Gets the status of the iteration once the calculation is finished.
+ ///
+ public ICalculationStatus IterationResult
+ {
+ [DebuggerStepThrough]
+ get
+ {
+ return (_iterator != null) ? _iterator.Status : DefaultStatus;
+ }
+ }
+
+ ///
+ /// Stops the solve process.
+ ///
+ ///
+ /// Note that it may take an indetermined amount of time for the solver to actually stop the process.
+ ///
+ public void StopSolve()
+ {
+ _hasBeenStopped = true;
+ }
+
+ ///
+ /// Solves the matrix equation Ax = b, where A is the coefficient matrix, b is the
+ /// solution vector and x is the unknown vector.
+ ///
+ /// The coefficient matrix, A.
+ /// The solution vector, b.
+ /// The result vector, x.
+ public Vector Solve(Matrix matrix, Vector vector)
+ {
+ if (vector == null)
+ {
+ throw new ArgumentNullException();
+ }
+
+ Vector result = new DenseVector(matrix.RowCount);
+ Solve(matrix, vector, result);
+ return result;
+ }
+
+ ///
+ /// Solves the matrix equation Ax = b, where A is the coefficient matrix, b is the
+ /// solution vector and x is the unknown vector.
+ ///
+ /// The coefficient matrix, A.
+ /// The solution vector, b
+ /// The result vector, x
+ public void Solve(Matrix matrix, Vector input, Vector result)
+ {
+ // If we were stopped before, we are no longer
+ // We're doing this at the start of the method to ensure
+ // that we can use these fields immediately.
+ _hasBeenStopped = false;
+
+ // Error checks
+ if (matrix == null)
+ {
+ throw new ArgumentNullException("matrix");
+ }
+
+ if (matrix.RowCount != matrix.ColumnCount)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixSquare, "matrix");
+ }
+
+ if (input == null)
+ {
+ throw new ArgumentNullException("input");
+ }
+
+ if (result == null)
+ {
+ throw new ArgumentNullException("result");
+ }
+
+ if (result.Count != input.Count)
+ {
+ throw new ArgumentException(Resources.ArgumentVectorsSameLength);
+ }
+
+ // Initialize the solver fields
+ // Set the convergence monitor
+ if (_iterator == null)
+ {
+ _iterator = Iterator.CreateDefault();
+ }
+
+ if (_preconditioner == null)
+ {
+ _preconditioner = new UnitPreconditioner();
+ }
+
+ _preconditioner.Initialize(matrix);
+
+ // Choose an initial guess x_0
+ // Take x_0 = 0
+ Vector xtemp = new DenseVector(input.Count);
+
+ // Choose k vectors q_1, q_2, ..., q_k
+ // Build a new set if:
+ // a) the stored set doesn't exist (i.e. == null)
+ // b) Is of an incorrect length (i.e. too long)
+ // c) The vectors are of an incorrect length (i.e. too long or too short)
+ var useOld = false;
+ if (_startingVectors != null)
+ {
+ // We don't accept collections with zero starting vectors so ...
+ if (_startingVectors.Count <= NumberOfStartingVectorsToCreate(_numberOfStartingVectors, input.Count))
+ {
+ // Only check the first vector for sizing. If that matches we assume the
+ // other vectors match too. If they don't the process will crash
+ if (_startingVectors[0].Count == input.Count)
+ {
+ useOld = true;
+ }
+ }
+ }
+
+ _startingVectors = useOld ? _startingVectors : CreateStartingVectors(_numberOfStartingVectors, input.Count);
+
+ // Store the number of starting vectors. Not really necessary but easier to type :)
+ var k = _startingVectors.Count;
+
+ // r_0 = b - Ax_0
+ // This is basically a SAXPY so it could be made a lot faster
+ Vector residuals = new DenseVector(matrix.RowCount);
+ CalculateTrueResidual(matrix, residuals, xtemp, input);
+
+ // Define the temporary scalars
+ var c = new double[k];
+
+ // Define the temporary vectors
+ Vector gtemp = new DenseVector(residuals.Count);
+
+ Vector u = new DenseVector(residuals.Count);
+ Vector utemp = new DenseVector(residuals.Count);
+ Vector temp = new DenseVector(residuals.Count);
+ Vector temp1 = new DenseVector(residuals.Count);
+
+ Vector zd = new DenseVector(residuals.Count);
+ Vector zg = new DenseVector(residuals.Count);
+ Vector zw = new DenseVector(residuals.Count);
+
+ Vector mult = 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].AlmostEqual(0, 1))
+ {
+ throw new Exception("Iterative solver experience a numerical break down");
+ }
+
+ // alpha_(jk+1) = q^T_1 r_((j-1)k+k) / c_((j-1)k+k)
+ var alpha = _startingVectors[0].DotProduct(residuals) / c[k - 1];
+
+ // u_(jk+1) = r_((j-1)k+k) - alpha_(jk+1) w_((j-1)k+k)
+ w[k - 1].Multiply(-alpha, mult);
+ residuals.Add(mult, 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.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)
+ utemp.Multiply(-rho, mult);
+ xtemp.Add(mult);
+
+ gtemp.Multiply(alpha);
+ xtemp.Add(gtemp);
+
+ // r_(jk+1) = rho_(j+1) A u~_(jk+1) + u_(jk+1)
+ u.CopyTo(residuals);
+
+ // Reuse temp
+ temp.Multiply(rho);
+ residuals.Add(temp);
+
+ // 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
+ double beta;
+ if (iterationNumber >= 1)
+ {
+ for (var s = i; s < k - 1; s++)
+ {
+ // beta^(jk+i)_((j-1)k+s) = -q^t_(s+1) z_d / c_((j-1)k+s)
+ beta = -_startingVectors[s + 1].DotProduct(zd) / c[s];
+
+ // z_d = z_d + beta^(jk+i)_((j-1)k+s) d_((j-1)k+s)
+ d[s].Multiply(beta, mult);
+ zd.Add(mult);
+
+ // z_g = z_g + beta^(jk+i)_((j-1)k+s) g_((j-1)k+s)
+ g[s].Multiply(beta, mult);
+ zg.Add(mult);
+
+ // z_w = z_w + beta^(jk+i)_((j-1)k+s) w_((j-1)k+s)
+ w[s].Multiply(beta, mult);
+ zw.Add(mult);
+ }
+ }
+
+ beta = rho * c[k - 1];
+ if (beta.AlmostEqual(0, 1))
+ {
+ throw new Exception("Iterative solver experience a numerical break down");
+ }
+
+ // beta^(jk+i)_((j-1)k+k) = -(q^T_1 (r_(jk+1) + rho_(j+1) z_w)) / (rho_(j+1) c_((j-1)k+k))
+ zw.Multiply(rho, mult);
+ residuals.Add(mult, temp);
+ beta = -_startingVectors[0].DotProduct(temp) / beta;
+
+ // z_g = z_g + beta^(jk+i)_((j-1)k+k) g_((j-1)k+k)
+ g[k - 1].Multiply(beta, mult);
+ zg.Add(mult);
+
+ // z_w = rho_(j+1) (z_w + beta^(jk+i)_((j-1)k+k) w_((j-1)k+k))
+ w[k - 1].Multiply(beta, mult);
+ zw.Add(mult);
+ zw.Multiply(rho);
+
+ // z_d = r_(jk+i) + z_w
+ residuals.Add(zw, zd);
+
+ // FOR (s = 1, ... i - 1)
+ for (var s = 0; s < i - 1; s++)
+ {
+ // beta^(jk+i)_(jk+s) = -q^T_s+1 z_d / c_(jk+s)
+ beta = -_startingVectors[s + 1].DotProduct(zd) / c[s];
+
+ // z_d = z_d + beta^(jk+i)_(jk+s) * d_(jk+s)
+ d[s].Multiply(beta, mult);
+ zd.Add(mult);
+
+ // z_g = z_g + beta^(jk+i)_(jk+s) * g_(jk+s)
+ g[s].Multiply(beta, mult);
+ zg.Add(mult);
+ }
+
+ // 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].AlmostEqual(0, 1))
+ {
+ throw new Exception("Iterative solver experience a numerical break down");
+ }
+
+ // alpha_(jk+i+1) = q^T_(i+1) u_(jk+i) / c_(jk+i)
+ alpha = _startingVectors[i + 1].DotProduct(u) / c[i];
+
+ // u_(jk+i+1) = u_(jk+i) - alpha_(jk+i+1) d_(jk+i)
+ d[i].Multiply(-alpha, mult);
+ u.Add(mult);
+
+ // SOLVE M g~_(jk+i) = g_(jk+i)
+ _preconditioner.Approximate(g[i], gtemp);
+
+ // x_(jk+i+1) = x_(jk+i) + rho_(j+1) alpha_(jk+i+1) g~_(jk+i)
+ gtemp.Multiply(rho * alpha, mult);
+ xtemp.Add(mult);
+
+ // w_(jk+i) = A g~_(jk+i)
+ matrix.Multiply(gtemp, w[i]);
+
+ // r_(jk+i+1) = r_(jk+i) - rho_(j+1) alpha_(jk+i+1) w_(jk+i)
+ w[i].Multiply(-rho * alpha, mult);
+ residuals.Add(mult);
+
+ // 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);
+ }
+
+ ///
+ /// Gets the number of starting vectors to create
+ ///
+ /// Maximum number
+ /// Number of variables
+ /// Number of starting vectors to create
+ 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));
+ }
+
+ ///
+ /// Returns an array of starting vectors.
+ ///
+ /// The maximum number of starting vectors that should be created.
+ /// The number of variables.
+ ///
+ /// An array with starting vectors. The array will never be larger than the
+ /// but it may be smaller if
+ /// the is smaller than
+ /// the .
+ ///
+ private static IList 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 matrix = new DenseMatrix(numberOfVariables, count);
+ for (var i = 0; i < matrix.ColumnCount; i++)
+ {
+ var samples = distribution.Samples().Take(matrix.RowCount).ToArray();
+
+ // 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();
+ 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));
+ }
+
+ return result;
+ }
+
+ ///
+ /// Create random vecrors array
+ ///
+ /// Number of vectors
+ /// Size of each vector
+ /// Array of random vectors
+ private static Vector[] CreateVectorArray(int arraySize, int vectorSize)
+ {
+ var result = new Vector[arraySize];
+ for (var i = 0; i < result.Length; i++)
+ {
+ result[i] = new DenseVector(vectorSize);
+ }
+
+ return result;
+ }
+
+ ///
+ /// Calculates the true residual of the matrix equation Ax = b according to: residual = b - Ax
+ ///
+ /// Source A.
+ /// Residual data.
+ /// x data.
+ /// b data.
+ private static void CalculateTrueResidual(Matrix matrix, Vector residual, Vector x, Vector b)
+ {
+ // -Ax = residual
+ matrix.Multiply(x, residual);
+ residual.Multiply(-1);
+
+ // residual + b
+ residual.Add(b);
+ }
+
+ ///
+ /// Determine if calculation should continue
+ ///
+ /// Number of iterations passed
+ /// Result .
+ /// Source .
+ /// Residual .
+ /// true if continue, otherwise false
+ private bool ShouldContinue(int iterationNumber, Vector result, Vector source, Vector residuals)
+ {
+ if (_hasBeenStopped)
+ {
+ _iterator.IterationCancelled();
+ return true;
+ }
+
+ _iterator.DetermineStatus(iterationNumber, result, source, residuals);
+ var status = _iterator.Status;
+
+ // We stop if either:
+ // - the user has stopped the calculation
+ // - the calculation needs to be stopped from a numerical point of view (divergence, convergence etc.)
+ return (!status.TerminatesCalculation) && (!_hasBeenStopped);
+ }
+
+ ///
+ /// Solves the matrix equation AX = B, where A is the coefficient matrix, B is the
+ /// solution matrix and X is the unknown matrix.
+ ///
+ /// The coefficient matrix, A.
+ /// The solution matrix, B.
+ /// The result matrix, X.
+ public Matrix Solve(Matrix matrix, Matrix 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;
+ }
+
+ ///
+ /// Solves the matrix equation AX = B, where A is the coefficient matrix, B is the
+ /// solution matrix and X is the unknown matrix.
+ ///
+ /// The coefficient matrix, A.
+ /// The solution matrix, B.
+ /// The result matrix, X
+ public void Solve(Matrix matrix, Matrix input, Matrix result)
+ {
+ if (matrix == null)
+ {
+ throw new ArgumentNullException("matrix");
+ }
+
+ if (input == null)
+ {
+ throw new ArgumentNullException("input");
+ }
+
+ if (result == null)
+ {
+ throw new ArgumentNullException("result");
+ }
+
+ if (matrix.RowCount != input.RowCount || input.RowCount != result.RowCount || input.ColumnCount != result.ColumnCount)
+ {
+ throw 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);
+ }
+ }
+ }
+ }
+}
diff --git a/src/Numerics/LinearAlgebra/Double/Solvers/Iterative/TFQMR.cs b/src/Numerics/LinearAlgebra/Double/Solvers/Iterative/TFQMR.cs
new file mode 100644
index 00000000..b06df5a9
--- /dev/null
+++ b/src/Numerics/LinearAlgebra/Double/Solvers/Iterative/TFQMR.cs
@@ -0,0 +1,532 @@
+//
+// 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.
+//
+
+namespace MathNet.Numerics.LinearAlgebra.Double.Solvers.Iterative
+{
+ using System;
+ using Preconditioners;
+ using Properties;
+ using Status;
+
+ ///
+ /// A Transpose Free Quasi-Minimal Residual (TFQMR) iterative matrix solver.
+ ///
+ ///
+ ///
+ /// The TFQMR algorithm was taken from:
+ /// Iterative methods for sparse linear systems.
+ ///
+ /// Yousef Saad
+ ///
+ /// Algorithm is described in Chapter 7, section 7.4.3, page 219
+ ///
+ ///
+ /// The example code below provides an indication of the possible use of the
+ /// solver.
+ ///
+ ///
+ public sealed class TFQMR : IIterativeSolver
+ {
+ ///
+ /// The status used if there is no status, i.e. the solver hasn't run yet and there is no
+ /// iterator.
+ ///
+ private static readonly ICalculationStatus DefaultStatus = new CalculationIndetermined();
+
+ ///
+ /// The preconditioner that will be used. Can be set to , in which case the default
+ /// pre-conditioner will be used.
+ ///
+ private IPreConditioner _preconditioner;
+
+ ///
+ /// The iterative process controller.
+ ///
+ private IIterator _iterator;
+
+ ///
+ /// Indicates if the user has stopped the solver.
+ ///
+ private bool _hasBeenStopped;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// When using this constructor the solver will use the with
+ /// the standard settings and a default preconditioner.
+ ///
+ public TFQMR() : this(null, null)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ ///
+ /// When using this constructor the solver will use a default preconditioner.
+ ///
+ ///
+ /// The main advantages of using a user defined are:
+ ///
+ /// It is possible to set the desired convergence limits.
+ ///
+ /// It is possible to check the reason for which the solver finished
+ /// the iterative procedure by calling the property.
+ ///
+ ///
+ ///
+ ///
+ /// The that will be used to monitor the iterative process.
+ public TFQMR(IIterator iterator) : this(null, iterator)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// When using this constructor the solver will use the with
+ /// the standard settings.
+ ///
+ /// The that will be used to precondition the matrix equation.
+ public TFQMR(IPreConditioner preconditioner) : this(preconditioner, null)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ ///
+ /// The main advantages of using a user defined are:
+ ///
+ /// It is possible to set the desired convergence limits.
+ ///
+ /// It is possible to check the reason for which the solver finished
+ /// the iterative procedure by calling the property.
+ ///
+ ///
+ ///
+ ///
+ /// The that will be used to precondition the matrix equation.
+ /// The that will be used to monitor the iterative process.
+ public TFQMR(IPreConditioner preconditioner, IIterator iterator)
+ {
+ _iterator = iterator;
+ _preconditioner = preconditioner;
+ }
+
+ ///
+ /// Sets the that will be used to precondition the iterative process.
+ ///
+ /// The preconditioner.
+ public void SetPreconditioner(IPreConditioner preconditioner)
+ {
+ _preconditioner = preconditioner;
+ }
+
+ ///
+ /// Sets the that will be used to track the iterative process.
+ ///
+ /// The iterator.
+ public void SetIterator(IIterator iterator)
+ {
+ _iterator = iterator;
+ }
+
+ ///
+ /// Gets the status of the iteration once the calculation is finished.
+ ///
+ public ICalculationStatus IterationResult
+ {
+ get
+ {
+ return (_iterator != null) ? _iterator.Status : DefaultStatus;
+ }
+ }
+
+ ///
+ /// Stops the solve process.
+ ///
+ ///
+ /// Note that it may take an indetermined amount of time for the solver to actually stop the process.
+ ///
+ public void StopSolve()
+ {
+ _hasBeenStopped = true;
+ }
+
+ ///
+ /// Solves the matrix equation Ax = b, where A is the coefficient matrix, b is the
+ /// solution vector and x is the unknown vector.
+ ///
+ /// The coefficient matrix, A.
+ /// The solution vector, b.
+ /// The result vector, x.
+ public Vector Solve(Matrix matrix, Vector vector)
+ {
+ if (vector == null)
+ {
+ throw new ArgumentNullException();
+ }
+
+ Vector result = new DenseVector(matrix.RowCount);
+ Solve(matrix, vector, result);
+ return result;
+ }
+
+ ///
+ /// Solves the matrix equation Ax = b, where A is the coefficient matrix, b is the
+ /// solution vector and x is the unknown vector.
+ ///
+ /// The coefficient matrix, A.
+ /// The solution vector, b
+ /// The result vector, x
+ public void Solve(Matrix matrix, Vector input, Vector result)
+ {
+ // If we were stopped before, we are no longer
+ // We're doing this at the start of the method to ensure
+ // that we can use these fields immediately.
+ _hasBeenStopped = false;
+
+ // Error checks
+ if (matrix == null)
+ {
+ throw new ArgumentNullException("matrix");
+ }
+
+ if (matrix.RowCount != matrix.ColumnCount)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixSquare, "matrix");
+ }
+
+ if (input == null)
+ {
+ throw new ArgumentNullException("input");
+ }
+
+ if (result == null)
+ {
+ throw new ArgumentNullException("result");
+ }
+
+ if (result.Count != input.Count)
+ {
+ throw new ArgumentException(Resources.ArgumentVectorsSameLength);
+ }
+
+ // Initialize the solver fields
+ // Set the convergence monitor
+ if (_iterator == null)
+ {
+ _iterator = Iterator.CreateDefault();
+ }
+
+ if (_preconditioner == null)
+ {
+ _preconditioner = new UnitPreconditioner();
+ }
+
+ _preconditioner.Initialize(matrix);
+
+ var d = new DenseVector(input.Count);
+ var r = new DenseVector(input);
+
+ var uodd = new DenseVector(input.Count);
+ var ueven = new DenseVector(input.Count);
+
+ var v = new DenseVector(input.Count);
+ var pseudoResiduals = new DenseVector(input);
+
+ var x = new DenseVector(input.Count);
+ var yodd = new DenseVector(input.Count);
+ var yeven = new DenseVector(input);
+
+ // Temp vectors
+ var temp = new DenseVector(input.Count);
+ var mult = new DenseVector(input.Count);
+
+ // Initialize
+ var startNorm = input.Norm(2);
+
+ // Define the scalars
+ double alpha = 0;
+ double eta = 0;
+ double theta = 0;
+
+ var tau = startNorm;
+ var rho = tau * tau;
+
+ // Calculate the initial values for v
+ // M temp = yEven
+ _preconditioner.Approximate(yeven, temp);
+
+ // v = A temp
+ matrix.Multiply(temp, v);
+
+ // Set uOdd
+ v.CopyTo(ueven);
+
+ // Start the iteration
+ var iterationNumber = 0;
+ while (ShouldContinue(iterationNumber, result, input, pseudoResiduals))
+ {
+ // First part of the step, the even bit
+ if (IsEven(iterationNumber))
+ {
+ // sigma = (v, r)
+ var sigma = v.DotProduct(r);
+ if (sigma.AlmostEqual(0, 1))
+ {
+ // FAIL HERE
+ _iterator.IterationCancelled();
+ break;
+ }
+
+ // alpha = rho / sigma
+ alpha = rho / sigma;
+
+ // yOdd = yEven - alpha * v
+ v.Multiply(-alpha, mult);
+ yeven.Add(mult, yodd);
+
+ // Solve M temp = yOdd
+ _preconditioner.Approximate(yodd, temp);
+
+ // uOdd = A temp
+ matrix.Multiply(temp, uodd);
+ }
+
+ // The intermediate step which is equal for both even and
+ // odd iteration steps.
+ // Select the correct vector
+ var uinternal = IsEven(iterationNumber) ? ueven : uodd;
+ var yinternal = IsEven(iterationNumber) ? yeven : yodd;
+
+ // pseudoResiduals = pseudoResiduals - alpha * uOdd
+ uinternal.Multiply(-alpha, mult);
+ pseudoResiduals.Add(mult);
+
+ // d = yOdd + theta * theta * eta / alpha * d
+ d.Multiply(theta * theta * eta / alpha, temp);
+ yinternal.Add(temp, d);
+
+ // theta = ||pseudoResiduals||_2 / tau
+ theta = pseudoResiduals.Norm(2) / tau;
+ var c = 1 / Math.Sqrt(1 + (theta * theta));
+
+ // tau = tau * theta * c
+ tau *= theta * c;
+
+ // eta = c^2 * alpha
+ eta = c * c * alpha;
+
+ // x = x + eta * d
+ d.Multiply(eta, mult);
+ x.Add(mult);
+
+ // Check convergence and see if we can bail
+ if (!ShouldContinue(iterationNumber, result, input, pseudoResiduals))
+ {
+ // Calculate the real values
+ _preconditioner.Approximate(x, result);
+
+ // Calculate the true residual. Use the temp vector for that
+ // so that we don't pollute the pseudoResidual vector for no
+ // good reason.
+ CalculateTrueResidual(matrix, temp, result, input);
+
+ // Now recheck the convergence
+ if (!ShouldContinue(iterationNumber, result, input, temp))
+ {
+ // We're all good now.
+ return;
+ }
+ }
+
+ // The odd step
+ if (!IsEven(iterationNumber))
+ {
+ if (rho.AlmostEqual(0, 1))
+ {
+ // FAIL HERE
+ _iterator.IterationCancelled();
+ break;
+ }
+
+ var rhoNew = pseudoResiduals.DotProduct(r);
+ var beta = rhoNew / rho;
+
+ // Update rho for the next loop
+ rho = rhoNew;
+
+ // yOdd = pseudoResiduals + beta * yOdd
+ yodd.Multiply(beta, mult);
+ pseudoResiduals.Add(mult, yeven);
+
+ // Solve M temp = yOdd
+ _preconditioner.Approximate(yeven, temp);
+
+ // uOdd = A temp
+ matrix.Multiply(temp, ueven);
+
+ // v = uEven + beta * (uOdd + beta * v)
+ v.Multiply(beta, mult);
+ uodd.Add(mult, temp);
+
+ temp.Multiply(beta, mult);
+ ueven.Add(mult, v);
+ }
+
+ // Calculate the real values
+ _preconditioner.Approximate(x, result);
+
+ iterationNumber++;
+ }
+ }
+
+ ///
+ /// Calculates the true residual of the matrix equation Ax = b according to: residual = b - Ax
+ ///
+ /// Instance of the A.
+ /// Residual values in .
+ /// Instance of the x.
+ /// Instance of the b.
+ private static void CalculateTrueResidual(Matrix matrix, Vector residual, Vector x, Vector b)
+ {
+ // -Ax = residual
+ matrix.Multiply(x, residual);
+ residual.Multiply(-1);
+
+ // residual + b
+ residual.Add(b);
+ }
+
+ ///
+ /// Determine if calculation should continue
+ ///
+ /// Number of iterations passed
+ /// Result .
+ /// Source .
+ /// Residual .
+ /// true if continue, otherwise false
+ private bool ShouldContinue(int iterationNumber, Vector result, Vector source, Vector residuals)
+ {
+ if (_hasBeenStopped)
+ {
+ _iterator.IterationCancelled();
+ return true;
+ }
+
+ _iterator.DetermineStatus(iterationNumber, result, source, residuals);
+ var status = _iterator.Status;
+
+ // We stop if either:
+ // - the user has stopped the calculation
+ // - the calculation needs to be stopped from a numerical point of view (divergence, convergence etc.)
+ return (!status.TerminatesCalculation) && (!_hasBeenStopped);
+ }
+
+ ///
+ /// Is even?
+ ///
+ /// Number to check
+ /// true if even, otherwise false
+ private static bool IsEven(int number)
+ {
+ return number % 2 == 0;
+ }
+
+ ///
+ /// Solves the matrix equation AX = B, where A is the coefficient matrix, B is the
+ /// solution matrix and X is the unknown matrix.
+ ///
+ /// The coefficient matrix, A.
+ /// The solution matrix, B.
+ /// The result matrix, X.
+ public Matrix Solve(Matrix matrix, Matrix 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;
+ }
+
+ ///
+ /// Solves the matrix equation AX = B, where A is the coefficient matrix, B is the
+ /// solution matrix and X is the unknown matrix.
+ ///
+ /// The coefficient matrix, A.
+ /// The solution matrix, B.
+ /// The result matrix, X
+ public void Solve(Matrix matrix, Matrix input, Matrix result)
+ {
+ if (matrix == null)
+ {
+ throw new ArgumentNullException("matrix");
+ }
+
+ if (input == null)
+ {
+ throw new ArgumentNullException("input");
+ }
+
+ if (result == null)
+ {
+ throw new ArgumentNullException("result");
+ }
+
+ if (matrix.RowCount != input.RowCount || input.RowCount != result.RowCount || input.ColumnCount != result.ColumnCount)
+ {
+ throw 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);
+ }
+ }
+ }
+ }
+}
diff --git a/src/Numerics/LinearAlgebra/Double/Solvers/Iterator.cs b/src/Numerics/LinearAlgebra/Double/Solvers/Iterator.cs
new file mode 100644
index 00000000..1caad4ef
--- /dev/null
+++ b/src/Numerics/LinearAlgebra/Double/Solvers/Iterator.cs
@@ -0,0 +1,325 @@
+//
+// 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.
+//
+
+namespace MathNet.Numerics.LinearAlgebra.Double.Solvers
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using Properties;
+ using Status;
+ using StopCriterium;
+
+ ///
+ /// An iterator that is used to check if an iterative calculation should continue or stop.
+ ///
+ public sealed class Iterator : IIterator
+ {
+ ///
+ /// The default status for the iterator.
+ ///
+ private static readonly ICalculationStatus DefaultStatus = new CalculationIndetermined();
+
+ ///
+ /// Creates a default iterator with all the objects.
+ ///
+ /// A new object.
+ public static IIterator CreateDefault()
+ {
+ var iterator = new Iterator();
+ iterator.Add(new FailureStopCriterium());
+ iterator.Add(new DivergenceStopCriterium());
+ iterator.Add(new IterationCountStopCriterium());
+ iterator.Add(new ResidualStopCriterium());
+
+ return iterator;
+ }
+
+ ///
+ /// The collection that holds all the stop criteria and the flag indicating if they should be added
+ /// to the child iterators.
+ ///
+ private readonly Dictionary _stopCriterias = new Dictionary();
+
+ ///
+ /// The status of the iterator.
+ ///
+ private ICalculationStatus _status = DefaultStatus;
+
+ ///
+ /// Indicates if the iteration was cancelled.
+ ///
+ private bool _wasIterationCancelled;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public Iterator() : this(null)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class with the specified stop criteria.
+ ///
+ ///
+ /// 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.
+ ///
+ /// Thrown if contains multiple stop criteria of the same type.
+ public Iterator(IEnumerable stopCriteria)
+ {
+ // Add the stop criteria
+ if (stopCriteria == null)
+ {
+ return;
+ }
+
+ foreach (var stopCriterium in stopCriteria.Where(stopCriterium => stopCriterium != null))
+ {
+ Add(stopCriterium);
+ }
+ }
+
+ ///
+ /// Adds an to the internal collection of stop-criteria. Only a
+ /// single stop criterium of each type can be stored.
+ ///
+ /// The stop criterium to add.
+ /// Thrown if is .
+ ///
+ /// Thrown if is of the same type as an already
+ /// stored criterium.
+ ///
+ public void Add(IIterationStopCriterium 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);
+ }
+
+ ///
+ /// Removes the from the internal collection.
+ ///
+ /// The stop criterium that must be removed.
+ public void Remove(IIterationStopCriterium stopCriterium)
+ {
+ if (stopCriterium == null)
+ {
+ throw new ArgumentNullException("stopCriterium");
+ }
+
+ if (!_stopCriterias.ContainsKey(stopCriterium.GetType()))
+ {
+ return;
+ }
+
+ // Remove from the collection
+ _stopCriterias.Remove(stopCriterium.GetType());
+ }
+
+ ///
+ /// Indicates if the specific stop criterium is stored by the .
+ ///
+ /// The stop criterium.
+ /// true if the contains the stop criterium; otherwise false.
+ public bool Contains(IIterationStopCriterium stopCriterium)
+ {
+ return stopCriterium != null && _stopCriterias.ContainsKey(stopCriterium.GetType());
+ }
+
+ ///
+ /// Gets the number of stored stop criteria.
+ ///
+ /// Used for testing only.
+ internal int NumberOfCriteria
+ {
+ get
+ {
+ return _stopCriterias.Count;
+ }
+ }
+
+ ///
+ /// Gets an IEnumerator that enumerates over all the stored stop criteria.
+ ///
+ /// Used for testing only.
+ internal IEnumerator StoredStopCriteria
+ {
+ get
+ {
+ return _stopCriterias.Select(criterium => criterium.Value).GetEnumerator();
+ }
+ }
+
+ ///
+ /// Indicates to the iterator that the iterative process has been cancelled.
+ ///
+ ///
+ /// Does not reset the stop-criteria.
+ ///
+ public void IterationCancelled()
+ {
+ _wasIterationCancelled = true;
+ _status = new CalculationCancelled();
+ }
+
+ ///
+ /// Determines the status of the iterative calculation based on the stop criteria stored
+ /// by the current IIterator. Result is set into Status field.
+ ///
+ /// The number of iterations that have passed so far.
+ /// The vector containing the current solution values.
+ /// The right hand side vector.
+ /// The vector containing the current residual vectors.
+ ///
+ /// 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.
+ ///
+ public void DetermineStatus(int iterationNumber, Vector solutionVector, Vector sourceVector, Vector 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();
+ }
+ }
+
+ ///
+ /// Gets the current calculation status.
+ ///
+ public ICalculationStatus Status
+ {
+ get
+ {
+ return _status;
+ }
+ }
+
+ ///
+ /// Resets the to the pre-calculation state.
+ ///
+ 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();
+ }
+ }
+
+ ///
+ /// Creates a deep clone of the current iterator.
+ ///
+ /// The deep clone of the current iterator.
+ public IIterator Clone()
+ {
+ var stopCriteria = _stopCriterias.Select(pair => pair.Value).Select(stopCriterium => (IIterationStopCriterium)stopCriterium.Clone()).ToList();
+ return new Iterator(stopCriteria);
+ }
+
+ #if !SILVERLIGHT
+ ///
+ /// Creates a deep clone of the current iterator.
+ ///
+ /// The deep clone of the current iterator.
+ object ICloneable.Clone()
+ {
+ return Clone();
+ }
+ #endif
+ }
+}
diff --git a/src/Numerics/LinearAlgebra/Double/Solvers/Preconditioners/Diagonal.cs b/src/Numerics/LinearAlgebra/Double/Solvers/Preconditioners/Diagonal.cs
new file mode 100644
index 00000000..9dccb70a
--- /dev/null
+++ b/src/Numerics/LinearAlgebra/Double/Solvers/Preconditioners/Diagonal.cs
@@ -0,0 +1,148 @@
+//
+// 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.
+//
+
+namespace MathNet.Numerics.LinearAlgebra.Double.Solvers.Preconditioners
+{
+ using System;
+ using Properties;
+
+ ///
+ /// A diagonal preconditioner. The preconditioner uses the inverse
+ /// of the matrix diagonal as preconditioning values.
+ ///
+ public sealed class Diagonal : IPreConditioner
+ {
+ ///
+ /// The inverse of the matrix diagonal.
+ ///
+ private double[] _inverseDiagonals;
+
+ ///
+ /// Returns the decomposed matrix diagonal.
+ ///
+ /// The matrix diagonal.
+ 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;
+ }
+
+ ///
+ /// Initializes the preconditioner and loads the internal data structures.
+ ///
+ ///
+ /// The upon which this preconditioner is based.
+ /// If is .
+ /// If is not a square matrix.
+ public void Initialize(Matrix matrix)
+ {
+ if (matrix == null)
+ {
+ throw new ArgumentNullException("matrix");
+ }
+
+ if (matrix.RowCount != matrix.ColumnCount)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixSquare, "matrix");
+ }
+
+ _inverseDiagonals = new double[matrix.RowCount];
+ for (var i = 0; i < matrix.RowCount; i++)
+ {
+ _inverseDiagonals[i] = 1 / matrix[i, i];
+ }
+ }
+
+ ///
+ /// Approximates the solution to the matrix equation Ax = b.
+ ///
+ /// The right hand side vector.
+ /// The left hand side vector.
+ public Vector Approximate(Vector 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 result = new DenseVector(rhs.Count);
+ Approximate(rhs, result);
+ return result;
+ }
+
+ ///
+ /// Approximates the solution to the matrix equation Ax = b.
+ ///
+ /// The right hand side vector.
+ /// The left hand side vector. Also known as the result vector.
+ public void Approximate(Vector rhs, Vector 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];
+ }
+ }
+ }
+}
diff --git a/src/Numerics/LinearAlgebra/Double/Solvers/Preconditioners/IPreConditioner.cs b/src/Numerics/LinearAlgebra/Double/Solvers/Preconditioners/IPreConditioner.cs
new file mode 100644
index 00000000..e6534041
--- /dev/null
+++ b/src/Numerics/LinearAlgebra/Double/Solvers/Preconditioners/IPreConditioner.cs
@@ -0,0 +1,73 @@
+//
+// 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.
+//
+
+namespace MathNet.Numerics.LinearAlgebra.Double.Solvers.Preconditioners
+{
+ ///
+ /// The base interface for preconditioner classes.
+ ///
+ ///
+ ///
+ /// Preconditioners are used by iterative solvers to improve the convergence
+ /// speed of the solving process. Increase in convergence speed
+ /// is related to the number of iterations necessary to get a converged solution.
+ /// So while in general the use of a preconditioner means that the iterative
+ /// solver will perform fewer iterations it does not guarantee that the actual
+ /// solution time decreases given that some preconditioners can be expensive to
+ /// setup and run.
+ ///
+ ///
+ /// Note that in general changes to the matrix will invalidate the preconditioner
+ /// if the changes occur after creating the preconditioner.
+ ///
+ ///
+ public interface IPreConditioner
+ {
+ ///
+ /// Initializes the preconditioner and loads the internal data structures.
+ ///
+ /// The matrix on which the preconditioner is based.
+ void Initialize(Matrix matrix);
+
+ ///
+ /// Approximates the solution to the matrix equation Mx = b.
+ ///
+ /// The right hand side vector.
+ /// The left hand side vector.
+ Vector Approximate(Vector rhs);
+
+ ///
+ /// Approximates the solution to the matrix equation Mx = b.
+ ///
+ /// The right hand side vector.
+ /// The left hand side vector. Also known as the result vector.
+ void Approximate(Vector rhs, Vector lhs);
+ }
+}
diff --git a/src/Numerics/LinearAlgebra/Double/Solvers/Preconditioners/Ilutp.cs b/src/Numerics/LinearAlgebra/Double/Solvers/Preconditioners/Ilutp.cs
new file mode 100644
index 00000000..8938e19b
--- /dev/null
+++ b/src/Numerics/LinearAlgebra/Double/Solvers/Preconditioners/Ilutp.cs
@@ -0,0 +1,727 @@
+//
+// 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.
+//
+
+namespace MathNet.Numerics.LinearAlgebra.Double.Solvers.Preconditioners
+{
+ using System;
+ using System.Collections.Generic;
+ using Properties;
+
+ ///
+ /// 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.
+ ///
+ ///
+ /// The ILUTP-Mem algorithm was taken from:
+ /// ILUTP_Mem: a Space-Efficient Incomplete LU Preconditioner
+ ///
+ /// Tzu-Yi Chen, Department of Mathematics and Computer Science,
+ /// Pomona College, Claremont CA 91711, USA
+ /// Published in:
+ /// Lecture Notes in Computer Science
+ /// Volume 3046 / 2004
+ /// pp. 20 - 28
+ /// Algorithm is described in Section 2, page 22
+ ///
+ public sealed class Ilutp : IPreConditioner
+ {
+ ///
+ /// The default fill level.
+ ///
+ public const double DefaultFillLevel = 200.0;
+
+ ///
+ /// The default drop tolerance.
+ ///
+ public const double DefaultDropTolerance = 0.0001;
+
+ ///
+ /// The decomposed upper triangular matrix.
+ ///
+ private SparseMatrix _upper;
+
+ ///
+ /// The decomposed lower triangular matrix.
+ ///
+ private SparseMatrix _lower;
+
+ ///
+ /// The array containing the pivot values.
+ ///
+ private int[] _pivots;
+
+ ///
+ /// The fill level.
+ ///
+ private double _fillLevel = DefaultFillLevel;
+
+ ///
+ /// The drop tolerance.
+ ///
+ private double _dropTolerance = DefaultDropTolerance;
+
+ ///
+ /// The pivot tolerance.
+ ///
+ private double _pivotTolerance;
+
+ ///
+ /// Initializes a new instance of the class with the default settings.
+ ///
+ public Ilutp()
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class with the specified settings.
+ ///
+ ///
+ /// 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.
+ ///
+ ///
+ /// 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.
+ ///
+ ///
+ /// The pivot tolerance which indicates at what level pivoting will take place. A
+ /// value of 0.0 means that no pivoting will take place.
+ ///
+ 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;
+ }
+
+ ///
+ /// 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.
+ ///
+ ///
+ ///
+ /// 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.
+ ///
+ ///
+ /// Note that any changes to the FillLevel after creating the preconditioner
+ /// will invalidate the created preconditioner and will require a re-initialization of
+ /// the preconditioner.
+ ///
+ ///
+ /// Thrown if a negative value is provided.
+ public double FillLevel
+ {
+ get
+ {
+ return _fillLevel;
+ }
+
+ set
+ {
+ if (value < 0)
+ {
+ throw new ArgumentOutOfRangeException("Value");
+ }
+
+ _fillLevel = value;
+ }
+ }
+
+ ///
+ /// 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.
+ ///
+ ///
+ ///
+ /// 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.
+ ///
+ ///
+ /// Note that any changes to the DropTolerance after creating the preconditioner
+ /// will invalidate the created preconditioner and will require a re-initialization of
+ /// the preconditioner.
+ ///
+ ///
+ /// Thrown if a negative value is provided.
+ public double DropTolerance
+ {
+ get
+ {
+ return _dropTolerance;
+ }
+
+ set
+ {
+ if (value < 0)
+ {
+ throw new ArgumentOutOfRangeException("Value");
+ }
+
+ _dropTolerance = value;
+ }
+ }
+
+ ///
+ /// 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.
+ ///
+ ///
+ ///
+ /// 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 row(i,j) > row(i,i) / PivotTolerance for
+ /// any j that is not equal to i.
+ ///
+ ///
+ /// Note that any changes to the PivotTolerance after creating the preconditioner
+ /// will invalidate the created preconditioner and will require a re-initialization of
+ /// the preconditioner.
+ ///
+ ///
+ /// Thrown if a negative value is provided.
+ public double PivotTolerance
+ {
+ get
+ {
+ return _pivotTolerance;
+ }
+
+ set
+ {
+ if (value < 0)
+ {
+ throw new ArgumentOutOfRangeException("Value");
+ }
+
+ _pivotTolerance = value;
+ }
+ }
+
+ ///
+ /// Returns the upper triagonal matrix that was created during the LU decomposition.
+ ///
+ ///
+ /// This method is used for debugging purposes only and should normally not be used.
+ ///
+ /// A new matrix containing the upper triagonal elements.
+ internal Matrix UpperTriangle()
+ {
+ return _upper.Clone();
+ }
+
+ ///
+ /// Returns the lower triagonal matrix that was created during the LU decomposition.
+ ///
+ ///
+ /// This method is used for debugging purposes only and should normally not be used.
+ ///
+ /// A new matrix containing the lower triagonal elements.
+ internal Matrix LowerTriangle()
+ {
+ return _lower.Clone();
+ }
+
+ ///
+ /// 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.
+ ///
+ ///
+ /// This method is used for debugging purposes only and should normally not be used.
+ ///
+ /// The pivot array.
+ internal int[] Pivots()
+ {
+ var result = new int[_pivots.Length];
+ for (var i = 0; i < _pivots.Length; i++)
+ {
+ result[i] = _pivots[i];
+ }
+
+ return result;
+ }
+
+ ///
+ /// Initializes the preconditioner and loads the internal data structures.
+ ///
+ ///
+ /// The 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.
+ ///
+ /// If is .
+ /// If is not a square matrix.
+ public void Initialize(Matrix matrix)
+ {
+ if (matrix == null)
+ {
+ throw new ArgumentNullException("matrix");
+ }
+
+ if (matrix.RowCount != matrix.ColumnCount)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixSquare, "matrix");
+ }
+
+ var sparseMatrix = (matrix is SparseMatrix) ? matrix as SparseMatrix : new SparseMatrix(matrix.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 workVector = new DenseVector(sparseMatrix.RowCount);
+ Vector rowVector = new DenseVector(sparseMatrix.ColumnCount);
+ var indexSorting = new int[sparseMatrix.RowCount];
+
+ // spaceLeft = lfilNnz * nnz(A)
+ var spaceLeft = (int)_fillLevel * sparseMatrix.NonZerosCount;
+
+ // for i = 1, .. , n
+ for (var i = 0; i < sparseMatrix.RowCount; i++)
+ {
+ // w = a(i,*)
+ sparseMatrix.Row(i, workVector);
+
+ // pivot the row
+ PivotRow(workVector);
+ var vectorNorm = workVector.Norm(Double.PositiveInfinity);
+
+ // for j = 1, .. , i - 1)
+ for (var j = 0; j < i; j++)
+ {
+ // if (w(j) != 0)
+ // {
+ // w(j) = w(j) / a(j,j)
+ // if (w(j) < dropTol)
+ // {
+ // w(j) = 0;
+ // }
+ // if (w(j) != 0)
+ // {
+ // w = w - w(j) * U(j,*)
+ // }
+ if (workVector[j] != 0.0)
+ {
+ // Calculate the multiplication factors that go into the L matrix
+ workVector[j] = workVector[j] / _upper[j, j];
+ if (Math.Abs(workVector[j]) < _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]);
+ workVector.Subtract(rowVector);
+ }
+ }
+ }
+
+ // for j = i, .. ,n
+ for (var j = i; j < sparseMatrix.RowCount; j++)
+ {
+ // if w(j) <= dropTol * ||A(i,*)||
+ // {
+ // w(j) = 0
+ // }
+ if (Math.Abs(workVector[j]) <= _dropTolerance * vectorNorm)
+ {
+ workVector[j] = 0.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 (Math.Abs(workVector[i]) < _pivotTolerance * Math.Abs(workVector[indexSorting[0]]))
+ {
+ // swap columns of u (which holds the values of A in the
+ // sections that haven't been partitioned yet.
+ SwapColumns(_upper, i, indexSorting[0]);
+
+ // Update P
+ var temp = _pivots[i];
+ _pivots[i] = _pivots[indexSorting[0]];
+ _pivots[indexSorting[0]] = temp;
+ }
+ }
+
+ // spaceLeft = spaceLeft - nnz(L(i,:)) - nnz(U(i,:))
+ spaceLeft -= lowerNonZeroCount + upperNonZeroCount;
+ }
+
+ for (var i = 0; i < _lower.RowCount; i++)
+ {
+ _lower[i, i] = 1.0;
+ }
+ }
+
+ ///
+ /// Pivot elements in the according to internal pivot array
+ ///
+ /// Row to pivot in
+ private void PivotRow(Vector row)
+ {
+ var knownPivots = new Dictionary();
+
+ // 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;
+ }
+ }
+ }
+
+ ///
+ /// Was pivoting already performed
+ ///
+ /// Pivots already done
+ /// Current item to pivot
+ /// true if performed, otherwise false
+ private bool PivotMapFound(Dictionary 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;
+ }
+
+ ///
+ /// Swap columns in the
+ ///
+ /// Source .
+ /// First column index to swap
+ /// Second column index to swap
+ private static void SwapColumns(Matrix 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;
+ }
+ }
+
+ ///
+ /// Sort vector descending, not changing vector but placing sorted indicies to
+ ///
+ /// Start sort form
+ /// Sort till upper bound
+ /// Array with sorted vector indicies
+ /// Source
+ private static void FindLargestItems(int lowerBound, int upperBound, int[] sortedIndices, Vector 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);
+ }
+
+ ///
+ /// Approximates the solution to the matrix equation Ax = b.
+ ///
+ /// The right hand side vector.
+ /// The left hand side vector.
+ public Vector Approximate(Vector 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 result = new DenseVector(rhs.Count);
+ Approximate(rhs, result);
+ return result;
+ }
+
+ ///
+ /// Approximates the solution to the matrix equation Ax = b.
+ ///
+ /// The right hand side vector.
+ /// The left hand side vector. Also known as the result vector.
+ public void Approximate(Vector rhs, Vector 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 rowValues = new DenseVector(_lower.RowCount);
+ for (var i = 0; i < _lower.RowCount; i++)
+ {
+ _lower.Row(i, rowValues);
+
+ var sum = 0.0;
+ 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 = 0.0;
+ 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);
+ }
+
+ ///
+ /// Pivot elements in accoring to internal pivot array
+ ///
+ /// Source .
+ /// Result after pivoting.
+ private void Pivot(Vector vector, Vector result)
+ {
+ for (var i = 0; i < _pivots.Length; i++)
+ {
+ result[i] = vector[_pivots[i]];
+ }
+ }
+ }
+}
diff --git a/src/Numerics/LinearAlgebra/Double/Solvers/Preconditioners/IlutpElementSorter.cs b/src/Numerics/LinearAlgebra/Double/Solvers/Preconditioners/IlutpElementSorter.cs
new file mode 100644
index 00000000..802314a1
--- /dev/null
+++ b/src/Numerics/LinearAlgebra/Double/Solvers/Preconditioners/IlutpElementSorter.cs
@@ -0,0 +1,225 @@
+//
+// 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.
+//
+
+namespace MathNet.Numerics.LinearAlgebra.Double.Solvers.Preconditioners
+{
+ ///
+ /// An element sort algorithm for the class.
+ ///
+ ///
+ /// 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.
+ ///
+ internal class IlutpElementSorter
+ {
+ ///
+ /// Sorts the elements of the vector in decreasing
+ /// fashion. The vector itself is not affected.
+ ///
+ /// The starting index.
+ /// The stopping index.
+ /// An array that will contain the sorted indices once the algorithm finishes.
+ /// The that contains the values that need to be sorted.
+ public static void SortDoubleIndicesDecreasing(int lowerBound, int upperBound, int[] sortedIndices, Vector 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);
+ }
+
+ ///
+ /// Sorts the elements of the vector in decreasing
+ /// fashion using heap sort algorithm. The vector itself is not affected.
+ ///
+ /// The starting index.
+ /// The stopping index.
+ /// An array that will contain the sorted indices once the algorithm finishes.
+ /// The that contains the values that need to be sorted.
+ private static void HeapSortDoublesIndices(int lowerBound, int upperBound, int[] sortedIndices, Vector 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;
+ }
+ }
+
+ ///
+ /// Build heap for double indicies
+ ///
+ /// Root position
+ /// Lenght of
+ /// Indicies of
+ /// Target
+ private static void BuildDoubleIndexHeap(int start, int count, int[] sortedIndices, Vector values)
+ {
+ while (start >= 0)
+ {
+ SiftDoubleIndices(sortedIndices, values, start, count);
+ start -= 1;
+ }
+ }
+
+ ///
+ /// Sift double indicies
+ ///
+ /// Indicies of
+ /// Target
+ /// Root position
+ /// Lenght of
+ private static void SiftDoubleIndices(int[] sortedIndices, Vector values, int begin, int count)
+ {
+ var root = begin;
+ int child;
+
+ while (root * 2 < count)
+ {
+ child = root * 2;
+ if ((child < count - 1) && (values[sortedIndices[child]] > values[sortedIndices[child + 1]]))
+ {
+ child += 1;
+ }
+
+ if (values[sortedIndices[root]] <= values[sortedIndices[child]])
+ {
+ return;
+ }
+
+ Exchange(sortedIndices, root, child);
+ root = child;
+ }
+ }
+
+ ///
+ /// Sorts the given integers in a decreasing fashion.
+ ///
+ /// The values.
+ public static void SortIntegersDecreasing(int[] values)
+ {
+ HeapSortIntegers(values, values.Length);
+ }
+
+ ///
+ /// Sort the given integers in a decreasing fashion using heapsort algorithm
+ ///
+ /// Array of values to sort
+ /// Length of
+ 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;
+ }
+ }
+
+ ///
+ /// Build heap
+ ///
+ /// Target values array
+ /// Root position
+ /// Lenght of
+ private static void BuildHeap(int[] values, int start, int count)
+ {
+ while (start >= 0)
+ {
+ Sift(values, start, count);
+ start -= 1;
+ }
+ }
+
+ ///
+ /// Sift values
+ ///
+ /// Target value array
+ /// Root position
+ /// Lenght of
+ 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;
+ }
+ }
+ }
+
+ ///
+ /// Exchange values in array
+ ///
+ /// Target values array
+ /// First value to exchanghe
+ /// Second value to exchanghe
+ private static void Exchange(int[] values, int first, int second)
+ {
+ var t = values[first];
+ values[first] = values[second];
+ values[second] = t;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Numerics/LinearAlgebra/Double/Solvers/Preconditioners/IncompleteLU.cs b/src/Numerics/LinearAlgebra/Double/Solvers/Preconditioners/IncompleteLU.cs
new file mode 100644
index 00000000..3f0e55bf
--- /dev/null
+++ b/src/Numerics/LinearAlgebra/Double/Solvers/Preconditioners/IncompleteLU.cs
@@ -0,0 +1,258 @@
+//
+// 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.
+//
+
+namespace MathNet.Numerics.LinearAlgebra.Double.Solvers.Preconditioners
+{
+ using System;
+ using Properties;
+
+ ///
+ /// An incomplete, level 0, LU factorization preconditioner.
+ ///
+ ///
+ /// The ILU(0) algorithm was taken from:
+ /// Iterative methods for sparse linear systems
+ /// Yousef Saad
+ /// Algorithm is described in Chapter 10, section 10.3.2, page 275
+ ///
+ public sealed class IncompleteLU : IPreConditioner
+ {
+ ///
+ /// The matrix holding the lower (L) and upper (U) matrices. The
+ /// decomposition matrices are combined to reduce storage.
+ ///
+ private SparseMatrix _decompositionLU;
+
+ ///
+ /// Returns the upper triagonal matrix that was created during the LU decomposition.
+ ///
+ /// A new matrix containing the upper triagonal elements.
+ internal Matrix 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;
+ }
+
+ ///
+ /// Returns the lower triagonal matrix that was created during the LU decomposition.
+ ///
+ /// A new matrix containing the lower triagonal elements.
+ internal Matrix 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;
+ }
+
+ ///
+ /// Initializes the preconditioner and loads the internal data structures.
+ ///
+ /// The matrix upon which the preconditioner is based.
+ /// If is .
+ /// If is not a square matrix.
+ public void Initialize(Matrix 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]);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ ///
+ /// Approximates the solution to the matrix equation Ax = b.
+ ///
+ /// The right hand side vector.
+ /// The left hand side vector.
+ public Vector Approximate(Vector 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 result = new DenseVector(rhs.Count);
+ Approximate(rhs, result);
+ return result;
+ }
+
+ ///
+ /// Approximates the solution to the matrix equation Ax = b.
+ ///
+ /// The right hand side vector.
+ /// The left hand side vector. Also known as the result vector.
+ public void Approximate(Vector rhs, Vector 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 -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 = 0.0;
+ for (var j = _decompositionLU.RowCount - 1; j > i; j--)
+ {
+ sum += rowValues[j] * lhs[j];
+ }
+
+ lhs[i] = 1 / rowValues[i] * (lhs[i] - sum);
+ }
+ }
+ }
+}
diff --git a/src/Numerics/LinearAlgebra/Double/Solvers/Preconditioners/UnitPreconditioner.cs b/src/Numerics/LinearAlgebra/Double/Solvers/Preconditioners/UnitPreconditioner.cs
new file mode 100644
index 00000000..9f586f76
--- /dev/null
+++ b/src/Numerics/LinearAlgebra/Double/Solvers/Preconditioners/UnitPreconditioner.cs
@@ -0,0 +1,136 @@
+//
+// 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.
+//
+
+namespace MathNet.Numerics.LinearAlgebra.Double.Solvers.Preconditioners
+{
+ using System;
+ using Properties;
+
+ ///
+ /// A unit preconditioner. This preconditioner does not actually do anything
+ /// it is only used when running an without
+ /// a preconditioner.
+ ///
+ internal sealed class UnitPreconditioner : IPreConditioner
+ {
+ ///
+ /// The coefficient matrix on which this preconditioner operates.
+ /// Is used to check dimensions on the different vectors that are processed.
+ ///
+ private int _size;
+
+ ///
+ /// Initializes the preconditioner and loads the internal data structures.
+ ///
+ ///
+ /// The matrix upon which the preconditioner is based.
+ ///
+ /// If is .
+ /// If is not a square matrix.
+ public void Initialize(Matrix matrix)
+ {
+ if (matrix == null)
+ {
+ throw new ArgumentNullException("matrix");
+ }
+
+ if (matrix.RowCount != matrix.ColumnCount)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixSquare, "matrix");
+ }
+
+ _size = matrix.RowCount;
+ }
+
+ ///
+ /// Approximates the solution to the matrix equation Ax = b.
+ ///
+ /// The right hand side vector.
+ /// The left hand side vector. Also known as the result vector.
+ /// If is .
+ /// If is .
+ ///
+ ///
+ /// If and do not have the same size.
+ ///
+ ///
+ /// - or -
+ ///
+ ///
+ /// If the size of is different the number of rows of the coefficient matrix.
+ ///
+ ///
+ public void Approximate(Vector rhs, Vector 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);
+ }
+
+ ///
+ /// Approximates the solution to the matrix equation Ax = b.
+ ///
+ /// The right hand side vector.
+ /// The left hand side vector.
+ /// If is .
+ ///
+ /// If the size of is different the number of rows of the coefficient matrix.
+ ///
+ public Vector Approximate(Vector rhs)
+ {
+ if (rhs == null)
+ {
+ throw new ArgumentNullException("rhs");
+ }
+
+ if (rhs.Count != _size)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixDimensions);
+ }
+
+ Vector result = new DenseVector(rhs.Count);
+ Approximate(rhs, result);
+ return result;
+ }
+ }
+}
diff --git a/src/Numerics/LinearAlgebra/Double/Solvers/Status/CalculationCancelled.cs b/src/Numerics/LinearAlgebra/Double/Solvers/Status/CalculationCancelled.cs
new file mode 100644
index 00000000..6377a79f
--- /dev/null
+++ b/src/Numerics/LinearAlgebra/Double/Solvers/Status/CalculationCancelled.cs
@@ -0,0 +1,49 @@
+//
+// 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.
+//
+
+namespace MathNet.Numerics.LinearAlgebra.Double.Solvers.Status
+{
+ ///
+ /// Indicates that a calculation was cancelled by the user.
+ ///
+ public struct CalculationCancelled : ICalculationStatus
+ {
+ ///
+ /// Gets a value indicating whether current status warrants stopping the calculation.
+ ///
+ public bool TerminatesCalculation
+ {
+ get
+ {
+ return true;
+ }
+ }
+ }
+}
diff --git a/src/Numerics/LinearAlgebra/Double/Solvers/Status/CalculationConverged.cs b/src/Numerics/LinearAlgebra/Double/Solvers/Status/CalculationConverged.cs
new file mode 100644
index 00000000..a835e4d7
--- /dev/null
+++ b/src/Numerics/LinearAlgebra/Double/Solvers/Status/CalculationConverged.cs
@@ -0,0 +1,51 @@
+//
+// 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.
+//
+
+namespace MathNet.Numerics.LinearAlgebra.Double.Solvers.Status
+{
+ ///
+ /// Indicates that a calculation has converged to the desired convergence levels.
+ ///
+ public struct CalculationConverged : ICalculationStatus
+ {
+ ///
+ /// Gets a value indicating whether current status warrants stopping the calculation.
+ ///
+ public bool TerminatesCalculation
+ {
+ get
+ {
+ return true;
+ }
+ }
+
+ // TODO: CalculationConverged: Should we put the achieved residuals and convergence levels on here?
+ }
+}
diff --git a/src/Numerics/LinearAlgebra/Double/Solvers/Status/CalculationDiverged.cs b/src/Numerics/LinearAlgebra/Double/Solvers/Status/CalculationDiverged.cs
new file mode 100644
index 00000000..c2be337a
--- /dev/null
+++ b/src/Numerics/LinearAlgebra/Double/Solvers/Status/CalculationDiverged.cs
@@ -0,0 +1,51 @@
+//
+// 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.
+//
+
+namespace MathNet.Numerics.LinearAlgebra.Double.Solvers.Status
+{
+ ///
+ /// Indicates that the calculation diverged.
+ ///
+ public struct CalculationDiverged : ICalculationStatus
+ {
+ ///
+ /// Gets a value indicating whether current status warrants stopping the calculation.
+ ///
+ public bool TerminatesCalculation
+ {
+ get
+ {
+ return true;
+ }
+ }
+
+ // TODO: CalculationDiverged - Should we put the residuals on here?
+ }
+}
diff --git a/src/Numerics/LinearAlgebra/Double/Solvers/Status/CalculationFailure.cs b/src/Numerics/LinearAlgebra/Double/Solvers/Status/CalculationFailure.cs
new file mode 100644
index 00000000..344a1d7e
--- /dev/null
+++ b/src/Numerics/LinearAlgebra/Double/Solvers/Status/CalculationFailure.cs
@@ -0,0 +1,51 @@
+//
+// 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.
+//
+
+namespace MathNet.Numerics.LinearAlgebra.Double.Solvers.Status
+{
+ ///
+ /// Indicates that a calculation has failed for some reason.
+ ///
+ public struct CalculationFailure : ICalculationStatus
+ {
+ ///
+ /// Gets a value indicating whether current status warrants stopping the calculation.
+ ///
+ public bool TerminatesCalculation
+ {
+ get
+ {
+ return true;
+ }
+ }
+
+ // TODO: CalcuationFailure - Indicate why the calculation has failed?
+ }
+}
diff --git a/src/Numerics/LinearAlgebra/Double/Solvers/Status/CalculationIndetermined.cs b/src/Numerics/LinearAlgebra/Double/Solvers/Status/CalculationIndetermined.cs
new file mode 100644
index 00000000..cd5e58be
--- /dev/null
+++ b/src/Numerics/LinearAlgebra/Double/Solvers/Status/CalculationIndetermined.cs
@@ -0,0 +1,49 @@
+//
+// 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.
+//
+
+namespace MathNet.Numerics.LinearAlgebra.Double.Solvers.Status
+{
+ ///
+ /// Indicates that the state of the calculation is indetermined, not started or stopped.
+ ///
+ public struct CalculationIndetermined : ICalculationStatus
+ {
+ ///
+ /// Gets a value indicating whether current status warrants stopping the calculation.
+ ///
+ public bool TerminatesCalculation
+ {
+ get
+ {
+ return false;
+ }
+ }
+ }
+}
diff --git a/src/Numerics/LinearAlgebra/Double/Solvers/Status/CalculationRunning.cs b/src/Numerics/LinearAlgebra/Double/Solvers/Status/CalculationRunning.cs
new file mode 100644
index 00000000..19dbc8a9
--- /dev/null
+++ b/src/Numerics/LinearAlgebra/Double/Solvers/Status/CalculationRunning.cs
@@ -0,0 +1,51 @@
+//
+// 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.
+//
+
+namespace MathNet.Numerics.LinearAlgebra.Double.Solvers.Status
+{
+ ///
+ /// Indicates that the calculation is running and no results are yet known.
+ ///
+ public struct CalculationRunning : ICalculationStatus
+ {
+ ///
+ /// Gets a value indicating whether current status warrants stopping the calculation.
+ ///
+ public bool TerminatesCalculation
+ {
+ get
+ {
+ return false;
+ }
+ }
+
+ // TODO: CalculationRunning - Get current residuals?
+ }
+}
diff --git a/src/Numerics/LinearAlgebra/Double/Solvers/Status/CalculationStoppedWithoutConvergence.cs b/src/Numerics/LinearAlgebra/Double/Solvers/Status/CalculationStoppedWithoutConvergence.cs
new file mode 100644
index 00000000..e4a9c799
--- /dev/null
+++ b/src/Numerics/LinearAlgebra/Double/Solvers/Status/CalculationStoppedWithoutConvergence.cs
@@ -0,0 +1,52 @@
+//
+// 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.
+//
+
+namespace MathNet.Numerics.LinearAlgebra.Double.Solvers.Status
+{
+ ///
+ /// Indicates that the calculation has been stopped due to reaching the stopping
+ /// limits, but that convergence was not achieved.
+ ///
+ public struct CalculationStoppedWithoutConvergence : ICalculationStatus
+ {
+ ///
+ /// Gets a value indicating whether current status warrants stopping the calculation.
+ ///
+ public bool TerminatesCalculation
+ {
+ get
+ {
+ return true;
+ }
+ }
+
+ // TODO: Indicate which stopping limit was reached?
+ }
+}
diff --git a/src/Numerics/LinearAlgebra/Double/Solvers/Status/ICalculationStatus.cs b/src/Numerics/LinearAlgebra/Double/Solvers/Status/ICalculationStatus.cs
new file mode 100644
index 00000000..97b867e6
--- /dev/null
+++ b/src/Numerics/LinearAlgebra/Double/Solvers/Status/ICalculationStatus.cs
@@ -0,0 +1,43 @@
+//
+// 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.
+//
+
+namespace MathNet.Numerics.LinearAlgebra.Double.Solvers.Status
+{
+ ///
+ /// Defines the base interface for calculation status objects.
+ ///
+ public interface ICalculationStatus
+ {
+ ///
+ /// Gets a value indicating whether current status warrants stopping the calculation.
+ ///
+ bool TerminatesCalculation { get; }
+ }
+}
diff --git a/src/Numerics/LinearAlgebra/Double/Solvers/StopCriterium/DivergenceStopCriterium.cs b/src/Numerics/LinearAlgebra/Double/Solvers/StopCriterium/DivergenceStopCriterium.cs
new file mode 100644
index 00000000..98426ade
--- /dev/null
+++ b/src/Numerics/LinearAlgebra/Double/Solvers/StopCriterium/DivergenceStopCriterium.cs
@@ -0,0 +1,391 @@
+//
+// 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.
+//
+
+namespace MathNet.Numerics.LinearAlgebra.Double.Solvers.StopCriterium
+{
+ using System;
+ using System.Diagnostics;
+ using Status;
+
+ ///
+ /// Monitors an iterative calculation for signs of divergence.
+ ///
+ public sealed class DivergenceStopCriterium : IIterationStopCriterium
+ {
+ ///
+ /// Default value for the maximum relative increase that the
+ /// residual may experience before a divergence warning is issued.
+ ///
+ public const double DefaultMaximumRelativeIncrease = 0.08;
+
+ ///
+ /// Default value for the minimum number of iterations over which
+ /// the residual must grow before a divergence warning is issued.
+ ///
+ public const int DefaultMinimumNumberOfIterations = 10;
+
+ ///
+ /// Defines the default last iteration number. Set to -1 because iterations normally
+ /// start at 0.
+ ///
+ private const int DefaultLastIterationNumber = -1;
+
+ ///
+ /// The default status.
+ ///
+ private static readonly ICalculationStatus DefaultStatus = new CalculationIndetermined();
+
+ ///
+ /// The maximum relative increase the residual may experience without triggering a divergence warning.
+ ///
+ private double _maximumRelativeIncrease;
+
+ ///
+ /// The number of iterations over which a residual increase should be tracked before issuing a divergence warning.
+ ///
+ private int _minimumNumberOfIterations;
+
+ ///
+ /// The status of the calculation
+ ///
+ private ICalculationStatus _status = DefaultStatus;
+
+ ///
+ /// The array that holds the tracking information.
+ ///
+ private double[] _residualHistory;
+
+ ///
+ /// The iteration number of the last iteration.
+ ///
+ private int _lastIteration = DefaultLastIterationNumber;
+
+ ///
+ /// Initializes a new instance of the class with the default maximum
+ /// relative increase and the default minimum number of tracking iterations.
+ ///
+ public DivergenceStopCriterium() : this(DefaultMaximumRelativeIncrease, DefaultMinimumNumberOfIterations)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class with the specified maximum
+ /// relative increase and the default minimum number of tracking iterations.
+ ///
+ /// The maximum relative increase that the residual may experience before a divergence warning is issued.
+ public DivergenceStopCriterium(double maximumRelativeIncrease) : this(maximumRelativeIncrease, DefaultMinimumNumberOfIterations)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class with the default maximum
+ /// relative increase and the specified minimum number of tracking iterations.
+ ///
+ /// The minimum number of iterations over which the residual must grow before a divergence warning is issued.
+ public DivergenceStopCriterium(int minimumIterations) : this(DefaultMinimumNumberOfIterations, minimumIterations)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class with the specified maximum
+ /// relative increase and the specified minimum number of tracking iterations.
+ ///
+ /// The maximum relative increase that the residual may experience before a divergence warning is issued.
+ /// The minimum number of iterations over which the residual must grow before a divergence warning is issued.
+ 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;
+ }
+
+ ///
+ /// Gets or sets the maximum relative increase that the residual may experience before a divergence warning is issued.
+ ///
+ /// Thrown if the Maximum is set to zero or below.
+ public double MaximumRelativeIncrease
+ {
+ [DebuggerStepThrough]
+ get
+ {
+ return _maximumRelativeIncrease;
+ }
+
+ [DebuggerStepThrough]
+ set
+ {
+ if (value <= 0)
+ {
+ throw new ArgumentOutOfRangeException("value");
+ }
+
+ _maximumRelativeIncrease = value;
+ }
+ }
+
+ ///
+ /// Returns the maximum relative increase to the default.
+ ///
+ public void ResetMaximumRelativeIncreaseToDefault()
+ {
+ _maximumRelativeIncrease = DefaultMaximumRelativeIncrease;
+ }
+
+ ///
+ /// Gets or sets the minimum number of iterations over which the residual must grow before
+ /// issuing a divergence warning.
+ ///
+ /// Thrown if the value is set to less than one.
+ 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;
+ }
+ }
+
+ ///
+ /// Returns the minimum number of iterations to the default.
+ ///
+ public void ResetNumberOfIterationsToDefault()
+ {
+ _minimumNumberOfIterations = DefaultMinimumNumberOfIterations;
+ }
+
+ ///
+ /// Determines the status of the iterative calculation based on the stop criteria stored
+ /// by the current . Result is set into Status field.
+ ///
+ /// The number of iterations that have passed so far.
+ /// The vector containing the current solution values.
+ /// The right hand side vector.
+ /// The vector containing the current residual vectors.
+ ///
+ /// 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.
+ ///
+ public void DetermineStatus(int iterationNumber, Vector solutionVector, Vector sourceVector, Vector 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;
+ }
+
+ ///
+ /// Detect if solution is diverging
+ ///
+ /// true if diverging, otherwise false
+ 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;
+ }
+
+ ///
+ /// Gets required history lenght
+ ///
+ private int RequiredHistoryLength
+ {
+ [DebuggerStepThrough]
+ get
+ {
+ return _minimumNumberOfIterations + 1;
+ }
+ }
+
+ ///
+ /// Set status to
+ ///
+ private void SetStatusToDiverged()
+ {
+ if (!(_status is CalculationDiverged))
+ {
+ _status = new CalculationDiverged();
+ }
+ }
+
+ ///
+ /// Set status to
+ ///
+ private void SetStatusToRunning()
+ {
+ if (!(_status is CalculationRunning))
+ {
+ _status = new CalculationRunning();
+ }
+ }
+
+ ///
+ /// Gets the current calculation status.
+ ///
+ public ICalculationStatus Status
+ {
+ [DebuggerStepThrough]
+ get
+ {
+ return _status;
+ }
+ }
+
+ ///
+ /// Resets the to the pre-calculation state.
+ ///
+ public void ResetToPrecalculationState()
+ {
+ _status = DefaultStatus;
+ _lastIteration = DefaultLastIterationNumber;
+ _residualHistory = null;
+ }
+
+ ///
+ /// Gets the which indicates what sort of stop criterium this
+ /// monitors.
+ ///
+ /// Returns .
+ public StopLevel StopLevel
+ {
+ [DebuggerStepThrough]
+ get
+ {
+ return StopLevel.Divergence;
+ }
+ }
+
+ ///
+ /// Clones the current and its settings.
+ ///
+ /// A new instance of the class.
+ public IIterationStopCriterium Clone()
+ {
+ return new DivergenceStopCriterium(_maximumRelativeIncrease, _minimumNumberOfIterations);
+ }
+
+#if !SILVERLIGHT
+ ///
+ /// Clone this object
+ ///
+ /// Object clone
+ object ICloneable.Clone()
+ {
+ return Clone();
+ }
+#endif
+ }
+}
diff --git a/src/Numerics/LinearAlgebra/Double/Solvers/StopCriterium/FailureStopCriterium.cs b/src/Numerics/LinearAlgebra/Double/Solvers/StopCriterium/FailureStopCriterium.cs
new file mode 100644
index 00000000..8e68a180
--- /dev/null
+++ b/src/Numerics/LinearAlgebra/Double/Solvers/StopCriterium/FailureStopCriterium.cs
@@ -0,0 +1,199 @@
+//
+// 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.
+//
+
+namespace MathNet.Numerics.LinearAlgebra.Double.Solvers.StopCriterium
+{
+ using System;
+ using System.Diagnostics;
+ using Properties;
+ using Status;
+
+ ///
+ /// Defines an that monitors residuals for NaN's.
+ ///
+ public sealed class FailureStopCriterium : IIterationStopCriterium
+ {
+ ///
+ /// Defines the default last iteration number. Set to -1 because iterations normally
+ /// start at 0.
+ ///
+ private const int DefaultLastIterationNumber = -1;
+
+ ///
+ /// The default status.
+ ///
+ private static readonly ICalculationStatus DefaultStatus = new CalculationIndetermined();
+
+ ///
+ /// The status of the calculation
+ ///
+ private ICalculationStatus _status = DefaultStatus;
+
+ ///
+ /// The iteration number of the last iteration.
+ ///
+ private int _lastIteration = DefaultLastIterationNumber;
+
+ ///
+ /// Determines the status of the iterative calculation based on the stop criteria stored
+ /// by the current . Result is set into Status field.
+ ///
+ /// The number of iterations that have passed so far.
+ /// The vector containing the current solution values.
+ /// The right hand side vector.
+ /// The vector containing the current residual vectors.
+ ///
+ /// 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.
+ ///
+ public void DetermineStatus(int iterationNumber, Vector solutionVector, Vector sourceVector, Vector 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;
+ }
+
+ ///
+ /// Set status to
+ ///
+ private void SetStatusToFailed()
+ {
+ if (!(_status is CalculationFailure))
+ {
+ _status = new CalculationFailure();
+ }
+ }
+
+ ///
+ /// Set status to
+ ///
+ private void SetStatusToRunning()
+ {
+ if (!(_status is CalculationRunning))
+ {
+ _status = new CalculationRunning();
+ }
+ }
+
+ ///
+ /// Gets the current calculation status.
+ ///
+ public ICalculationStatus Status
+ {
+ [DebuggerStepThrough]
+ get
+ {
+ return _status;
+ }
+ }
+
+ ///
+ /// Resets the to the pre-calculation state.
+ ///
+ public void ResetToPrecalculationState()
+ {
+ _status = DefaultStatus;
+ _lastIteration = DefaultLastIterationNumber;
+ }
+
+ ///
+ /// Gets the which indicates what sort of stop criterium this
+ /// monitors.
+ ///
+ /// Returns .
+ public StopLevel StopLevel
+ {
+ [DebuggerStepThrough]
+ get
+ {
+ return StopLevel.CalculationFailure;
+ }
+ }
+
+ ///
+ /// Clones the current and its settings.
+ ///
+ /// A new instance of the class.
+ public IIterationStopCriterium Clone()
+ {
+ return new FailureStopCriterium();
+ }
+
+#if !SILVERLIGHT
+ ///
+ /// Clones the current and its settings.
+ ///
+ /// A new instance of the class.
+ object ICloneable.Clone()
+ {
+ return Clone();
+ }
+#endif
+ }
+}
diff --git a/src/Numerics/LinearAlgebra/Double/Solvers/StopCriterium/IIterationStopCriterium.cs b/src/Numerics/LinearAlgebra/Double/Solvers/StopCriterium/IIterationStopCriterium.cs
new file mode 100644
index 00000000..6355b190
--- /dev/null
+++ b/src/Numerics/LinearAlgebra/Double/Solvers/StopCriterium/IIterationStopCriterium.cs
@@ -0,0 +1,79 @@
+//
+// 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.
+//
+
+namespace MathNet.Numerics.LinearAlgebra.Double.Solvers.StopCriterium
+{
+ using System;
+ using Status;
+
+ ///
+ /// The base interface for classes that provide stop criteria for iterative calculations.
+ ///
+ public interface IIterationStopCriterium
+#if !SILVERLIGHT
+ : ICloneable
+#endif
+ {
+ ///
+ /// Determines the status of the iterative calculation based on the stop criteria stored
+ /// by the current . Status is set to Status field of current object.
+ ///
+ /// The number of iterations that have passed so far.
+ /// The vector containing the current solution values.
+ /// The right hand side vector.
+ /// The vector containing the current residual vectors.
+ ///
+ /// 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.
+ ///
+ void DetermineStatus(int iterationNumber, Vector solutionVector, Vector sourceVector, Vector residualVector);
+
+ ///
+ /// Gets the current calculation status.
+ ///
+ /// is not a legal value. Status should be set in implementation.
+ ICalculationStatus Status { get; }
+
+ ///
+ /// Resets the to the pre-calculation state.
+ ///
+ /// To implementers: Invoking this method should not clear the user defined
+ /// property values, only the state that is used to track the progress of the
+ /// calculation.
+ void ResetToPrecalculationState();
+
+ ///
+ /// Gets the which indicates what sort of stop criterium this
+ /// monitors.
+ ///
+ StopLevel StopLevel { get; }
+
+#if SILVERLIGHT
+ IIterationStopCriterium Clone();
+#endif
+ }
+}
diff --git a/src/Numerics/LinearAlgebra/Double/Solvers/StopCriterium/IterationCountStopCriterium.cs b/src/Numerics/LinearAlgebra/Double/Solvers/StopCriterium/IterationCountStopCriterium.cs
new file mode 100644
index 00000000..b7250c0d
--- /dev/null
+++ b/src/Numerics/LinearAlgebra/Double/Solvers/StopCriterium/IterationCountStopCriterium.cs
@@ -0,0 +1,225 @@
+//
+// 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.
+//
+
+namespace MathNet.Numerics.LinearAlgebra.Double.Solvers.StopCriterium
+{
+ using System;
+ using System.Diagnostics;
+ using Status;
+
+ ///
+ /// Defines an that monitors the numbers of iteration
+ /// steps as stop criterium.
+ ///
+ public sealed class IterationCountStopCriterium : IIterationStopCriterium
+ {
+ ///
+ /// The default value for the maximum number of iterations the process is allowed
+ /// to perform.
+ ///
+ public const int DefaultMaximumNumberOfIterations = 1000;
+
+ ///
+ /// The default status.
+ ///
+ private static readonly ICalculationStatus DefaultStatus = new CalculationIndetermined();
+
+ ///
+ /// The maximum number of iterations the calculation is allowed to perform.
+ ///
+ private int _maximumNumberOfIterations;
+
+ ///
+ /// The status of the calculation
+ ///
+ private ICalculationStatus _status = DefaultStatus;
+
+ ///
+ /// Initializes a new instance of the class with the default maximum
+ /// number of iterations.
+ ///
+ public IterationCountStopCriterium() : this(DefaultMaximumNumberOfIterations)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class with the specified maximum
+ /// number of iterations.
+ ///
+ /// The maximum number of iterations the calculation is allowed to perform.
+ public IterationCountStopCriterium(int maximumNumberOfIterations)
+ {
+ if (maximumNumberOfIterations < 1)
+ {
+ throw new ArgumentOutOfRangeException("maximumNumberOfIterations");
+ }
+
+ _maximumNumberOfIterations = maximumNumberOfIterations;
+ }
+
+ ///
+ /// Gets or sets the maximum number of iterations the calculation is allowed to perform.
+ ///
+ /// Thrown if the Maximum is set to a negative value.
+ public int MaximumNumberOfIterations
+ {
+ [DebuggerStepThrough]
+ get
+ {
+ return _maximumNumberOfIterations;
+ }
+
+ [DebuggerStepThrough]
+ set
+ {
+ if (value < 1)
+ {
+ throw new ArgumentOutOfRangeException("value");
+ }
+
+ _maximumNumberOfIterations = value;
+ }
+ }
+
+ ///
+ /// Returns the maximum number of iterations to the default.
+ ///
+ public void ResetMaximumNumberOfIterationsToDefault()
+ {
+ _maximumNumberOfIterations = DefaultMaximumNumberOfIterations;
+ }
+
+ ///
+ /// Determines the status of the iterative calculation based on the stop criteria stored
+ /// by the current . Result is set into Status field.
+ ///
+ /// The number of iterations that have passed so far.
+ /// The vector containing the current solution values.
+ /// The right hand side vector.
+ /// The vector containing the current residual vectors.
+ ///
+ /// 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.
+ ///
+ public void DetermineStatus(int iterationNumber, Vector solutionVector, Vector sourceVector, Vector residualVector)
+ {
+ if (iterationNumber < 0)
+ {
+ throw new ArgumentOutOfRangeException("iterationNumber");
+ }
+
+ if (iterationNumber >= _maximumNumberOfIterations)
+ {
+ SetStatusToFinished();
+ }
+ else
+ {
+ SetStatusToRunning();
+ }
+ }
+
+ ///
+ /// Set status to
+ ///
+ private void SetStatusToFinished()
+ {
+ if (!(_status is CalculationStoppedWithoutConvergence))
+ {
+ _status = new CalculationStoppedWithoutConvergence();
+ }
+ }
+
+ ///
+ /// Set status to
+ ///
+ private void SetStatusToRunning()
+ {
+ if (!(_status is CalculationRunning))
+ {
+ _status = new CalculationRunning();
+ }
+ }
+
+ ///
+ /// Gets the current calculation status.
+ ///
+ public ICalculationStatus Status
+ {
+ [DebuggerStepThrough]
+ get
+ {
+ return _status;
+ }
+ }
+
+ ///
+ /// Resets the to the pre-calculation state.
+ ///
+ public void ResetToPrecalculationState()
+ {
+ _status = DefaultStatus;
+ }
+
+ ///
+ /// Gets the which indicates what sort of stop criterium this
+ /// monitors.
+ ///
+ /// Returns .
+ public StopLevel StopLevel
+ {
+ [DebuggerStepThrough]
+ get
+ {
+ return StopLevel.StoppedWithoutConvergence;
+ }
+ }
+
+ ///
+ /// Clones the current and its settings.
+ ///
+ /// A new instance of the class.
+ public IIterationStopCriterium Clone()
+ {
+ return new IterationCountStopCriterium(_maximumNumberOfIterations);
+ }
+
+#if !SILVERLIGHT
+ ///
+ /// Clones the current and its settings.
+ ///
+ /// A new instance of the object.
+ object ICloneable.Clone()
+ {
+ return Clone();
+ }
+#endif
+ }
+}
diff --git a/src/Numerics/LinearAlgebra/Double/Solvers/StopCriterium/ResidualStopCriterium.cs b/src/Numerics/LinearAlgebra/Double/Solvers/StopCriterium/ResidualStopCriterium.cs
new file mode 100644
index 00000000..aac8a500
--- /dev/null
+++ b/src/Numerics/LinearAlgebra/Double/Solvers/StopCriterium/ResidualStopCriterium.cs
@@ -0,0 +1,407 @@
+//
+// 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.
+//
+
+namespace MathNet.Numerics.LinearAlgebra.Double.Solvers.StopCriterium
+{
+ using System;
+ using System.Diagnostics;
+ using Properties;
+ using Status;
+
+ ///
+ /// Defines an that monitors residuals as stop criterium.
+ ///
+ public sealed class ResidualStopCriterium : IIterationStopCriterium
+ {
+ ///
+ /// The default value for the maximum value of the residual.
+ ///
+ public const double DefaultMaximumResidual = 1e-12;
+
+ ///
+ /// The default value for the minimum number of iterations.
+ ///
+ public const int DefaultMinimumIterationsBelowMaximum = 0;
+
+ ///
+ /// Defines the default last iteration number. Set to -1 because iterations normally start at 0.
+ ///
+ private const int DefaultLastIterationNumber = -1;
+
+ ///
+ /// The default status.
+ ///
+ private static readonly ICalculationStatus DefaultStatus = new CalculationIndetermined();
+
+ ///
+ /// The maximum value for the residual below which the calculation is considered converged.
+ ///
+ private double _maximum;
+
+ ///
+ /// The minimum number of iterations for which the residual has to be below the maximum before
+ /// the calculation is considered converged.
+ ///
+ private int _minimumIterationsBelowMaximum;
+
+ ///
+ /// The status of the calculation
+ ///
+ private ICalculationStatus _status = DefaultStatus;
+
+ ///
+ /// The number of iterations since the residuals got below the maximum.
+ ///
+ private int _iterationCount;
+
+ ///
+ /// The iteration number of the last iteration.
+ ///
+ private int _lastIteration = DefaultLastIterationNumber;
+
+ ///
+ /// Initializes a new instance of the class with the default maximum
+ /// residual and the default minimum number of iterations.
+ ///
+ public ResidualStopCriterium() : this(DefaultMaximumResidual, DefaultMinimumIterationsBelowMaximum)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class with the specified
+ /// maximum residual and the default minimum number of iterations.
+ ///
+ /// The maximum value for the residual below which the calculation is considered converged.
+ public ResidualStopCriterium(double maximum) : this(maximum, DefaultMinimumIterationsBelowMaximum)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class with the default maximum residual
+ /// and specified minimum number of iterations.
+ ///
+ ///
+ /// The minimum number of iterations for which the residual has to be below the maximum before
+ /// the calculation is considered converged.
+ ///
+ public ResidualStopCriterium(int minimumIterationsBelowMaximum) : this(DefaultMaximumResidual, minimumIterationsBelowMaximum)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class with the specified
+ /// maximum residual and minimum number of iterations.
+ ///
+ ///
+ /// The maximum value for the residual below which the calculation is considered converged.
+ ///
+ ///
+ /// The minimum number of iterations for which the residual has to be below the maximum before
+ /// the calculation is considered converged.
+ ///
+ public ResidualStopCriterium(double maximum, int minimumIterationsBelowMaximum)
+ {
+ if (maximum < 0)
+ {
+ throw new ArgumentOutOfRangeException("maximum");
+ }
+
+ if (minimumIterationsBelowMaximum < 0)
+ {
+ throw new ArgumentOutOfRangeException("minimumIterationsBelowMaximum");
+ }
+
+ _maximum = maximum;
+ _minimumIterationsBelowMaximum = minimumIterationsBelowMaximum;
+ }
+
+ ///
+ /// Gets or sets the maximum value for the residual below which the calculation is considered
+ /// converged.
+ ///
+ /// Thrown if the Maximum is set to a negative value.
+ public double Maximum
+ {
+ [DebuggerStepThrough]
+ get
+ {
+ return _maximum;
+ }
+
+ [DebuggerStepThrough]
+ set
+ {
+ if (value < 0)
+ {
+ throw new ArgumentOutOfRangeException("value");
+ }
+
+ _maximum = value;
+ }
+ }
+
+ ///
+ /// Returns the maximum residual to the default.
+ ///
+ public void ResetMaximumResidualToDefault()
+ {
+ _maximum = DefaultMaximumResidual;
+ }
+
+ ///
+ /// Gets or sets the minimum number of iterations for which the residual has to be
+ /// below the maximum before the calculation is considered converged.
+ ///
+ /// Thrown if the BelowMaximumFor is set to a value less than 1.
+ public int MinimumIterationsBelowMaximum
+ {
+ [DebuggerStepThrough]
+ get
+ {
+ return _minimumIterationsBelowMaximum;
+ }
+
+ [DebuggerStepThrough]
+ set
+ {
+ if (value < 0)
+ {
+ throw new ArgumentOutOfRangeException("value");
+ }
+
+ _minimumIterationsBelowMaximum = value;
+ }
+ }
+
+ ///
+ /// Returns the minimum number of iterations to the default.
+ ///
+ public void ResetMinimumIterationsBelowMaximumToDefault()
+ {
+ _minimumIterationsBelowMaximum = DefaultMinimumIterationsBelowMaximum;
+ }
+
+ ///
+ /// Determines the status of the iterative calculation based on the stop criteria stored
+ /// by the current . Result is set into Status field.
+ ///
+ /// The number of iterations that have passed so far.
+ /// The vector containing the current solution values.
+ /// The right hand side vector.
+ /// The vector containing the current residual vectors.
+ ///
+ /// 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.
+ ///
+ public void DetermineStatus(int iterationNumber, Vector solutionVector, Vector sourceVector, Vector 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;
+ }
+
+ ///
+ /// Calculate stop criterium
+ ///
+ /// Solution vector norm
+ /// Criterium value
+ 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);
+ }
+
+ ///
+ /// Set status to
+ ///
+ private void SetStatusToDiverged()
+ {
+ if (!(_status is CalculationDiverged))
+ {
+ _status = new CalculationDiverged();
+ }
+ }
+
+ ///
+ /// Set status to
+ ///
+ private void SetStatusToConverged()
+ {
+ if (!(_status is CalculationConverged))
+ {
+ _status = new CalculationConverged();
+ }
+ }
+
+ ///
+ /// Set status to
+ ///
+ private void SetStatusToRunning()
+ {
+ if (!(_status is CalculationRunning))
+ {
+ _status = new CalculationRunning();
+ }
+ }
+
+ ///
+ /// Gets the current calculation status.
+ ///
+ public ICalculationStatus Status
+ {
+ [DebuggerStepThrough]
+ get
+ {
+ return _status;
+ }
+ }
+
+ ///
+ /// Resets the to the pre-calculation state.
+ ///
+ public void ResetToPrecalculationState()
+ {
+ _status = DefaultStatus;
+ _iterationCount = 0;
+ _lastIteration = DefaultLastIterationNumber;
+ }
+
+ ///
+ /// Gets the which indicates what sort of stop criterium this
+ /// monitors.
+ ///
+ /// Returns .
+ public StopLevel StopLevel
+ {
+ [DebuggerStepThrough]
+ get
+ {
+ return StopLevel.Convergence;
+ }
+ }
+
+ ///
+ /// Clones the current and its settings.
+ ///
+ /// A new instance of the class.
+ public IIterationStopCriterium Clone()
+ {
+ return new ResidualStopCriterium(_maximum, _minimumIterationsBelowMaximum);
+ }
+
+#if !SILVERLIGHT
+ ///
+ /// Clones the current and its settings.
+ ///
+ /// A new instance of the object.
+ object ICloneable.Clone()
+ {
+ return Clone();
+ }
+#endif
+ }
+}
diff --git a/src/Numerics/LinearAlgebra/Double/Solvers/StopCriterium/StopLevel.cs b/src/Numerics/LinearAlgebra/Double/Solvers/StopCriterium/StopLevel.cs
new file mode 100644
index 00000000..ac3e0739
--- /dev/null
+++ b/src/Numerics/LinearAlgebra/Double/Solvers/StopCriterium/StopLevel.cs
@@ -0,0 +1,57 @@
+//
+// 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.
+//
+
+namespace MathNet.Numerics.LinearAlgebra.Double.Solvers.StopCriterium
+{
+ ///
+ /// Indicates what an IIterationStopCriterium monitors for stop criteria.
+ ///
+ public enum StopLevel
+ {
+ ///
+ /// The monitors calculation failures in the
+ /// iterative calculation.
+ ///
+ CalculationFailure,
+
+ ///
+ /// The monitors the calculation for signs of divergence.
+ ///
+ Divergence,
+
+ ///
+ /// The guards the calculation against unlimited continuation
+ /// by monitoring user specified limits, e.g. the maximum number of iterations.
+ ///
+ StoppedWithoutConvergence,
+
+ ///
+ /// The monitors the calculation for convergence, usually
+ /// based on the residuals of the calculation.
+ ///
+ Convergence
+ }
+}
diff --git a/src/Numerics/LinearAlgebra/Double/SparseMatrix.cs b/src/Numerics/LinearAlgebra/Double/SparseMatrix.cs
index fb541ad7..bd73b1df 100644
--- a/src/Numerics/LinearAlgebra/Double/SparseMatrix.cs
+++ b/src/Numerics/LinearAlgebra/Double/SparseMatrix.cs
@@ -891,6 +891,71 @@ namespace MathNet.Numerics.LinearAlgebra.Double
return ret;
}
+ /// Calculates the Frobenius norm of this matrix.
+ /// The Frobenius norm of this matrix.
+ public override double FrobeniusNorm()
+ {
+ var transpose = (SparseMatrix)Transpose();
+ var aat = this * transpose;
+
+ var norm = 0.0;
+
+ for (var i = 0; i < aat._rowIndex.Length; i++)
+ {
+ // Get the begin / end index for the current row
+ var startIndex = aat._rowIndex[i];
+ var endIndex = i < aat._rowIndex.Length - 1 ? aat._rowIndex[i + 1] : aat.NonZerosCount;
+
+ // Get the values for the current row
+ if (startIndex == endIndex)
+ {
+ // Begin and end are equal. There are no values in the row, Move to the next row
+ continue;
+ }
+
+ for (var j = startIndex; j < endIndex; j++)
+ {
+ if (i == aat._columnIndices[j])
+ {
+ norm += Math.Abs(aat._nonZeroValues[j]);
+ }
+ }
+ }
+
+ norm = Math.Sqrt(norm);
+ return norm;
+ }
+
+ /// Calculates the infinity norm of this matrix.
+ /// The infinity norm of this matrix.
+ public override double InfinityNorm()
+ {
+ var norm = 0.0;
+ for (var i = 0; i < _rowIndex.Length; i++)
+ {
+ // Get the begin / end index for the current row
+ var startIndex = _rowIndex[i];
+ var endIndex = i < _rowIndex.Length - 1 ? _rowIndex[i + 1] : NonZerosCount;
+
+ // Get the values for the current row
+ if (startIndex == endIndex)
+ {
+ // Begin and end are equal. There are no values in the row, Move to the next row
+ continue;
+ }
+
+ var s = 0.0;
+ for (var j = startIndex; j < endIndex; j++)
+ {
+ s += Math.Abs(_nonZeroValues[j]);
+ }
+
+ norm = Math.Max(norm, s);
+ }
+
+ return norm;
+ }
+
///
/// Copies the requested row elements into a new .
///
@@ -1184,46 +1249,43 @@ namespace MathNet.Numerics.LinearAlgebra.Double
{
var otherSparseMatrix = other as SparseMatrix;
var resultSparseMatrix = result as SparseMatrix;
-
if (otherSparseMatrix == null || resultSparseMatrix == null)
{
base.Multiply(other, result);
+ return;
}
- else
+
+ if (ColumnCount != otherSparseMatrix.RowCount)
{
- if (ColumnCount != otherSparseMatrix.RowCount)
- {
- throw new ArgumentException(Resources.ArgumentMatrixDimensions);
- }
+ throw new ArgumentException(Resources.ArgumentMatrixDimensions);
+ }
- if (resultSparseMatrix.RowCount != RowCount || resultSparseMatrix.ColumnCount != otherSparseMatrix.ColumnCount)
+ if (resultSparseMatrix.RowCount != RowCount || resultSparseMatrix.ColumnCount != otherSparseMatrix.ColumnCount)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixDimensions);
+ }
+
+ resultSparseMatrix.Clear();
+ var columnVector = new DenseVector(otherSparseMatrix.RowCount);
+ for (var row = 0; row < RowCount; row++)
+ {
+ // Get the begin / end index for the current row
+ var startIndex = _rowIndex[row];
+ var endIndex = row < _rowIndex.Length - 1 ? _rowIndex[row + 1] : NonZerosCount;
+ if (startIndex == endIndex)
{
- throw new ArgumentException(Resources.ArgumentMatrixDimensions);
+ continue;
}
- resultSparseMatrix.Clear();
-
- var columnVector = new DenseVector(otherSparseMatrix.RowCount);
- for (var row = 0; row < RowCount; row++)
+ for (var column = 0; column < otherSparseMatrix.ColumnCount; column++)
{
- // Get the begin / end index for the current row
- var startIndex = _rowIndex[row];
- var endIndex = row < _rowIndex.Length - 1 ? _rowIndex[row + 1] : NonZerosCount;
- if (startIndex == endIndex)
- {
- continue;
- }
-
- for (var column = 0; column < otherSparseMatrix.ColumnCount; column++)
- {
- // Multiply row of matrix A on column of matrix B
- otherSparseMatrix.Column(column, columnVector);
- var sum = CommonParallel.Aggregate(
- startIndex,
- endIndex,
- index => _nonZeroValues[index] * columnVector[_columnIndices[index]]);
- resultSparseMatrix.SetValueAt(row, column, sum);
- }
+ // Multiply row of matrix A on column of matrix B
+ otherSparseMatrix.Column(column, columnVector);
+ var sum = CommonParallel.Aggregate(
+ startIndex,
+ endIndex,
+ index => _nonZeroValues[index] * columnVector[_columnIndices[index]]);
+ resultSparseMatrix.SetValueAt(row, column, sum);
}
}
}
@@ -1253,6 +1315,98 @@ namespace MathNet.Numerics.LinearAlgebra.Double
return result;
}
+ ///
+ /// Multiplies this dense matrix with transpose of another dense matrix and places the results into the result dense matrix.
+ ///
+ /// The matrix to multiply with.
+ /// The result of the multiplication.
+ /// If the other matrix is .
+ /// If the result matrix is .
+ /// If this.Columns != other.Rows.
+ /// If the result matrix's dimensions are not the this.Rows x other.Columns.
+ public override void TransposeAndMultiply(Matrix other, Matrix result)
+ {
+ var otherSparse = other as SparseMatrix;
+ var resultSparse = result as SparseMatrix;
+
+ if (otherSparse == null || resultSparse == null)
+ {
+ base.TransposeAndMultiply(other, result);
+ return;
+ }
+
+ if (ColumnCount != otherSparse.ColumnCount)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixDimensions);
+ }
+
+ if ((resultSparse.RowCount != RowCount) || (resultSparse.ColumnCount != otherSparse.RowCount))
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixDimensions);
+ }
+
+ resultSparse.Clear();
+ for (var j = 0; j < RowCount; j++)
+ {
+ // Get the begin / end index for the row
+ var startIndexOther = otherSparse._rowIndex[j];
+ var endIndexOther = j < otherSparse._rowIndex.Length - 1 ? otherSparse._rowIndex[j + 1] : otherSparse.NonZerosCount;
+ if (startIndexOther == endIndexOther)
+ {
+ continue;
+ }
+
+ for (var i = 0; i < RowCount; i++)
+ {
+ // Multiply row of matrix A on row of matrix B
+ // Get the begin / end index for the row
+ var startIndexThis = _rowIndex[i];
+ var endIndexThis = i < _rowIndex.Length - 1 ? _rowIndex[i + 1] : NonZerosCount;
+ if (startIndexThis == endIndexThis)
+ {
+ continue;
+ }
+
+ var i1 = i;
+ var sum = CommonParallel.Aggregate(
+ startIndexOther,
+ endIndexOther,
+ index =>
+ {
+ var ind = FindItem(i1, otherSparse._columnIndices[index]);
+ return ind >= 0 ? otherSparse._nonZeroValues[index] * _nonZeroValues[ind] : 0.0;
+ });
+
+ resultSparse.SetValueAt(i, j, sum + result.At(i, j));
+ }
+ }
+ }
+
+ ///
+ /// Multiplies this matrix with transpose of another matrix and returns the result.
+ ///
+ /// The matrix to multiply with.
+ /// If this.Columns != other.Rows.
+ /// If the other matrix is .
+ /// The result of multiplication.
+ public override Matrix TransposeAndMultiply(Matrix other)
+ {
+ var otherSparse = other as SparseMatrix;
+ if (otherSparse == null)
+ {
+ return base.TransposeAndMultiply(other);
+ }
+
+ if (ColumnCount != otherSparse.ColumnCount)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixDimensions);
+ }
+
+ var result = (SparseMatrix)CreateMatrix(RowCount, other.RowCount);
+ TransposeAndMultiply(other, result);
+ return result;
+ }
+
///
/// Multiplies two sparse matrices.
///
diff --git a/src/Numerics/LinearAlgebra/Double/SparseVector.cs b/src/Numerics/LinearAlgebra/Double/SparseVector.cs
index 228929be..b1a24145 100644
--- a/src/Numerics/LinearAlgebra/Double/SparseVector.cs
+++ b/src/Numerics/LinearAlgebra/Double/SparseVector.cs
@@ -1230,19 +1230,23 @@ namespace MathNet.Numerics.LinearAlgebra.Double
{
throw new ArgumentOutOfRangeException("p");
}
- else if (Double.IsPositiveInfinity(p))
+
+ if (NonZerosCount == 0)
{
- return CommonParallel.Select(0, NonZerosCount, (index, localData) => localData = Math.Max(localData, Math.Abs(_nonZeroValues[index])), Math.Max);
+ return 0.0;
}
- else
- {
- var sum = CommonParallel.Aggregate(
- 0,
- NonZerosCount,
- index => Math.Pow(Math.Abs(_nonZeroValues[index]), p));
- return Math.Pow(sum, 1.0 / p);
+ if (Double.IsPositiveInfinity(p))
+ {
+ return CommonParallel.Select(0, NonZerosCount, (index, localData) => localData = Math.Max(localData, Math.Abs(_nonZeroValues[index])), Math.Max);
}
+
+ var sum = CommonParallel.Aggregate(
+ 0,
+ NonZerosCount,
+ index => Math.Pow(Math.Abs(_nonZeroValues[index]), p));
+
+ return Math.Pow(sum, 1.0 / p);
}
#endregion
diff --git a/src/Numerics/Numerics.csproj b/src/Numerics/Numerics.csproj
index 8f71b135..78f3b5d1 100644
--- a/src/Numerics/Numerics.csproj
+++ b/src/Numerics/Numerics.csproj
@@ -104,26 +104,74 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Code
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -168,17 +216,6 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/Numerics/Precision.cs b/src/Numerics/Precision.cs
index 03756181..7d9e2c3f 100644
--- a/src/Numerics/Precision.cs
+++ b/src/Numerics/Precision.cs
@@ -756,6 +756,28 @@ namespace MathNet.Numerics
return AlmostEqualWithError(a, b, diff, _defaultSingleRelativeAccuracy);
}
+ ///
+ /// Checks whether two Compex numbers are almost equal.
+ ///
+ /// The first number
+ /// The second number
+ /// true if the two values differ by no more than 10 * 2^(-52); false otherwise.
+ 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);
+ }
+
///
/// Checks whether two structures with precision support are almost equal.
///
diff --git a/src/Numerics/Properties/Resources.Designer.cs b/src/Numerics/Properties/Resources.Designer.cs
index 9795d673..4907d9a0 100644
--- a/src/Numerics/Properties/Resources.Designer.cs
+++ b/src/Numerics/Properties/Resources.Designer.cs
@@ -672,6 +672,24 @@ namespace MathNet.Numerics.Properties {
}
}
+ ///
+ /// Looks up a localized string similar to The given stop criterium already exist in the collection..
+ ///
+ internal static string StopCriteriumDuplicate {
+ get {
+ return ResourceManager.GetString("StopCriteriumDuplicate", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to There is no stop criterium in the collection..
+ ///
+ internal static string StopCriteriumMissing {
+ get {
+ return ResourceManager.GetString("StopCriteriumMissing", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to String parameter cannot be empty or null..
///
diff --git a/src/Numerics/Properties/Resources.resx b/src/Numerics/Properties/Resources.resx
index 620943f2..f231e0e1 100644
--- a/src/Numerics/Properties/Resources.resx
+++ b/src/Numerics/Properties/Resources.resx
@@ -333,4 +333,10 @@
{0} is not a supported type.
+
+ The given stop criterium already exist in the collection.
+
+
+ There is no stop criterium in the collection.
+
\ No newline at end of file
diff --git a/src/Numerics/Threading/CommonParallel.cs b/src/Numerics/Threading/CommonParallel.cs
index 0984ebb3..e7f89909 100644
--- a/src/Numerics/Threading/CommonParallel.cs
+++ b/src/Numerics/Threading/CommonParallel.cs
@@ -31,6 +31,7 @@
namespace MathNet.Numerics.Threading
{
using System;
+ using System.Numerics;
#if !SILVERLIGHT
using System.Collections.Concurrent;
@@ -139,6 +140,67 @@ namespace MathNet.Numerics.Threading
return sum;
}
+ ///
+ /// Aggregates a function over a loop for Complex data type.
+ ///
+ /// Starting index of the loop.
+ /// Ending index of the loop
+ /// The function to aggregate.
+ /// The sum of the function over the loop.
+ public static Complex Aggregate(int fromInclusive, int toExclusive, Func body)
+ {
+ var sync = new object();
+ var sum = Complex.Zero;
+
+#if SILVERLIGHT
+ Parallel.For(
+ fromInclusive,
+ toExclusive,
+ () => Complex.Zero,
+ (i, localData) => localData += body(i),
+ localResult =>
+ {
+ lock (sync)
+ {
+ sum += localResult;
+ }
+ });
+#else
+
+ if (Control.DisableParallelization || Control.NumberOfParallelWorkerThreads < 2)
+ {
+ for (var index = fromInclusive; index < toExclusive; index++)
+ {
+ sum += body(index);
+ }
+ }
+ else
+ {
+ Parallel.ForEach(
+ Partitioner.Create(fromInclusive, toExclusive),
+ new ParallelOptions { MaxDegreeOfParallelism = Control.NumberOfParallelWorkerThreads },
+ () => Complex.Zero,
+ (range, loopState, localData) =>
+ {
+ for (var i = range.Item1; i < range.Item2; i++)
+ {
+ localData += body(i);
+ }
+
+ return localData;
+ },
+ localResult =>
+ {
+ lock (sync)
+ {
+ sum += localResult;
+ }
+ });
+ }
+#endif
+ return sum;
+ }
+
///
/// Executes each of the provided actions inside a discrete, asynchronous task.
///
diff --git a/src/Silverlight/Silverlight.csproj b/src/Silverlight/Silverlight.csproj
index 7a5771eb..d66ac0c6 100644
--- a/src/Silverlight/Silverlight.csproj
+++ b/src/Silverlight/Silverlight.csproj
@@ -221,6 +221,9 @@
LinearAlgebra\Double\DenseVector.cs
+
+ LinearAlgebra\Double\DiagonalMatrix.cs
+
LinearAlgebra\Double\Factorization\Cholesky.cs
@@ -239,12 +242,27 @@
LinearAlgebra\Double\Factorization\ExtensionMethods.cs
+
+ LinearAlgebra\Double\Factorization\GramSchmidt.cs
+
LinearAlgebra\Double\Factorization\LU.cs
LinearAlgebra\Double\Factorization\QR.cs
+
+ LinearAlgebra\Double\Factorization\SparseCholesky.cs
+
+
+ LinearAlgebra\Double\Factorization\SparseLU.cs
+
+
+ LinearAlgebra\Double\Factorization\SparseQR.cs
+
+
+ LinearAlgebra\Double\Factorization\SparseSvd.cs
+
LinearAlgebra\Double\Factorization\Svd.cs
@@ -281,6 +299,93 @@
LinearAlgebra\Double\Matrix.cs
+
+ LinearAlgebra\Double\Solvers\IIterativeSolver.cs
+
+
+ LinearAlgebra\Double\Solvers\IIterativeSolverSetup.cs
+
+
+ LinearAlgebra\Double\Solvers\IIterator.cs
+
+
+ LinearAlgebra\Double\Solvers\Iterative\BiCgStab.cs
+
+
+ LinearAlgebra\Double\Solvers\Iterative\CompositeSolver.cs
+
+
+ LinearAlgebra\Double\Solvers\Iterative\GpBiCg.cs
+
+
+ LinearAlgebra\Double\Solvers\Iterative\MlkBiCgStab.cs
+
+
+ LinearAlgebra\Double\Solvers\Iterative\TFQMR.cs
+
+
+ LinearAlgebra\Double\Solvers\Iterator.cs
+
+
+ LinearAlgebra\Double\Solvers\Preconditioners\Diagonal.cs
+
+
+ LinearAlgebra\Double\Solvers\Preconditioners\Ilutp.cs
+
+
+ LinearAlgebra\Double\Solvers\Preconditioners\IlutpElementSorter.cs
+
+
+ LinearAlgebra\Double\Solvers\Preconditioners\IncompleteLU.cs
+
+
+ LinearAlgebra\Double\Solvers\Preconditioners\IPreConditioner.cs
+
+
+ LinearAlgebra\Double\Solvers\Preconditioners\UnitPreconditioner.cs
+
+
+ LinearAlgebra\Double\Solvers\Status\CalculationCancelled.cs
+
+
+ LinearAlgebra\Double\Solvers\Status\CalculationConverged.cs
+
+
+ LinearAlgebra\Double\Solvers\Status\CalculationDiverged.cs
+
+
+ LinearAlgebra\Double\Solvers\Status\CalculationFailure.cs
+
+
+ LinearAlgebra\Double\Solvers\Status\CalculationIndetermined.cs
+
+
+ LinearAlgebra\Double\Solvers\Status\CalculationRunning.cs
+
+
+ LinearAlgebra\Double\Solvers\Status\CalculationStoppedWithoutConvergence.cs
+
+
+ LinearAlgebra\Double\Solvers\Status\ICalculationStatus.cs
+
+
+ LinearAlgebra\Double\Solvers\StopCriterium\DivergenceStopCriterium.cs
+
+
+ LinearAlgebra\Double\Solvers\StopCriterium\FailureStopCriterium.cs
+
+
+ LinearAlgebra\Double\Solvers\StopCriterium\IIterationStopCriterium.cs
+
+
+ LinearAlgebra\Double\Solvers\StopCriterium\IterationCountStopCriterium.cs
+
+
+ LinearAlgebra\Double\Solvers\StopCriterium\ResidualStopCriterium.cs
+
+
+ LinearAlgebra\Double\Solvers\StopCriterium\StopLevel.cs
+
LinearAlgebra\Double\SparseMatrix.cs
diff --git a/src/UnitTests/AssertHelpers.cs b/src/UnitTests/AssertHelpers.cs
index 20c9a2b7..ffd22bb2 100644
--- a/src/UnitTests/AssertHelpers.cs
+++ b/src/UnitTests/AssertHelpers.cs
@@ -28,7 +28,6 @@
// OTHER DEALINGS IN THE SOFTWARE.
//
-
namespace MathNet.Numerics.UnitTests
{
using System.Collections.Generic;
@@ -38,8 +37,38 @@ namespace MathNet.Numerics.UnitTests
///
/// A class which includes some assertion helper methods particularly for numerical code.
///
- class AssertHelpers
+ internal class AssertHelpers
{
+ ///
+ /// Asserts that the expected value and the actual value are equal.
+ ///
+ /// The expected value.
+ /// The actual value.
+ public static void AreEqual(Complex expected, Complex actual)
+ {
+ if (expected.IsNaN() && actual.IsNaN())
+ {
+ return;
+ }
+
+ if (expected.IsInfinity() && expected.IsInfinity())
+ {
+ return;
+ }
+
+ bool pass = expected.Real.AlmostEqual(actual.Real);
+ if (!pass)
+ {
+ Assert.Fail("Real components are not equal. Expected:{0}; Actual:{1}", expected.Real, actual.Real);
+ }
+
+ pass = expected.Imaginary.AlmostEqual(actual.Imaginary);
+ if (!pass)
+ {
+ Assert.Fail("Imaginary components are not equal. Expected:{0}; Actual:{1}", expected.Imaginary, actual.Imaginary);
+ }
+ }
+
///
/// Asserts that the expected value and the actual value are equal up to a certain number of decimal places. If both
/// and are NaN then no assert is thrown.
@@ -49,7 +78,7 @@ namespace MathNet.Numerics.UnitTests
/// The number of decimal places to agree on.
public static void AlmostEqual(double expected, double actual, int decimalPlaces)
{
- if(double.IsNaN(expected) && double.IsNaN(actual))
+ if (double.IsNaN(expected) && double.IsNaN(actual))
{
return;
}
@@ -57,7 +86,7 @@ namespace MathNet.Numerics.UnitTests
bool pass = Precision.AlmostEqualInDecimalPlaces(expected, actual, decimalPlaces);
if (!pass)
{
- //signals Gallio that the test failed.
+ // signals Gallio that the test failed.
Assert.Fail("Not equal within {0} places. Expected:{1}; Actual:{2}", decimalPlaces, expected, actual);
}
}
@@ -83,7 +112,6 @@ namespace MathNet.Numerics.UnitTests
}
}
-
///
/// Asserts that the expected value and the actual value are equal up to a certain number of decimal places.
///
diff --git a/src/UnitTests/LinearAlgebraTests/Double/DenseMatrixTests.cs b/src/UnitTests/LinearAlgebraTests/Double/DenseMatrixTests.cs
index 2226c3fe..878b4704 100644
--- a/src/UnitTests/LinearAlgebraTests/Double/DenseMatrixTests.cs
+++ b/src/UnitTests/LinearAlgebraTests/Double/DenseMatrixTests.cs
@@ -59,16 +59,18 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[Test]
public void CanCreateMatrixFrom1DArray()
{
- Dictionary testData = new Dictionary();
- testData.Add("Singular3x3", new DenseMatrix(3, 3, new double[] { 1, 1, 1, 1, 1, 1, 2, 2, 2 }));
- testData.Add("Square3x3", new DenseMatrix(3, 3, new double[] { -1.1, 0.0, -4.4, -2.2, 1.1, 5.5, -3.3, 2.2, 6.6 }));
- testData.Add("Square4x4", new DenseMatrix(4, 4, new double[] { -1.1, 0.0, 1.0, -4.4, -2.2, 1.1, 2.1, 5.5, -3.3, 2.2, 6.2, 6.6, -4.4, 3.3, 4.3, -7.7 }));
- testData.Add("Tall3x2", new DenseMatrix(3, 2, new double[] { -1.1, 0.0, -4.4, -2.2, 1.1, 5.5 }));
- testData.Add("Wide2x3", new DenseMatrix(2, 3, new double[] { -1.1, 0.0, -2.2, 1.1, -3.3, 2.2 }));
+ Dictionary testData = new Dictionary
+ {
+ { "Singular3x3", new DenseMatrix(3, 3, new[] { 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 2.0, 2.0, 2.0 }) },
+ { "Square3x3", new DenseMatrix(3, 3, new[] { -1.1, 0.0, -4.4, -2.2, 1.1, 5.5, -3.3, 2.2, 6.6 }) },
+ { "Square4x4", new DenseMatrix(4, 4, new[] { -1.1, 0.0, 1.0, -4.4, -2.2, 1.1, 2.1, 5.5, -3.3, 2.2, 6.2, 6.6, -4.4, 3.3, 4.3, -7.7 }) },
+ { "Tall3x2", new DenseMatrix(3, 2, new[] { -1.1, 0.0, -4.4, -2.2, 1.1, 5.5 }) },
+ { "Wide2x3", new DenseMatrix(2, 3, new[] { -1.1, 0.0, -2.2, 1.1, -3.3, 2.2 }) }
+ };
foreach (var name in testData.Keys)
{
- Assert.AreEqual(testMatrices[name], testData[name]);
+ Assert.AreEqual(TestMatrices[name], testData[name]);
}
}
@@ -84,9 +86,9 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[Test]
public void MatrixFrom2DArrayIsCopy()
{
- var matrix = new DenseMatrix(testData2D["Singular3x3"]);
+ var matrix = new DenseMatrix(TestData2D["Singular3x3"]);
matrix[0, 0] = 10.0;
- Assert.AreEqual(1.0, testData2D["Singular3x3"][0, 0]);
+ Assert.AreEqual(1.0, TestData2D["Singular3x3"][0, 0]);
}
[Test]
@@ -98,12 +100,12 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[Row("Wide2x3")]
public void CanCreateMatrixFrom2DArray(string name)
{
- var matrix = new DenseMatrix(testData2D[name]);
- for (var i = 0; i < testData2D[name].GetLength(0); i++)
+ 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++)
+ for (var j = 0; j < TestData2D[name].GetLength(1); j++)
{
- Assert.AreEqual(testData2D[name][i, j], matrix[i, j]);
+ Assert.AreEqual(TestData2D[name][i, j], matrix[i, j]);
}
}
}
@@ -147,7 +149,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedArgumentException]
public void IdentityFailsWithZeroOrNegativeOrder(int order)
{
- var matrix = DenseMatrix.Identity(order);
+ DenseMatrix.Identity(order);
}
}
}
diff --git a/src/UnitTests/LinearAlgebraTests/Double/DiagonalMatrixTests.cs b/src/UnitTests/LinearAlgebraTests/Double/DiagonalMatrixTests.cs
new file mode 100644
index 00000000..6a61a8b9
--- /dev/null
+++ b/src/UnitTests/LinearAlgebraTests/Double/DiagonalMatrixTests.cs
@@ -0,0 +1,449 @@
+//
+// 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.
+//
+
+namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using MbUnit.Framework;
+ using LinearAlgebra.Double;
+
+ public class DiagonalMatrixTests : MatrixTests
+ {
+ [SetUp]
+ public override void SetupMatrices()
+ {
+ TestData2D = new Dictionary
+ {
+ { "Singular3x3", new [,] { { 1.0, 0.0, 0.0 }, { 0.0, 0.0, 0.0 }, { 0.0, 0.0, 3.0 } } },
+ { "Square3x3", new [,] { { -1.1, 0.0, 0.0 }, { 0.0, 1.1, 0.0 }, { 0.0, 0.0, 6.6 } } },
+ { "Square4x4", new [,] { { -1.1, 0.0, 0.0, 0.0 }, { 0.0, 1.1, 0.0, 0.0 }, { 0.0, 0.0, 6.2, 0.0}, { 0.0, 0.0, 0.0, -7.7 } } },
+ { "Singular4x4", new [,] { { -1.1, 0.0, 0.0, 0.0 }, { 0.0, -2.2, 0.0, 0.0 }, { 0.0, 0.0, 0.0, 0.0}, { 0.0, 0.0, 0.0, -4.4 } } },
+ { "Tall3x2", new [,] { { -1.1, 0.0 }, { 0.0, 1.1 }, { 0.0, 0.0 } } },
+ { "Wide2x3", new [,] { { -1.1, 0.0, 0.0 }, { 0.0, 1.1, 0.0 } } }
+ };
+
+ TestMatrices = new Dictionary();
+ foreach (var name in TestData2D.Keys)
+ {
+ TestMatrices.Add(name, CreateMatrix(TestData2D[name]));
+ }
+ }
+
+ protected override Matrix CreateMatrix(int rows, int columns)
+ {
+ return new DiagonalMatrix(rows, columns);
+ }
+
+ protected override Matrix CreateMatrix(double[,] data)
+ {
+ return new DiagonalMatrix(data);
+ }
+
+ protected override Vector CreateVector(int size)
+ {
+ return new DenseVector(size);
+ }
+
+ protected override Vector CreateVector(double[] data)
+ {
+ return new DenseVector(data);
+ }
+
+ [Test]
+ public void CanCreateMatrixFromDiagonalArray()
+ {
+ var testData = new Dictionary
+ {
+ { "Singular3x3", new DiagonalMatrix(3, 3, new[] { 1.0, 0.0, 3.0}) },
+ { "Square3x3", new DiagonalMatrix(4, 4, new[] { -1.1, 1.1, 6.6 }) },
+ { "Square4x4", new DiagonalMatrix(4, 4, new[] { -1.1, 1.1, 6.2, -7.7 }) },
+ { "Tall3x2", new DiagonalMatrix(3, 2, new[] { -1.1, 1.1 }) },
+ { "Wide2x3", new DiagonalMatrix(2, 3, new[] { -1.1, 1.1 }) },
+ };
+
+ foreach (var name in testData.Keys)
+ {
+ Assert.AreEqual(TestMatrices[name], testData[name]);
+ }
+ }
+
+ [Test]
+ public void MatrixFrom1DArrayIsReference()
+ {
+ var data = new double[] { 1, 2, 3, 4, 5};
+ 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], result[i, i]);
+ }
+
+ result = data.PointwiseDivide(other);
+ for (var i = 0; i < min; i++)
+ {
+ Assert.AreEqual(data[i, i] / other[i, i], result[i, i]);
+ }
+ }
+ }
+
+ 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();
+ }
+ }
+}
diff --git a/src/UnitTests/LinearAlgebraTests/Double/Factorization/GramSchmidtTests.cs b/src/UnitTests/LinearAlgebraTests/Double/Factorization/GramSchmidtTests.cs
new file mode 100644
index 00000000..0ecbe6c9
--- /dev/null
+++ b/src/UnitTests/LinearAlgebraTests/Double/Factorization/GramSchmidtTests.cs
@@ -0,0 +1,330 @@
+//
+// 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.
+//
+
+namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double.Factorization
+{
+ using MbUnit.Framework;
+ using LinearAlgebra.Double.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], matrixQfromR[i, j], 1.0e-11);
+ }
+ }
+ }
+
+ [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], bReconstruct[i], 1.0e-11);
+ }
+
+ // 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], matrixBReconstruct[i, j], 1.0e-11);
+ }
+ }
+
+ // 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], bReconstruct[i], 1.0e-11);
+ }
+
+ // 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], matrixBReconstruct[i, j], 1.0e-11);
+ }
+ }
+
+ // 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]);
+ }
+ }
+ }
+ }
+}
diff --git a/src/UnitTests/LinearAlgebraTests/Double/LinearAlgebraProviderTests.cs b/src/UnitTests/LinearAlgebraTests/Double/LinearAlgebraProviderTests.cs
index c89c5ed0..cd6ac547 100644
--- a/src/UnitTests/LinearAlgebraTests/Double/LinearAlgebraProviderTests.cs
+++ b/src/UnitTests/LinearAlgebraTests/Double/LinearAlgebraProviderTests.cs
@@ -147,8 +147,8 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[Row("Tall3x2", "Wide2x3")]
public void CanMatrixMultiply(string nameX, string nameY)
{
- var x = (DenseMatrix)testMatrices[nameX];
- var y = (DenseMatrix)testMatrices[nameY];
+ var x = (DenseMatrix)TestMatrices[nameX];
+ var y = (DenseMatrix)TestMatrices[nameY];
var c = (DenseMatrix)CreateMatrix(x.RowCount, y.ColumnCount);
Provider.MatrixMultiply(x.Data, x.RowCount, x.ColumnCount, y.Data, y.RowCount, y.ColumnCount, c.Data);
@@ -170,8 +170,8 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[Row("Tall3x2", "Wide2x3")]
public void CanMatrixMultiplyWithUpdate(string nameX, string nameY)
{
- var x = (DenseMatrix)testMatrices[nameX];
- var y = (DenseMatrix)testMatrices[nameY];
+ var x = (DenseMatrix)TestMatrices[nameX];
+ var y = (DenseMatrix)TestMatrices[nameY];
var c = (DenseMatrix)CreateMatrix(x.RowCount, y.ColumnCount);
Provider.MatrixMultiplyWithUpdate(Transpose.DontTranspose, Transpose.DontTranspose, 2.0, x.Data, x.RowCount, x.ColumnCount, y.Data, y.RowCount, y.ColumnCount, 1.0, c.Data);
diff --git a/src/UnitTests/LinearAlgebraTests/Double/MatrixLoader.cs b/src/UnitTests/LinearAlgebraTests/Double/MatrixLoader.cs
index 776bf053..533836cd 100644
--- a/src/UnitTests/LinearAlgebraTests/Double/MatrixLoader.cs
+++ b/src/UnitTests/LinearAlgebraTests/Double/MatrixLoader.cs
@@ -37,8 +37,8 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
public abstract class MatrixLoader
{
- protected Dictionary testData2D;
- protected Dictionary testMatrices;
+ protected Dictionary TestData2D;
+ protected Dictionary TestMatrices;
protected abstract Matrix CreateMatrix(int rows, int columns);
protected abstract Matrix CreateMatrix(double[,] data);
@@ -46,20 +46,22 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
protected abstract Vector CreateVector(double[] data);
[SetUp]
- public void SetupMatrices()
+ public virtual void SetupMatrices()
{
- testData2D = new Dictionary();
- testData2D.Add("Singular3x3", new double[,] { { 1, 1, 2 }, { 1, 1, 2 }, { 1, 1, 2 } });
- testData2D.Add("Square3x3", new double[,] { { -1.1, -2.2, -3.3 }, { 0, 1.1, 2.2 }, { -4.4, 5.5, 6.6 } });
- testData2D.Add("Square4x4", new double[,] { { -1.1, -2.2, -3.3, -4.4 }, { 0, 1.1, 2.2, 3.3 }, { 1.0, 2.1, 6.2, 4.3 }, { -4.4, 5.5, 6.6, -7.7 } });
- testData2D.Add("Singular4x4", new double[,] { { -1.1, -2.2, -3.3, -4.4 }, { -1.1, -2.2, -3.3, -4.4 }, { -1.1, -2.2, -3.3, -4.4 }, { -1.1, -2.2, -3.3, -4.4 } });
- testData2D.Add("Tall3x2", new double[,] { { -1.1, -2.2 }, { 0, 1.1 }, { -4.4, 5.5 } });
- testData2D.Add("Wide2x3", new double[,] { { -1.1, -2.2, -3.3 }, { 0, 1.1, 2.2 } });
-
- testMatrices = new Dictionary();
- foreach (var name in testData2D.Keys)
+ TestData2D = new Dictionary
+ {
+ { "Singular3x3", new [,] { { 1.0, 1.0, 2.0 }, { 1.0, 1.0, 2.0 }, { 1.0, 1.0, 2.0 } } },
+ { "Square3x3", new[,] { { -1.1, -2.2, -3.3 }, { 0.0, 1.1, 2.2 }, { -4.4, 5.5, 6.6 } } },
+ { "Square4x4", new[,] { { -1.1, -2.2, -3.3, -4.4 }, { 0.0, 1.1, 2.2, 3.3 }, { 1.0, 2.1, 6.2, 4.3 }, { -4.4, 5.5, 6.6, -7.7 } } },
+ { "Singular4x4", new[,] { { -1.1, -2.2, -3.3, -4.4 }, { -1.1, -2.2, -3.3, -4.4 }, { -1.1, -2.2, -3.3, -4.4 }, { -1.1, -2.2, -3.3, -4.4 } } },
+ { "Tall3x2", new[,] { { -1.1, -2.2 }, { 0.0, 1.1 }, { -4.4, 5.5 } } },
+ { "Wide2x3", new[,] { { -1.1, -2.2, -3.3 }, { 0.0, 1.1, 2.2 } } },
+ };
+
+ TestMatrices = new Dictionary();
+ foreach (var name in TestData2D.Keys)
{
- testMatrices.Add(name, CreateMatrix(testData2D[name]));
+ TestMatrices.Add(name, CreateMatrix(TestData2D[name]));
}
}
@@ -68,17 +70,17 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
// Fill a matrix with standard random numbers.
var normal = new Distributions.Normal();
normal.RandomSource = new Random.MersenneTwister(1);
- var A = new DenseMatrix(row, col);
+ var matrixA = new DenseMatrix(row, col);
for (int i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
{
- A[i, j] = normal.Sample();
+ matrixA[i, j] = normal.Sample();
}
}
// Generate a matrix which is positive definite.
- return A;
+ return matrixA;
}
public static Matrix GenerateRandomPositiveDefiniteDenseMatrix(int order)
@@ -86,17 +88,17 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
// Fill a matrix with standard random numbers.
var normal = new Distributions.Normal();
normal.RandomSource = new Random.MersenneTwister(1);
- var A = new DenseMatrix(order);
+ var matrixA = new DenseMatrix(order);
for (int i = 0; i < order; i++)
{
for (int j = 0; j < order; j++)
{
- A[i, j] = normal.Sample();
+ matrixA[i, j] = normal.Sample();
}
}
// Generate a matrix which is positive definite.
- return A.Transpose() * A;
+ return matrixA.Transpose() * matrixA;
}
public static Vector GenerateRandomDenseVector(int order)
@@ -119,17 +121,17 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
// Fill a matrix with standard random numbers.
var normal = new Distributions.Normal();
normal.RandomSource = new Random.MersenneTwister(1);
- var A = new UserDefinedMatrix(row, col);
+ var matrixA = new UserDefinedMatrix(row, col);
for (int i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
{
- A[i, j] = normal.Sample();
+ matrixA[i, j] = normal.Sample();
}
}
// Generate a matrix which is positive definite.
- return A;
+ return matrixA;
}
public static Matrix GenerateRandomPositiveDefiniteUserDefinedMatrix(int order)
@@ -137,17 +139,17 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
// Fill a matrix with standard random numbers.
var normal = new Distributions.Normal();
normal.RandomSource = new Random.MersenneTwister(1);
- var A = new UserDefinedMatrix(order);
+ var matrixA = new UserDefinedMatrix(order);
for (int i = 0; i < order; i++)
{
for (int j = 0; j < order; j++)
{
- A[i, j] = normal.Sample();
+ matrixA[i, j] = normal.Sample();
}
}
// Generate a matrix which is positive definite.
- return A.Transpose() * A;
+ return matrixA.Transpose() * matrixA;
}
public static Vector GenerateRandomUserDefinedVector(int order)
diff --git a/src/UnitTests/LinearAlgebraTests/Double/MatrixTests.Arithmetic.cs b/src/UnitTests/LinearAlgebraTests/Double/MatrixTests.Arithmetic.cs
index 5aa83c8d..a405cc3c 100644
--- a/src/UnitTests/LinearAlgebraTests/Double/MatrixTests.Arithmetic.cs
+++ b/src/UnitTests/LinearAlgebraTests/Double/MatrixTests.Arithmetic.cs
@@ -40,7 +40,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[MultipleAsserts]
public void CanMultiplyWithScalar(double scalar)
{
- var matrix = this.testMatrices["Singular3x3"];
+ var matrix = this.TestMatrices["Singular3x3"];
var clone = matrix.Clone();
clone.Multiply(scalar);
@@ -56,7 +56,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[Test]
public void CanMultiplyWithVector()
{
- var A = this.testMatrices["Singular3x3"];
+ var A = this.TestMatrices["Singular3x3"];
var x = new DenseVector(new[] { 1.0, 2.0, 3.0 });
var y = A * x;
@@ -73,7 +73,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[Test]
public void CanMultiplyWithVectorIntoResult()
{
- var A = this.testMatrices["Singular3x3"];
+ var A = this.TestMatrices["Singular3x3"];
var x = new DenseVector(new[] { 1.0, 2.0, 3.0 });
var y = new DenseVector(3);
A.Multiply(x, y);
@@ -89,7 +89,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[Test]
public void CanMultiplyWithVectorIntoResultWhenUpdatingInputArgument()
{
- var A = this.testMatrices["Singular3x3"];
+ var A = this.TestMatrices["Singular3x3"];
var x = new DenseVector(new[] { 1.0, 2.0, 3.0 });
var y = x;
A.Multiply(x, x);
@@ -109,7 +109,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedArgumentNullException]
public void MultiplyWithVectorIntoResultFailsWhenResultIsNull()
{
- var A = this.testMatrices["Singular3x3"];
+ var A = this.TestMatrices["Singular3x3"];
var x = new DenseVector(new[] { 1.0, 2.0, 3.0 });
Vector y = null;
A.Multiply(x, y);
@@ -119,7 +119,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedArgumentException]
public void MultiplyWithVectorIntoResultFailsWhenResultIsTooLarge()
{
- var A = this.testMatrices["Singular3x3"];
+ var A = this.TestMatrices["Singular3x3"];
var x = new DenseVector(new[] { 1.0, 2.0, 3.0 });
Vector y = new DenseVector(4);
A.Multiply(x, y);
@@ -132,7 +132,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[MultipleAsserts]
public void CanOperatorLeftMultiplyWithScalar(double scalar)
{
- var matrix = this.testMatrices["Singular3x3"];
+ var matrix = this.TestMatrices["Singular3x3"];
var clone = matrix * scalar;
for (var i = 0; i < matrix.RowCount; i++)
@@ -151,7 +151,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[MultipleAsserts]
public void CanOperatorRightMultiplyWithScalar(double scalar)
{
- var matrix = this.testMatrices["Singular3x3"];
+ var matrix = this.TestMatrices["Singular3x3"];
var clone = matrix * scalar;
for (var i = 0; i < matrix.RowCount; i++)
@@ -170,7 +170,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[MultipleAsserts]
public void CanMultiplyWithScalarIntoResult(double scalar)
{
- var matrix = this.testMatrices["Singular3x3"];
+ var matrix = this.TestMatrices["Singular3x3"];
var result = matrix.Clone();
matrix.Multiply(scalar, result);
@@ -187,7 +187,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedException(typeof(ArgumentNullException))]
public void MultiplyWithScalarIntoResultFailsWhenResultIsNull()
{
- var matrix = this.testMatrices["Singular3x3"];
+ var matrix = this.TestMatrices["Singular3x3"];
Matrix result = null;
matrix.Multiply(2.3, result);
}
@@ -196,7 +196,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedArgumentException]
public void MultiplyWithScalarFailsWhenResultHasMoreRows()
{
- var matrix = this.testMatrices["Singular3x3"];
+ var matrix = this.TestMatrices["Singular3x3"];
var result = this.CreateMatrix(matrix.RowCount + 1, matrix.ColumnCount);
matrix.Multiply(2.3, result);
}
@@ -205,7 +205,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedArgumentException]
public void MultiplyWithScalarFailsWhenResultHasMoreColumns()
{
- var matrix = this.testMatrices["Singular3x3"];
+ var matrix = this.TestMatrices["Singular3x3"];
var result = this.CreateMatrix(matrix.RowCount, matrix.ColumnCount + 1);
matrix.Multiply(2.3, result);
}
@@ -231,8 +231,8 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[Row("Singular4x4", "Square4x4")]
public void CanAddMatrix(string mtxA, string mtxB)
{
- var A = this.testMatrices[mtxA];
- var B = this.testMatrices[mtxB];
+ var A = this.TestMatrices[mtxA];
+ var B = this.TestMatrices[mtxB];
var matrix = A.Clone();
matrix.Add(B);
@@ -249,7 +249,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedException(typeof(ArgumentNullException))]
public void AddMatrixThrowsExceptionWhenArgumentIsNull()
{
- var matrix = this.testMatrices["Singular4x4"];
+ var matrix = this.TestMatrices["Singular4x4"];
Matrix other = null;
matrix.Add(other);
}
@@ -258,8 +258,8 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void AddMatrixThrowsExceptionArgumentHasTooFewColumns()
{
- var matrix = this.testMatrices["Singular3x3"];
- var other = this.testMatrices["Tall3x2"];
+ var matrix = this.TestMatrices["Singular3x3"];
+ var other = this.TestMatrices["Tall3x2"];
matrix.Add(other);
}
@@ -267,8 +267,8 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void AddMatrixThrowsExceptionArgumentHasTooFewRows()
{
- var matrix = this.testMatrices["Singular3x3"];
- var other = this.testMatrices["Wide2x3"];
+ var matrix = this.TestMatrices["Singular3x3"];
+ var other = this.TestMatrices["Wide2x3"];
matrix.Add(other);
}
@@ -277,8 +277,8 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[Row("Singular4x4", "Square4x4")]
public void AddOperator(string mtxA, string mtxB)
{
- var A = this.testMatrices[mtxA];
- var B = this.testMatrices[mtxB];
+ var A = this.TestMatrices[mtxA];
+ var B = this.TestMatrices[mtxB];
var result = A + B;
for (var i = 0; i < A.RowCount; i++)
@@ -295,7 +295,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
public void AddOperatorThrowsExceptionWhenLeftsideIsNull()
{
Matrix matrix = null;
- var other = this.testMatrices["Singular3x3"];
+ var other = this.TestMatrices["Singular3x3"];
var result = matrix + other;
}
@@ -303,7 +303,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedException(typeof(ArgumentNullException))]
public void AddOperatorThrowsExceptionWhenRightsideIsNull()
{
- var matrix = this.testMatrices["Singular3x3"];
+ var matrix = this.TestMatrices["Singular3x3"];
Matrix other = null;
var result = matrix + other;
}
@@ -312,8 +312,8 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void AddOperatorThrowsExceptionWhenRightsideHasTooFewColumns()
{
- var matrix = this.testMatrices["Singular3x3"];
- var other = this.testMatrices["Tall3x2"];
+ var matrix = this.TestMatrices["Singular3x3"];
+ var other = this.TestMatrices["Tall3x2"];
var result = matrix + other;
}
@@ -321,8 +321,8 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void AddOperatorThrowsExceptionWhenRightsideHasTooFewRows()
{
- var matrix = this.testMatrices["Singular3x3"];
- var other = this.testMatrices["Wide2x3"];
+ var matrix = this.TestMatrices["Singular3x3"];
+ var other = this.TestMatrices["Wide2x3"];
var result = matrix + other;
}
@@ -331,8 +331,8 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[Row("Singular4x4", "Square4x4")]
public void CanSubtractMatrix(string mtxA, string mtxB)
{
- var A = this.testMatrices[mtxA];
- var B = this.testMatrices[mtxB];
+ var A = this.TestMatrices[mtxA];
+ var B = this.TestMatrices[mtxB];
var matrix = A.Clone();
matrix.Subtract(B);
@@ -349,7 +349,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedException(typeof(ArgumentNullException))]
public void SubtractMatrixThrowsExceptionWhenRightSideIsNull()
{
- var matrix = this.testMatrices["Singular4x4"];
+ var matrix = this.TestMatrices["Singular4x4"];
Matrix other = null;
matrix.Subtract(other);
}
@@ -358,8 +358,8 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void SubtractMatrixThrowsExceptionWhenRightSideHasTooFewColumns()
{
- var matrix = this.testMatrices["Singular3x3"];
- var other = this.testMatrices["Tall3x2"];
+ var matrix = this.TestMatrices["Singular3x3"];
+ var other = this.TestMatrices["Tall3x2"];
matrix.Subtract(other);
}
@@ -367,8 +367,8 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void SubtractMatrixThrowsExceptionWhenRightSideHasTooFewRows()
{
- var matrix = this.testMatrices["Singular3x3"];
- var other = this.testMatrices["Wide2x3"];
+ var matrix = this.TestMatrices["Singular3x3"];
+ var other = this.TestMatrices["Wide2x3"];
matrix.Subtract(other);
}
@@ -377,8 +377,8 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[Row("Singular4x4", "Square4x4")]
public void SubtractOperator(string mtxA, string mtxB)
{
- var A = this.testMatrices[mtxA];
- var B = this.testMatrices[mtxB];
+ var A = this.TestMatrices[mtxA];
+ var B = this.TestMatrices[mtxB];
var result = A - B;
for (var i = 0; i < A.RowCount; i++)
@@ -395,7 +395,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
public void SubtractOperatorThrowsExceptionWhenLeftsideIsNull()
{
Matrix matrix = null;
- var other = this.testMatrices["Singular3x3"];
+ var other = this.TestMatrices["Singular3x3"];
var result = matrix - other;
}
@@ -403,7 +403,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedException(typeof(ArgumentNullException))]
public void SubtractOperatorThrowsExceptionWhenRightsideIsNull()
{
- var matrix = this.testMatrices["Singular3x3"];
+ var matrix = this.TestMatrices["Singular3x3"];
Matrix other = null;
var result = matrix - other;
}
@@ -412,8 +412,8 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void SubtractOperatorThrowsExceptionWhenRightsideHasTooFewColumns()
{
- var matrix = this.testMatrices["Singular3x3"];
- var other = this.testMatrices["Tall3x2"];
+ var matrix = this.TestMatrices["Singular3x3"];
+ var other = this.TestMatrices["Tall3x2"];
var result = matrix - other;
}
@@ -421,8 +421,8 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void SubtractOperatorThrowsExceptionWhenRightsideHasTooFewRows()
{
- var matrix = this.testMatrices["Singular3x3"];
- var other = this.testMatrices["Wide2x3"];
+ var matrix = this.TestMatrices["Singular3x3"];
+ var other = this.TestMatrices["Wide2x3"];
var result = matrix - other;
}
@@ -435,8 +435,8 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[MultipleAsserts]
public void CanMultiplyMatrixWithMatrix(string nameA, string nameB)
{
- var A = this.testMatrices[nameA];
- var B = this.testMatrices[nameB];
+ var A = this.TestMatrices[nameA];
+ var B = this.TestMatrices[nameB];
var C = A * B;
Assert.AreEqual(C.RowCount, A.RowCount);
@@ -451,12 +451,80 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
}
}
+ [Test]
+ [Row("Singular3x3")]
+ [Row("Singular4x4")]
+ [Row("Wide2x3")]
+ [Row("Tall3x2")]
+ [MultipleAsserts]
+ public void CanTransposeAndMultiplyMatrixWithMatrix(string nameA)
+ {
+ var A = this.TestMatrices[nameA];
+ var B = this.TestMatrices[nameA];
+ var C = A.TransposeAndMultiply(B);
+
+ Assert.AreEqual(C.RowCount, A.RowCount);
+ Assert.AreEqual(C.ColumnCount, B.RowCount);
+
+ for (var i = 0; i < C.RowCount; i++)
+ {
+ for (var j = 0; j < C.ColumnCount; j++)
+ {
+ AssertHelpers.AlmostEqual(A.Row(i) * B.Row(j), C[i, j], 15);
+ }
+ }
+ }
+
+ [Test]
+ [ExpectedException(typeof(ArgumentException))]
+ public void TransposeAndMultiplyMatrixMatrixFailsWhenSizesAreIncompatible()
+ {
+ var matrix = this.TestMatrices["Singular3x3"];
+ var other = this.TestMatrices["Tall3x2"];
+ var result = matrix.TransposeAndMultiply(other);
+ }
+
+ [Test]
+ [ExpectedException(typeof(ArgumentNullException))]
+ public void TransposeAndMultiplyMatrixMatrixFailsWhenRightArgumentIsNull()
+ {
+ var matrix = this.TestMatrices["Wide2x3"];
+ Matrix 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 A = this.TestMatrices[nameA];
+ var B = this.TestMatrices[nameA];
+ var C = this.CreateMatrix(A.RowCount, B.RowCount);
+ A.TransposeAndMultiply(B, C);
+
+ Assert.AreEqual(C.RowCount, A.RowCount);
+ Assert.AreEqual(C.ColumnCount, B.RowCount);
+
+ for (var i = 0; i < C.RowCount; i++)
+ {
+ for (var j = 0; j < C.ColumnCount; j++)
+ {
+ AssertHelpers.AlmostEqual(A.Row(i) * B.Row(j), C[i, j], 15);
+ }
+ }
+ }
+
[Test]
[ExpectedException(typeof(ArgumentException))]
public void MultiplyMatrixMatrixFailsWhenSizesAreIncompatible()
{
- var matrix = this.testMatrices["Singular3x3"];
- var other = this.testMatrices["Wide2x3"];
+ var matrix = this.TestMatrices["Singular3x3"];
+ var other = this.TestMatrices["Wide2x3"];
var result = matrix * other;
}
@@ -465,7 +533,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
public void MultiplyMatrixMatrixFailsWhenLeftArgumentIsNull()
{
Matrix matrix = null;
- var other = this.testMatrices["Wide2x3"];
+ var other = this.TestMatrices["Wide2x3"];
var result = matrix * other;
}
@@ -473,7 +541,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedException(typeof(ArgumentNullException))]
public void MultiplyMatrixMatrixFailsWhenRightArgumentIsNull()
{
- var matrix = this.testMatrices["Wide2x3"];
+ var matrix = this.TestMatrices["Wide2x3"];
Matrix other = null;
var result = matrix * other;
}
@@ -485,10 +553,10 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[Row("Wide2x3", "Tall3x2")]
[Row("Tall3x2", "Wide2x3")]
[MultipleAsserts]
- public void CanMultiplyMatrixWithMatrixIntoResult(string nameA, string nameB)
+ public virtual void CanMultiplyMatrixWithMatrixIntoResult(string nameA, string nameB)
{
- var A = this.testMatrices[nameA];
- var B = this.testMatrices[nameB];
+ var A = this.TestMatrices[nameA];
+ var B = this.TestMatrices[nameB];
var C = this.CreateMatrix(A.RowCount, B.ColumnCount);
A.Multiply(B, C);
@@ -513,7 +581,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[MultipleAsserts]
public void CanNegate(string name)
{
- var matrix = this.testMatrices[name];
+ var matrix = this.TestMatrices[name];
var copy = matrix.Clone();
copy.Negate();
@@ -536,7 +604,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[MultipleAsserts]
public void CanNegateIntoResult(string name)
{
- var matrix = this.testMatrices[name];
+ var matrix = this.TestMatrices[name];
var copy = matrix.Clone();
matrix.Negate(copy);
@@ -554,7 +622,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedArgumentNullException]
public void NegateIntoResultFailsWhenResultIsNull()
{
- var matrix = this.testMatrices["Singular3x3"];
+ var matrix = this.TestMatrices["Singular3x3"];
Matrix copy = null;
matrix.Negate(copy);
}
@@ -563,7 +631,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedArgumentException]
public void NegateIntoResultFailsWhenResultHasMoreRows()
{
- var matrix = this.testMatrices["Singular3x3"];
+ var matrix = this.TestMatrices["Singular3x3"];
var target = this.CreateMatrix(matrix.RowCount + 1, matrix.ColumnCount);
matrix.Negate(target);
}
@@ -572,7 +640,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedArgumentException]
public void NegateIntoResultFailsWhenResultHasMoreColumns()
{
- var matrix = this.testMatrices["Singular3x3"];
+ var matrix = this.TestMatrices["Singular3x3"];
var target = this.CreateMatrix(matrix.RowCount + 1, matrix.ColumnCount);
matrix.Negate(target);
}
@@ -581,8 +649,8 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[Test]
public void KroneckerProduct()
{
- var A = this.testMatrices["Wide2x3"];
- var B = this.testMatrices["Square3x3"];
+ var A = this.TestMatrices["Wide2x3"];
+ var B = this.TestMatrices["Square3x3"];
var result = this.CreateMatrix(A.RowCount * B.RowCount, A.ColumnCount * B.ColumnCount);
A.KroneckerProduct(B, result);
for (var i = 0; i < A.RowCount; i++)
@@ -603,8 +671,8 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[Test]
public void KroneckerProductResult()
{
- var A = this.testMatrices["Wide2x3"];
- var B = this.testMatrices["Square3x3"];
+ var A = this.TestMatrices["Wide2x3"];
+ var B = this.TestMatrices["Square3x3"];
var result = A.KroneckerProduct(B);
for (var i = 0; i < A.RowCount; i++)
{
@@ -627,7 +695,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[Row(-4, ExpectedException = typeof(ArgumentOutOfRangeException))]
public void NormalizeColumns(int pValue)
{
- var matrix = this.testMatrices["Singular3x3"];
+ var matrix = this.TestMatrices["Singular3x3"];
var result = matrix.NormalizeColumns(pValue);
for (var j = 0; j < result.ColumnCount; j++)
{
@@ -642,7 +710,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[Row(-3, ExpectedException = typeof(ArgumentOutOfRangeException))]
public void NormalizeRows(int pValue)
{
- var matrix = this.testMatrices["Singular3x3"].NormalizeRows(pValue);
+ var matrix = this.TestMatrices["Singular3x3"].NormalizeRows(pValue);
for (var i = 0; i < matrix.RowCount; i++)
{
var row = matrix.Row(i);
@@ -653,7 +721,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[Test]
public void PointwiseMultiplyResult()
{
- foreach (var data in this.testMatrices.Values)
+ foreach (var data in this.TestMatrices.Values)
{
var other = data.Clone();
var result = data.Clone();
@@ -681,7 +749,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedException(typeof(ArgumentNullException))]
public void PointwiseMultiplyWithNullOtherShouldThrowException()
{
- var matrix = this.testMatrices["Wide2x3"];
+ var matrix = this.TestMatrices["Wide2x3"];
Matrix other = null;
var result = matrix.Clone();
matrix.PointwiseMultiply(other, result);
@@ -691,7 +759,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedException(typeof(ArgumentNullException))]
public void PointwiseMultiplyWithResultNullShouldThrowException()
{
- var matrix = this.testMatrices["Wide2x3"];
+ var matrix = this.TestMatrices["Wide2x3"];
var other = matrix.Clone();
matrix.PointwiseMultiply(other, null);
}
@@ -700,7 +768,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedException(typeof(ArgumentException))]
public void PointwiseMultiplyWithInvalidOtherMatrixDimensionsShouldThrowException()
{
- var matrix = this.testMatrices["Wide2x3"];
+ var matrix = this.TestMatrices["Wide2x3"];
var other = this.CreateMatrix(matrix.RowCount + 1, matrix.ColumnCount);
var result = matrix.Clone();
matrix.PointwiseMultiply(other, result);
@@ -710,16 +778,16 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedException(typeof(ArgumentException))]
public void PointwiseMultiplyWithInvalidResultMatrixDimensionsShouldThrowException()
{
- var matrix = this.testMatrices["Wide2x3"];
+ var matrix = this.TestMatrices["Wide2x3"];
var other = matrix.Clone();
var result = this.CreateMatrix(matrix.RowCount + 1, matrix.ColumnCount);
matrix.PointwiseMultiply(other, result);
}
[Test]
- public void PointwiseDivideIResult()
+ public virtual void PointwiseDivideResult()
{
- foreach (var data in this.testMatrices.Values)
+ foreach (var data in this.TestMatrices.Values)
{
var other = data.Clone();
var result = data.Clone();
@@ -747,7 +815,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedException(typeof(ArgumentNullException))]
public void PointwiseDivideWithNullOtherShouldThrowException()
{
- var matrix = this.testMatrices["Wide2x3"];
+ var matrix = this.TestMatrices["Wide2x3"];
Matrix other = null;
var result = matrix.Clone();
matrix.PointwiseDivide(other, result);
@@ -757,7 +825,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedException(typeof(ArgumentNullException))]
public void PointwiseDivideWithResultNullShouldThrowException()
{
- var matrix = this.testMatrices["Wide2x3"];
+ var matrix = this.TestMatrices["Wide2x3"];
var other = matrix.Clone();
matrix.PointwiseDivide(other, null);
}
@@ -766,7 +834,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedException(typeof(ArgumentException))]
public void PointwiseDivideWithInvalidOtherMatrixDimensionsShouldThrowException()
{
- var matrix = this.testMatrices["Wide2x3"];
+ var matrix = this.TestMatrices["Wide2x3"];
var other = this.CreateMatrix(matrix.RowCount + 1, matrix.ColumnCount);
var result = matrix.Clone();
matrix.PointwiseDivide(other, result);
@@ -776,7 +844,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedException(typeof(ArgumentException))]
public void PointwiseDivideWithInvalidResultMatrixDimensionsShouldThrowException()
{
- var matrix = this.testMatrices["Wide2x3"];
+ var matrix = this.TestMatrices["Wide2x3"];
var other = matrix.Clone();
var result = this.CreateMatrix(matrix.RowCount + 1, matrix.ColumnCount);
matrix.PointwiseDivide(other, result);
@@ -803,7 +871,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[Test]
public void Trace()
{
- var matrix = this.testMatrices["Square3x3"];
+ var matrix = this.TestMatrices["Square3x3"];
var trace = matrix.Trace();
Assert.AreEqual(6.6, trace);
}
@@ -812,7 +880,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedArgumentException]
public void TraceOfNonSquareMatrixShouldThrowException()
{
- var matrix = this.testMatrices["Wide2x3"];
+ var matrix = this.TestMatrices["Wide2x3"];
var trace = matrix.Trace();
}
}
diff --git a/src/UnitTests/LinearAlgebraTests/Double/MatrixTests.cs b/src/UnitTests/LinearAlgebraTests/Double/MatrixTests.cs
index be0eb6e2..5e025892 100644
--- a/src/UnitTests/LinearAlgebraTests/Double/MatrixTests.cs
+++ b/src/UnitTests/LinearAlgebraTests/Double/MatrixTests.cs
@@ -42,7 +42,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[MultipleAsserts]
public void CanCloneMatrix(string name)
{
- var matrix = this.CreateMatrix(this.testData2D[name]);
+ var matrix = this.CreateMatrix(this.TestData2D[name]);
var clone = matrix.Clone();
Assert.AreNotSame(matrix, clone);
@@ -66,7 +66,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[MultipleAsserts]
public void CanCloneMatrixUsingICloneable(string name)
{
- var matrix = this.testMatrices[name];
+ var matrix = this.TestMatrices[name];
var clone = (Matrix)((ICloneable)matrix).Clone();
Assert.AreNotSame(matrix, clone);
@@ -90,7 +90,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[MultipleAsserts]
public void CanCopyTo(string name)
{
- var matrix = this.testMatrices[name];
+ var matrix = this.TestMatrices[name];
var copy = this.CreateMatrix(matrix.RowCount, matrix.ColumnCount);
matrix.CopyTo(copy);
@@ -108,7 +108,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedArgumentNullException]
public void CopyToFailsWhenTargetIsNull()
{
- var matrix = this.testMatrices["Singular3x3"];
+ var matrix = this.TestMatrices["Singular3x3"];
Matrix target = null;
matrix.CopyTo(target);
}
@@ -117,7 +117,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedArgumentException]
public void CopyToFailsWhenTargetHasMoreRows()
{
- var matrix = this.testMatrices["Singular3x3"];
+ var matrix = this.TestMatrices["Singular3x3"];
var target = this.CreateMatrix(matrix.RowCount + 1, matrix.ColumnCount);
matrix.CopyTo(target);
}
@@ -126,7 +126,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedArgumentException]
public void CopyToFailsWhenTargetHasMoreColumns()
{
- var matrix = this.testMatrices["Singular3x3"];
+ var matrix = this.TestMatrices["Singular3x3"];
var target = this.CreateMatrix(matrix.RowCount + 1, matrix.ColumnCount);
matrix.CopyTo(target);
}
@@ -154,9 +154,9 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[MultipleAsserts]
public void CanEquateMatrices(string name)
{
- var matrix1 = this.CreateMatrix(this.testData2D[name]);
- var matrix2 = this.CreateMatrix(this.testData2D[name]);
- var matrix3 = this.CreateMatrix(this.testData2D[name].GetLength(0), this.testData2D[name].GetLength(1));
+ var matrix1 = this.CreateMatrix(this.TestData2D[name]);
+ var matrix2 = this.CreateMatrix(this.TestData2D[name]);
+ var matrix3 = this.CreateMatrix(this.TestData2D[name].GetLength(0), this.TestData2D[name].GetLength(1));
Assert.IsTrue(matrix1.Equals(matrix1));
Assert.IsTrue(matrix1.Equals(matrix2));
Assert.IsFalse(matrix1.Equals(matrix3));
@@ -183,7 +183,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[Row("Wide2x3")]
public void TestingForEqualityWithNonMatrixReturnsFalse(string name)
{
- var matrix = this.CreateMatrix(this.testData2D[name]);
+ var matrix = this.CreateMatrix(this.TestData2D[name]);
Assert.IsFalse(matrix.Equals(2));
}
@@ -195,8 +195,8 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[Row("Wide2x3")]
public void CanTestForEqualityUsingObjectEquals(string name)
{
- var matrix1 = this.CreateMatrix(this.testData2D[name]);
- var matrix2 = this.CreateMatrix(this.testData2D[name]);
+ var matrix1 = this.CreateMatrix(this.TestData2D[name]);
+ var matrix2 = this.CreateMatrix(this.TestData2D[name]);
Assert.IsTrue(matrix1.Equals((object)matrix2));
}
@@ -207,7 +207,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void RangeCheckFails(int i, int j, string name)
{
- var d = this.testMatrices[name][i, j];
+ var d = this.TestMatrices[name][i, j];
}
[Test]
@@ -219,7 +219,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[Test]
public void CanClearMatrix()
{
- var matrix = this.testMatrices["Singular3x3"].Clone();
+ var matrix = this.TestMatrices["Singular3x3"].Clone();
matrix.Clear();
for (var i = 0; i < matrix.RowCount; i++)
{
@@ -237,7 +237,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[Row(2, "Square3x3")]
public void CanGetRow(int rowIndex, string name)
{
- var matrix = this.testMatrices[name];
+ var matrix = this.TestMatrices[name];
var row = matrix.Row(rowIndex);
Assert.AreEqual(matrix.ColumnCount, row.Count);
@@ -251,7 +251,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void GetRowThrowsArgumentOutOfRangeWithNegativeIndex()
{
- var matrix = this.testMatrices["Singular3x3"];
+ var matrix = this.TestMatrices["Singular3x3"];
matrix.Row(-1);
}
@@ -259,7 +259,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void GetRowThrowsArgumentOutOfRangeWithOverflowingRowIndex()
{
- var matrix = this.testMatrices["Singular3x3"];
+ var matrix = this.TestMatrices["Singular3x3"];
matrix.Row(matrix.RowCount);
}
@@ -270,7 +270,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[Row(2, "Square3x3")]
public void CanGetRowWithResult(int rowIndex, string name)
{
- var matrix = this.testMatrices[name];
+ var matrix = this.TestMatrices[name];
var row = CreateVector(matrix.ColumnCount);
matrix.Row(rowIndex, row);
@@ -285,7 +285,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedException(typeof(ArgumentNullException))]
public void GetRowWithResultFailsWhenResultIsNull()
{
- var matrix = this.testMatrices["Singular3x3"];
+ var matrix = this.TestMatrices["Singular3x3"];
matrix.Row(0, null);
}
@@ -293,7 +293,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void GetRowWithResultThrowsArgumentOutOfRangeWithNegativeIndex()
{
- var matrix = this.testMatrices["Singular3x3"];
+ var matrix = this.TestMatrices["Singular3x3"];
var row = CreateVector(matrix.ColumnCount);
matrix.Row(-1, row);
}
@@ -302,7 +302,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void GetRowWithResultThrowsArgumentOutOfRangeWithOverflowingRowIndex()
{
- var matrix = this.testMatrices["Singular3x3"];
+ var matrix = this.TestMatrices["Singular3x3"];
var row = CreateVector(matrix.ColumnCount);
matrix.Row(matrix.RowCount, row);
}
@@ -314,7 +314,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[Row(2, 0, 3, "Square3x3")]
public void CanGetRowWithRange(int rowIndex, int start, int length, string name)
{
- var matrix = this.testMatrices[name];
+ var matrix = this.TestMatrices[name];
var row = matrix.Row(rowIndex, start, length);
Assert.AreEqual(length, row.Count);
@@ -328,7 +328,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedException(typeof(ArgumentException))]
public void GetRowWithRangeResultArgumentExeptionWhenLengthIsZero()
{
- var matrix = this.testMatrices["Singular3x3"];
+ var matrix = this.TestMatrices["Singular3x3"];
var result = CreateVector(matrix.ColumnCount);
matrix.Row(0, 0, 0, result);
}
@@ -337,7 +337,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedException(typeof(ArgumentException))]
public void GetRowWithRangeFailsWithTooSmallResultVector()
{
- var matrix = this.testMatrices["Singular3x3"];
+ var matrix = this.TestMatrices["Singular3x3"];
var result = this.CreateVector(matrix.ColumnCount - 1);
matrix.Row(0, 0, 0, result);
}
@@ -349,7 +349,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[Row(2, "Square3x3")]
public void CanGetColumn(int colIndex, string name)
{
- var matrix = this.testMatrices[name];
+ var matrix = this.TestMatrices[name];
var col = matrix.Column(colIndex);
Assert.AreEqual(matrix.RowCount, col.Count);
@@ -363,7 +363,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void GetColumnThrowsArgumentOutOfRangeWithNegativeIndex()
{
- var matrix = this.testMatrices["Singular3x3"];
+ var matrix = this.TestMatrices["Singular3x3"];
matrix.Column(-1);
}
@@ -371,7 +371,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void GetColumnThrowsArgumentOutOfRangeWithOverflowingRowIndex()
{
- var matrix = this.testMatrices["Singular3x3"];
+ var matrix = this.TestMatrices["Singular3x3"];
matrix.Column(matrix.ColumnCount);
}
@@ -382,7 +382,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[Row(2, "Square3x3")]
public void CanGetColumnWithResult(int colIndex, string name)
{
- var matrix = this.testMatrices[name];
+ var matrix = this.TestMatrices[name];
var col = CreateVector(matrix.RowCount);
matrix.Column(colIndex, col);
@@ -397,7 +397,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedException(typeof(ArgumentNullException))]
public void GetColumnFailsWhenResultIsNull()
{
- var matrix = this.testMatrices["Singular3x3"];
+ var matrix = this.TestMatrices["Singular3x3"];
matrix.Column(0, null);
}
@@ -405,7 +405,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void GetColumnWithResultThrowsArgumentOutOfRangeWithNegativeIndex()
{
- var matrix = this.testMatrices["Singular3x3"];
+ var matrix = this.TestMatrices["Singular3x3"];
var column = CreateVector(matrix.ColumnCount);
matrix.Column(-1, column);
}
@@ -414,7 +414,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void GetColumnWithResultThrowsArgumentOutOfRangeWithOverflowingRowIndex()
{
- var matrix = this.testMatrices["Singular3x3"];
+ var matrix = this.TestMatrices["Singular3x3"];
var column = CreateVector(matrix.RowCount);
matrix.Row(matrix.ColumnCount, column);
}
@@ -426,7 +426,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[Row(2, 0, 3, "Square3x3")]
public void CanGetColumnWithRange(int colIndex, int start, int length, string name)
{
- var matrix = this.testMatrices[name];
+ var matrix = this.TestMatrices[name];
var col = matrix.Column(colIndex, start, length);
Assert.AreEqual(length, col.Count);
@@ -440,7 +440,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedException(typeof(ArgumentException))]
public void GetColumnWithRangeResultArgumentExeptionWhenLengthIsZero()
{
- var matrix = this.testMatrices["Singular3x3"];
+ var matrix = this.TestMatrices["Singular3x3"];
var col = CreateVector(matrix.RowCount);
matrix.Column(0, 0, 0, col);
}
@@ -449,7 +449,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedException(typeof(ArgumentException))]
public void GetColumnWithRangeFailsWithTooSmallResultVector()
{
- var matrix = this.testMatrices["Singular3x3"];
+ var matrix = this.TestMatrices["Singular3x3"];
var result = this.CreateVector(matrix.RowCount - 1);
matrix.Column(0, 0, matrix.RowCount, result);
}
@@ -461,7 +461,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[Row(2, "Square3x3")]
public void CanSetRow(int rowIndex, string name)
{
- var matrix = this.testMatrices[name].Clone();
+ var matrix = this.TestMatrices[name].Clone();
matrix.SetRow(rowIndex, CreateVector(matrix.ColumnCount));
for (var i = 0; i < matrix.RowCount; i++)
@@ -474,7 +474,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
}
else
{
- Assert.AreEqual(this.testMatrices[name][i, j], matrix[i, j]);
+ Assert.AreEqual(this.TestMatrices[name][i, j], matrix[i, j]);
}
}
}
@@ -487,7 +487,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[Row(2, "Square3x3")]
public void CanSetColumn(int colIndex, string name)
{
- var matrix = this.testMatrices[name].Clone();
+ var matrix = this.TestMatrices[name].Clone();
matrix.SetColumn(colIndex, CreateVector(matrix.ColumnCount));
for (var i = 0; i < matrix.RowCount; i++)
@@ -500,7 +500,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
}
else
{
- Assert.AreEqual(this.testMatrices[name][i, j], matrix[i, j]);
+ Assert.AreEqual(this.TestMatrices[name][i, j], matrix[i, j]);
}
}
}
@@ -515,7 +515,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[MultipleAsserts]
public void UpperTriangleResult(string name)
{
- var data = this.testMatrices[name];
+ var data = this.TestMatrices[name];
var result = this.CreateMatrix(data.RowCount, data.ColumnCount);
var lower = data.UpperTriangle();
for (var i = 0; i < data.RowCount; i++)
@@ -539,7 +539,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedArgumentNullException]
public void UpperTriangleWithResultNullShouldThrowException()
{
- var data = this.testMatrices["Square3x3"];
+ var data = this.TestMatrices["Square3x3"];
Matrix result = null;
data.UpperTriangle(result);
}
@@ -548,7 +548,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedArgumentException]
public void UpperTriangleWithUnEqualRowsShouldThrowException()
{
- var data = this.testMatrices["Square3x3"];
+ var data = this.TestMatrices["Square3x3"];
var result = this.CreateMatrix(data.RowCount + 1, data.ColumnCount);
data.UpperTriangle(result);
}
@@ -557,7 +557,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedArgumentException]
public void UpperTriangleWithUnEqualColumnsShouldThrowException()
{
- var data = this.testMatrices["Square3x3"];
+ var data = this.TestMatrices["Square3x3"];
var result = this.CreateMatrix(data.RowCount, data.ColumnCount + 1);
data.UpperTriangle(result);
}
@@ -565,7 +565,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[Test]
public void StrictlyLowerTriangle()
{
- foreach (var data in this.testMatrices.Values)
+ foreach (var data in this.TestMatrices.Values)
{
var lower = data.StrictlyLowerTriangle();
for (var i = 0; i < data.RowCount; i++)
@@ -588,7 +588,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[Test]
public void StrictlyLowerTriangleResult()
{
- foreach (var data in this.testMatrices.Values)
+ foreach (var data in this.TestMatrices.Values)
{
var lower = this.CreateMatrix(data.RowCount, data.ColumnCount);
data.StrictlyLowerTriangle(lower);
@@ -613,7 +613,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedArgumentNullException]
public void StrictlyLowerTriangleWithNullParameterShouldThrowException()
{
- var data = this.testMatrices["Square3x3"];
+ var data = this.TestMatrices["Square3x3"];
Matrix lower = null;
data.StrictlyLowerTriangle(lower);
}
@@ -622,7 +622,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedArgumentException]
public void StrictlyLowerTriangleWithInvalidColumnNumberShouldThrowException()
{
- var data = this.testMatrices["Square3x3"];
+ var data = this.TestMatrices["Square3x3"];
var lower = this.CreateMatrix(data.RowCount, data.ColumnCount + 1);
data.StrictlyLowerTriangle(lower);
}
@@ -631,7 +631,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedArgumentException]
public void StrictlyLowerTriangleWithInvalidRowNumberShouldThrowException()
{
- var data = this.testMatrices["Square3x3"];
+ var data = this.TestMatrices["Square3x3"];
var lower = this.CreateMatrix(data.RowCount + 1, data.ColumnCount);
data.StrictlyLowerTriangle(lower);
}
@@ -640,7 +640,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[Test]
public void StrictlyUpperTriangle()
{
- foreach (var data in this.testMatrices.Values)
+ foreach (var data in this.TestMatrices.Values)
{
var lower = data.StrictlyUpperTriangle();
for (var i = 0; i < data.RowCount; i++)
@@ -663,7 +663,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[Test]
public void StrictlyUpperTriangleResult()
{
- foreach (var data in this.testMatrices.Values)
+ foreach (var data in this.TestMatrices.Values)
{
var lower = this.CreateMatrix(data.RowCount, data.ColumnCount);
data.StrictlyUpperTriangle(lower);
@@ -688,7 +688,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedArgumentNullException]
public void StrictlyUpperTriangleWithNullParameterShouldThrowException()
{
- var data = this.testMatrices["Square3x3"];
+ var data = this.TestMatrices["Square3x3"];
Matrix lower = null;
data.StrictlyUpperTriangle(lower);
}
@@ -697,7 +697,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedArgumentException]
public void StrictlyUpperTriangleWithInvalidColumnNumberShouldThrowException()
{
- var data = this.testMatrices["Square3x3"];
+ var data = this.TestMatrices["Square3x3"];
var lower = this.CreateMatrix(data.RowCount, data.ColumnCount + 1);
data.StrictlyUpperTriangle(lower);
}
@@ -706,7 +706,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedArgumentException]
public void StrictlyUpperTriangleWithInvalidRowNumberShouldThrowException()
{
- var data = this.testMatrices["Square3x3"];
+ var data = this.TestMatrices["Square3x3"];
var lower = this.CreateMatrix(data.RowCount + 1, data.ColumnCount);
data.StrictlyUpperTriangle(lower);
}
@@ -720,7 +720,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[MultipleAsserts]
public void CanTransposeMatrix(string name)
{
- var matrix = this.CreateMatrix(this.testData2D[name]);
+ var matrix = this.CreateMatrix(this.TestData2D[name]);
var transpose = matrix.Transpose();
Assert.AreNotSame(matrix, transpose);
@@ -742,9 +742,9 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[Row("Wide2x3", new double[] { 1, 2 })]
[Row("Singular3x3", null, ExpectedException = typeof(ArgumentNullException))]
[Row("Singular3x3", new double[] { 1, 2, 3, 4, 5 }, ExpectedException = typeof(ArgumentException))]
- public void SetColumnWithArray(string name, double[] column)
+ public virtual void SetColumnWithArray(string name, double[] column)
{
- var matrix = this.testMatrices[name];
+ var matrix = this.TestMatrices[name];
for (var i = 0; i < matrix.ColumnCount; i++)
{
matrix.SetColumn(i, column);
@@ -759,7 +759,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedArgumentOutOfRangeException]
public void SetColumnArrayWithInvalidColumnIndexShouldThrowException()
{
- var matrix = this.testMatrices["Square3x3"];
+ var matrix = this.TestMatrices["Square3x3"];
double[] column = { 1, 2, 3 };
matrix.SetColumn(-1, column);
}
@@ -768,7 +768,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedArgumentOutOfRangeException]
public void SetColumnArrayWithInvalidColumnIndexShouldThrowException2()
{
- var matrix = this.testMatrices["Square3x3"];
+ var matrix = this.TestMatrices["Square3x3"];
double[] column = { 1, 2, 3 };
matrix.SetColumn(matrix.ColumnCount + 1, column);
}
@@ -779,9 +779,9 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[Row("Tall3x2", new double[] { 1, 2, 3 })]
[Row("Wide2x3", new double[] { 1, 2 })]
[Row("Singular3x3", new double[] { 1, 2, 3, 4, 5 }, ExpectedException = typeof(ArgumentException))]
- public void SetColumnWithVector(string name, double[] column)
+ public virtual void SetColumnWithVector(string name, double[] column)
{
- var matrix = this.testMatrices[name];
+ var matrix = this.TestMatrices[name];
var columnVector = CreateVector(column);
for (var i = 0; i < matrix.ColumnCount; i++)
{
@@ -797,7 +797,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedArgumentNullException]
public void SetColumnWithNullVectorShouldThrowException()
{
- var matrix = this.testMatrices["Square3x3"];
+ var matrix = this.TestMatrices["Square3x3"];
Vector columnVector = null;
matrix.SetColumn(1, columnVector);
}
@@ -806,7 +806,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedArgumentOutOfRangeException]
public void SetColumnVectorWithInvalidColumnIndexShouldThrowException()
{
- var matrix = this.testMatrices["Square3x3"];
+ var matrix = this.TestMatrices["Square3x3"];
var column = this.CreateVector(new double[] { 1, 2, 3 });
matrix.SetColumn(-1, column);
}
@@ -815,7 +815,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedArgumentOutOfRangeException]
public void SetColumnVectorWithInvalidColumnIndexShouldThrowException2()
{
- var matrix = this.testMatrices["Square3x3"];
+ var matrix = this.TestMatrices["Square3x3"];
var column = this.CreateVector(new double[] { 1, 2, 3 });
matrix.SetColumn(matrix.ColumnCount + 1, column);
}
@@ -855,7 +855,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedException(typeof(ArgumentNullException))]
public void InsertNullColumnShouldThrowExecption()
{
- var matrix = this.testMatrices["Square3x3"];
+ var matrix = this.TestMatrices["Square3x3"];
matrix.InsertColumn(0, null);
}
@@ -883,9 +883,9 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[Row("Wide2x3", new double[] { 1, 2, 3 })]
[Row("Singular3x3", null, ExpectedException = typeof(ArgumentNullException))]
[Row("Singular3x3", new double[] { 1, 2, 3, 4, 5 }, ExpectedException = typeof(ArgumentException))]
- public void SetRowWithArray(string name, double[] row)
+ public virtual void SetRowWithArray(string name, double[] row)
{
- var matrix = this.testMatrices[name];
+ var matrix = this.TestMatrices[name];
for (var i = 0; i < matrix.RowCount; i++)
{
matrix.SetRow(i, row);
@@ -900,7 +900,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedArgumentOutOfRangeException]
public void SetRowArrayWithInvalidRowIndexShouldThrowException()
{
- var matrix = this.testMatrices["Square3x3"];
+ var matrix = this.TestMatrices["Square3x3"];
double[] row = { 1, 2, 3 };
matrix.SetRow(-1, row);
}
@@ -909,7 +909,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedArgumentOutOfRangeException]
public void SetRowArrayWithInvalidRowIndexShouldThrowException2()
{
- var matrix = this.testMatrices["Square3x3"];
+ var matrix = this.TestMatrices["Square3x3"];
double[] row = { 1, 2, 3 };
matrix.SetRow(matrix.RowCount + 1, row);
}
@@ -920,9 +920,9 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[Row("Tall3x2", new double[] { 1, 2 })]
[Row("Wide2x3", new double[] { 1, 2, 3 })]
[Row("Singular3x3", new double[] { 1, 2, 3, 4, 5 }, ExpectedException = typeof(ArgumentException))]
- public void SetRowWithVector(string name, double[] row)
+ public virtual void SetRowWithVector(string name, double[] row)
{
- var matrix = this.testMatrices[name];
+ var matrix = this.TestMatrices[name];
var rowVector = CreateVector(row);
for (var i = 0; i < matrix.RowCount; i++)
{
@@ -938,7 +938,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedArgumentNullException]
public void SetRowWithNullVectorShouldThrowException()
{
- var matrix = this.testMatrices["Square3x3"];
+ var matrix = this.TestMatrices["Square3x3"];
Vector rowVector = null;
matrix.SetRow(1, rowVector);
}
@@ -947,7 +947,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedArgumentOutOfRangeException]
public void SetRowVectorWithInvalidRowIndexShouldThrowException()
{
- var matrix = this.testMatrices["Square3x3"];
+ var matrix = this.TestMatrices["Square3x3"];
var row = this.CreateVector(new double[] { 1, 2, 3 });
matrix.SetRow(-1, row);
}
@@ -956,7 +956,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedArgumentOutOfRangeException]
public void SetRowVectorWithInvalidRowIndexShouldThrowException2()
{
- var matrix = this.testMatrices["Square3x3"];
+ var matrix = this.TestMatrices["Square3x3"];
var row = this.CreateVector(new double[] { 1, 2, 3 });
matrix.SetRow(matrix.RowCount + 1, row);
}
@@ -972,9 +972,9 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[Row(0, 2, -1, 2, ExpectedException = typeof(ArgumentOutOfRangeException))]
[Row(0, -1, 0, 2, ExpectedException = typeof(ArgumentException))]
[Row(0, 2, 0, -1, ExpectedException = typeof(ArgumentException))]
- public void SetSubMatrix(int rowStart, int rowLength, int colStart, int colLength)
+ public virtual void SetSubMatrix(int rowStart, int rowLength, int colStart, int colLength)
{
- foreach (var matrix in this.testMatrices.Values)
+ foreach (var matrix in this.TestMatrices.Values)
{
var subMatrix = matrix.SubMatrix(0, 2, 0, 2);
subMatrix[0, 0] = 10.0;
@@ -997,7 +997,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedArgumentNullException]
public void SetSubMatrixWithNullSubMatrixShouldThrowException()
{
- var data = this.testMatrices["Square3x3"];
+ var data = this.TestMatrices["Square3x3"];
Matrix subMatrix = null;
data.SetSubMatrix(0, 2, 0, 2, subMatrix);
}
@@ -1009,7 +1009,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[Row("Tall3x2", new double[] { 1, 2 })]
public void SetDiagonalVector(string name, double[] diagonal)
{
- var matrix = this.testMatrices[name];
+ var matrix = this.TestMatrices[name];
var vector = CreateVector(diagonal);
matrix.SetDiagonal(vector);
@@ -1026,7 +1026,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedArgumentNullException]
public void SetDiagonalWithNullVectorParameterShouldThrowException()
{
- var matrix = this.testMatrices["Square3x3"];
+ var matrix = this.TestMatrices["Square3x3"];
Vector vector = null;
matrix.SetDiagonal(vector);
}
@@ -1039,7 +1039,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[Row("Square3x3", null, ExpectedException = typeof(ArgumentNullException))]
public void SetDiagonalArray(string name, double[] diagonal)
{
- var matrix = this.testMatrices[name];
+ var matrix = this.TestMatrices[name];
matrix.SetDiagonal(diagonal);
var min = Math.Min(matrix.ColumnCount, matrix.RowCount);
Assert.AreEqual(diagonal.Length, min);
@@ -1084,7 +1084,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedException(typeof(ArgumentNullException))]
public void InsertNullRowShouldThrowExecption()
{
- var matrix = this.testMatrices["Square3x3"];
+ var matrix = this.TestMatrices["Square3x3"];
matrix.InsertRow(0, null);
}
@@ -1108,7 +1108,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[Test]
public void ToArray()
{
- foreach (var data in this.testMatrices.Values)
+ foreach (var data in this.TestMatrices.Values)
{
var array = data.ToArray();
Assert.AreEqual(data.RowCount, array.GetLength(0));
@@ -1127,7 +1127,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[Test]
public void ToColumnWiseArray()
{
- foreach (var data in this.testMatrices.Values)
+ foreach (var data in this.TestMatrices.Values)
{
var array = data.ToColumnWiseArray();
Assert.AreEqual(data.RowCount * data.ColumnCount, array.Length);
@@ -1145,7 +1145,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[Test]
public void ToRowWiseArray()
{
- foreach (var data in this.testMatrices.Values)
+ foreach (var data in this.TestMatrices.Values)
{
var array = data.ToRowWiseArray();
Assert.AreEqual(data.RowCount * data.ColumnCount, array.Length);
@@ -1166,10 +1166,10 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[Row("Square3x3")]
[Row("Tall3x2")]
[MultipleAsserts]
- public void CanPermuteMatrixRows(string name)
+ public virtual void CanPermuteMatrixRows(string name)
{
- var matrix = this.CreateMatrix(this.testData2D[name]);
- var matrixp = this.CreateMatrix(this.testData2D[name]);
+ var matrix = this.CreateMatrix(this.TestData2D[name]);
+ var matrixp = this.CreateMatrix(this.TestData2D[name]);
var permutation = new Permutation(new[] { 2, 0, 1 });
matrixp.PermuteRows(permutation);
@@ -1191,10 +1191,10 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[Row("Square3x3")]
[Row("Wide2x3")]
[MultipleAsserts]
- public void CanPermuteMatrixColumns(string name)
+ public virtual void CanPermuteMatrixColumns(string name)
{
- var matrix = this.CreateMatrix(this.testData2D[name]);
- var matrixp = this.CreateMatrix(this.testData2D[name]);
+ var matrix = this.CreateMatrix(this.TestData2D[name]);
+ var matrixp = this.CreateMatrix(this.TestData2D[name]);
var permutation = new Permutation(new[] { 2, 0, 1 });
matrixp.PermuteColumns(permutation);
@@ -1214,8 +1214,8 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[Test]
public void CanAppendMatrices()
{
- var left = this.CreateMatrix(this.testData2D["Singular3x3"]);
- var right = this.CreateMatrix(this.testData2D["Tall3x2"]);
+ var left = this.CreateMatrix(this.TestData2D["Singular3x3"]);
+ var right = this.CreateMatrix(this.TestData2D["Tall3x2"]);
var result = left.Append(right);
Assert.AreEqual(left.ColumnCount + right.ColumnCount, result.ColumnCount);
Assert.AreEqual(left.RowCount, right.RowCount);
@@ -1240,7 +1240,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedArgumentNullException]
public void CanAppendWithRightParameterNullShouldThrowException()
{
- var left = this.testMatrices["Square3x3"];
+ var left = this.TestMatrices["Square3x3"];
Matrix right = null;
left.Append(right);
}
@@ -1249,8 +1249,8 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedArgumentNullException]
public void CanAppendWithResultParameterNullShouldThrowException()
{
- var left = this.testMatrices["Square3x3"];
- var right = this.testMatrices["Tall3x2"];
+ var left = this.TestMatrices["Square3x3"];
+ var right = this.TestMatrices["Tall3x2"];
Matrix result = null;
left.Append(right, result);
}
@@ -1259,8 +1259,8 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedArgumentException]
public void AppendingTwoMatricesWithDifferentRowCountShouldThrowException()
{
- var left = this.testMatrices["Square3x3"];
- var right = this.testMatrices["Wide2x3"];
+ var left = this.TestMatrices["Square3x3"];
+ var right = this.TestMatrices["Wide2x3"];
var result = left.Append(right);
}
@@ -1268,8 +1268,8 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedArgumentException]
public void AppendingWithInvalidResultMatrixColumnsShouldThrowException()
{
- var left = this.testMatrices["Square3x3"];
- var right = this.testMatrices["Tall3x2"];
+ var left = this.TestMatrices["Square3x3"];
+ var right = this.TestMatrices["Tall3x2"];
var result = this.CreateMatrix(3, 2);
left.Append(right, result);
}
@@ -1277,8 +1277,8 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[Test]
public void CanStackMatrices()
{
- var top = this.testMatrices["Square3x3"];
- var bottom = this.testMatrices["Wide2x3"];
+ var top = this.TestMatrices["Square3x3"];
+ var bottom = this.TestMatrices["Wide2x3"];
var result = top.Stack(bottom);
Assert.AreEqual(top.RowCount + bottom.RowCount, result.RowCount);
Assert.AreEqual(top.ColumnCount, result.ColumnCount);
@@ -1303,7 +1303,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedArgumentNullException]
public void StackingWithBottomParameterNullShouldThrowException()
{
- var top = this.testMatrices["Square3x3"];
+ var top = this.TestMatrices["Square3x3"];
Matrix bottom = null;
var result = this.CreateMatrix(top.RowCount + top.RowCount, top.ColumnCount);
top.Stack(bottom, result);
@@ -1313,8 +1313,8 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedArgumentNullException]
public void StackingWithResultParameterNullShouldThrowException()
{
- var top = this.testMatrices["Square3x3"];
- var bottom = this.testMatrices["Square3x3"];
+ var top = this.TestMatrices["Square3x3"];
+ var bottom = this.TestMatrices["Square3x3"];
Matrix result = null;
top.Stack(bottom, result);
}
@@ -1323,8 +1323,8 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedArgumentException]
public void StackingTwoMatricesWithDifferentColumnsShouldThrowException()
{
- var top = this.testMatrices["Square3x3"];
- var lower = this.testMatrices["Tall3x2"];
+ var top = this.TestMatrices["Square3x3"];
+ var lower = this.TestMatrices["Tall3x2"];
var result = this.CreateMatrix(top.RowCount + lower.RowCount, top.ColumnCount);
top.Stack(lower, result);
}
@@ -1333,8 +1333,8 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedArgumentException]
public void StackingWithInvalidResultMatrixRowsShouldThrowException()
{
- var top = this.testMatrices["Square3x3"];
- var bottom = this.testMatrices["Wide2x3"];
+ var top = this.TestMatrices["Square3x3"];
+ var bottom = this.TestMatrices["Wide2x3"];
var result = this.CreateMatrix(1, 3);
top.Stack(bottom, result);
}
@@ -1342,8 +1342,8 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[Test]
public void CanDiagonallyStackMatrics()
{
- var top = this.testMatrices["Tall3x2"];
- var bottom = this.testMatrices["Wide2x3"];
+ var top = this.TestMatrices["Tall3x2"];
+ var bottom = this.TestMatrices["Wide2x3"];
var result = top.DiagonalStack(bottom);
Assert.AreEqual(top.RowCount + bottom.RowCount, result.RowCount);
Assert.AreEqual(top.ColumnCount + bottom.ColumnCount, result.ColumnCount);
@@ -1372,16 +1372,16 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedArgumentNullException]
public void DiagonalStackWithLowerNullShouldThrowException()
{
- var top = this.testMatrices["Square3x3"];
+ var top = this.TestMatrices["Square3x3"];
Matrix lower = null;
top.DiagonalStack(lower);
}
[Test]
- public void CanDiagonallyStackMatricesWithPassingResult()
+ public virtual void CanDiagonallyStackMatricesWithPassingResult()
{
- var top = this.testMatrices["Tall3x2"];
- var bottom = this.testMatrices["Wide2x3"];
+ var top = this.TestMatrices["Tall3x2"];
+ var bottom = this.TestMatrices["Wide2x3"];
var result = this.CreateMatrix(top.RowCount + bottom.RowCount, top.ColumnCount + bottom.ColumnCount);
top.DiagonalStack(bottom, result);
Assert.AreEqual(top.RowCount + bottom.RowCount, result.RowCount);
@@ -1411,8 +1411,8 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedArgumentNullException]
public void DiagonalStackWithResultNullShouldThrowException()
{
- var top = this.testMatrices["Square3x3"];
- var lower = this.testMatrices["Wide2x3"];
+ var top = this.TestMatrices["Square3x3"];
+ var lower = this.TestMatrices["Wide2x3"];
Matrix result = null;
top.DiagonalStack(lower, result);
}
@@ -1421,61 +1421,61 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedArgumentException]
public void DiagonalStackWithInvalidResultMatrixShouldThrowException()
{
- var top = this.testMatrices["Square3x3"];
- var lower = this.testMatrices["Wide2x3"];
+ var top = this.TestMatrices["Square3x3"];
+ var lower = this.TestMatrices["Wide2x3"];
var result = this.CreateMatrix(top.RowCount + lower.RowCount + 2, top.ColumnCount + lower.ColumnCount);
top.DiagonalStack(lower, result);
}
[Test]
- public void FrobeniusNorm()
+ public virtual void FrobeniusNorm()
{
- var matrix = testMatrices["Square3x3"];
+ var matrix = TestMatrices["Square3x3"];
AssertHelpers.AlmostEqual(10.77775486824598, matrix.FrobeniusNorm(), 14);
- matrix = testMatrices["Wide2x3"];
+ matrix = TestMatrices["Wide2x3"];
AssertHelpers.AlmostEqual(4.79478883789474, matrix.FrobeniusNorm(), 14);
- matrix = testMatrices["Tall3x2"];
+ matrix = TestMatrices["Tall3x2"];
AssertHelpers.AlmostEqual(7.54122006044115, matrix.FrobeniusNorm(), 14);
}
[Test]
- public void InfinityNorm()
+ public virtual void InfinityNorm()
{
- Matrix matrix = testMatrices["Square3x3"];
+ Matrix matrix = TestMatrices["Square3x3"];
Assert.AreEqual(16.5, matrix.InfinityNorm());
- matrix = testMatrices["Wide2x3"];
+ matrix = TestMatrices["Wide2x3"];
Assert.AreEqual(6.6, matrix.InfinityNorm());
- matrix = testMatrices["Tall3x2"];
+ matrix = TestMatrices["Tall3x2"];
Assert.AreEqual(9.9, matrix.InfinityNorm());
}
[Test]
- public void L1Norm()
+ public virtual void L1Norm()
{
- Matrix matrix = testMatrices["Square3x3"];
+ Matrix matrix = TestMatrices["Square3x3"];
Assert.AreEqual(12.1, matrix.L1Norm());
- matrix = testMatrices["Wide2x3"];
+ matrix = TestMatrices["Wide2x3"];
Assert.AreEqual(5.5, matrix.L1Norm());
- matrix = testMatrices["Tall3x2"];
+ matrix = TestMatrices["Tall3x2"];
Assert.AreEqual(8.8, matrix.L1Norm());
}
[Test]
- public void L2Norm()
+ public virtual void L2Norm()
{
- var matrix = testMatrices["Square3x3"];
+ var matrix = TestMatrices["Square3x3"];
AssertHelpers.AlmostEqual(10.391347375312632, matrix.L2Norm(), 14);
- matrix = testMatrices["Wide2x3"];
+ matrix = TestMatrices["Wide2x3"];
AssertHelpers.AlmostEqual(4.7540849434107635, matrix.L2Norm(), 14);
- matrix = testMatrices["Tall3x2"];
+ matrix = TestMatrices["Tall3x2"];
AssertHelpers.AlmostEqual(7.182727033856683, matrix.L2Norm(), 14);
}
}
diff --git a/src/UnitTests/LinearAlgebraTests/Double/Solvers/Iterative/BiCgStabTest.cs b/src/UnitTests/LinearAlgebraTests/Double/Solvers/Iterative/BiCgStabTest.cs
new file mode 100644
index 00000000..1e851a84
--- /dev/null
+++ b/src/UnitTests/LinearAlgebraTests/Double/Solvers/Iterative/BiCgStabTest.cs
@@ -0,0 +1,205 @@
+namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double.Solvers.Iterative
+{
+ using LinearAlgebra.Double;
+ using LinearAlgebra.Double.Solvers;
+ using LinearAlgebra.Double.Solvers.Iterative;
+ using LinearAlgebra.Double.Solvers.Status;
+ using LinearAlgebra.Double.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 input = new DenseVector(2);
+
+ var solver = new BiCgStab();
+ solver.Solve(matrix, input);
+ }
+
+ [Test]
+ [ExpectedArgumentException]
+ public void SolveLongMatrix()
+ {
+ var matrix = new SparseMatrix(3, 2);
+ Vector input = new DenseVector(3);
+
+ var solver = new BiCgStab();
+ solver.Solve(matrix, input);
+ }
+
+ [Test]
+ [MultipleAsserts]
+ public void SolveUnitMatrixAndBackMultiply()
+ {
+ // Create the identity matrix
+ Matrix matrix = new SparseMatrix(100);
+ for (var i = 0; i < matrix.RowCount; i++)
+ {
+ matrix[i, i] = 1.0;
+ }
+
+ // Create the y vector
+ Vector y = new DenseVector(matrix.RowCount, 1);
+
+ // Create an iteration monitor which will keep track of iterative convergence
+ var monitor = new Iterator(new IIterationStopCriterium[]
+ {
+ 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]).IsSmaller(ConvergenceBoundary, 1), "#05-" + i);
+ }
+ }
+
+ [Test]
+ [MultipleAsserts]
+ public void SolveScaledUnitMatrixAndBackMultiply()
+ {
+ // Create the identity matrix
+ Matrix matrix = new SparseMatrix(100);
+ for (var i = 0; i < matrix.RowCount; i++)
+ {
+ matrix[i, i] = 1.0;
+ }
+
+ // Scale it with a funny number
+ matrix.Multiply(System.Math.PI);
+
+ // Create the y vector
+ Vector y = new DenseVector(matrix.RowCount, 1);
+
+ // Create an iteration monitor which will keep track of iterative convergence
+ var monitor = new Iterator(new IIterationStopCriterium[]
+ {
+ 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]).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 y = new DenseVector(matrix.RowCount, 1);
+
+ // Create an iteration monitor which will keep track of iterative convergence
+ var monitor = new Iterator(new IIterationStopCriterium[]
+ {
+ 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(System.Math.Abs(y[i] - z[i]).IsSmaller(ConvergenceBoundary, 1), "#05-" + i);
+ }
+ }
+ }
+}
diff --git a/src/UnitTests/LinearAlgebraTests/Double/Solvers/Iterative/GpBiCgTest.cs b/src/UnitTests/LinearAlgebraTests/Double/Solvers/Iterative/GpBiCgTest.cs
new file mode 100644
index 00000000..a4154c7b
--- /dev/null
+++ b/src/UnitTests/LinearAlgebraTests/Double/Solvers/Iterative/GpBiCgTest.cs
@@ -0,0 +1,207 @@
+namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double.Solvers.Iterative
+{
+ using LinearAlgebra.Double;
+ using LinearAlgebra.Double.Solvers;
+ using LinearAlgebra.Double.Solvers.Iterative;
+ using LinearAlgebra.Double.Solvers.Status;
+ using LinearAlgebra.Double.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 input = new DenseVector(2);
+
+ var solver = new GpBiCg();
+ solver.Solve(matrix, input);
+ }
+
+ [Test]
+ [ExpectedArgumentException]
+ public void SolveLongMatrix()
+ {
+ var matrix = new SparseMatrix(3, 2);
+ Vector input = new DenseVector(3);
+
+ var solver = new GpBiCg();
+ solver.Solve(matrix, input);
+ }
+
+ [Test]
+ [MultipleAsserts]
+ public void SolveUnitMatrixAndBackMultiply()
+ {
+ // Create the identity matrix
+ Matrix matrix = new SparseMatrix(100);
+ for (var i = 0; i < matrix.RowCount; i++)
+ {
+ matrix[i, i] = 1.0;
+ }
+
+ // Create the y vector
+ Vector y = new DenseVector(matrix.RowCount, 1);
+
+ // Create an iteration monitor which will keep track of iterative convergence
+ var monitor = new Iterator(new IIterationStopCriterium[]
+ {
+ 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]).IsSmaller(ConvergenceBoundary, 1), "#05-" + i);
+ }
+ }
+
+ [Test]
+ [MultipleAsserts]
+ public void SolveScaledUnitMatrixAndBackMultiply()
+ {
+ // Create the identity matrix
+ Matrix matrix = new SparseMatrix(100);
+ for (var i = 0; i < matrix.RowCount; i++)
+ {
+ matrix[i, i] = 1.0;
+ }
+
+ // Scale it with a funny number
+ matrix.Multiply(System.Math.PI);
+
+ // Create the y vector
+ Vector y = new DenseVector(matrix.RowCount, 1);
+
+ // Create an iteration monitor which will keep track of iterative convergence
+ var monitor = new Iterator(new IIterationStopCriterium[]
+ {
+ 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]).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 y = new DenseVector(matrix.RowCount, 1);
+
+ // Create an iteration monitor which will keep track of iterative convergence
+ var monitor = new Iterator(new IIterationStopCriterium[]
+ {
+ 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(System.Math.Abs(y[i] - z[i]).IsSmaller(ConvergenceBoundary, 1), "#05-" + i);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/UnitTests/LinearAlgebraTests/Double/Solvers/Iterative/MlkBiCgStabTest.cs b/src/UnitTests/LinearAlgebraTests/Double/Solvers/Iterative/MlkBiCgStabTest.cs
new file mode 100644
index 00000000..92883217
--- /dev/null
+++ b/src/UnitTests/LinearAlgebraTests/Double/Solvers/Iterative/MlkBiCgStabTest.cs
@@ -0,0 +1,205 @@
+namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double.Solvers.Iterative
+{
+ using LinearAlgebra.Double;
+ using LinearAlgebra.Double.Solvers;
+ using LinearAlgebra.Double.Solvers.Iterative;
+ using LinearAlgebra.Double.Solvers.Status;
+ using LinearAlgebra.Double.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 input = new DenseVector(2);
+
+ var solver = new MlkBiCgStab();
+ solver.Solve(matrix, input);
+ }
+
+ [Test]
+ [ExpectedArgumentException]
+ public void SolveLongMatrix()
+ {
+ var matrix = new SparseMatrix(3, 2);
+ Vector input = new DenseVector(3);
+
+ var solver = new MlkBiCgStab();
+ solver.Solve(matrix, input);
+ }
+
+ [Test]
+ [MultipleAsserts]
+ public void SolveUnitMatrixAndBackMultiply()
+ {
+ // Create the identity matrix
+ Matrix matrix = new SparseMatrix(100);
+ for (var i = 0; i < matrix.RowCount; i++)
+ {
+ matrix[i, i] = 1.0;
+ }
+
+ // Create the y vector
+ Vector y = new DenseVector(matrix.RowCount, 1);
+
+ // Create an iteration monitor which will keep track of iterative convergence
+ var monitor = new Iterator(new IIterationStopCriterium[]
+ {
+ 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]).IsSmaller(ConvergenceBoundary, 1), "#05-" + i);
+ }
+ }
+
+ [Test]
+ [MultipleAsserts]
+ public void SolveScaledUnitMatrixAndBackMultiply()
+ {
+ // Create the identity matrix
+ Matrix matrix = new SparseMatrix(100);
+ for (var i = 0; i < matrix.RowCount; i++)
+ {
+ matrix[i, i] = 1.0;
+ }
+
+ // Scale it with a funny number
+ matrix.Multiply(System.Math.PI);
+
+ // Create the y vector
+ Vector y = new DenseVector(matrix.RowCount, 1);
+
+ // Create an iteration monitor which will keep track of iterative convergence
+ var monitor = new Iterator(new IIterationStopCriterium[]
+ {
+ 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]).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 y = new DenseVector(matrix.RowCount, 1);
+
+ // Create an iteration monitor which will keep track of iterative convergence
+ var monitor = new Iterator(new IIterationStopCriterium[]
+ {
+ 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(System.Math.Abs(y[i] - z[i]).IsSmaller(ConvergenceBoundary, 1), "#05-" + i);
+ }
+ }
+ }
+}
diff --git a/src/UnitTests/LinearAlgebraTests/Double/Solvers/Iterative/TFQMRTest.cs b/src/UnitTests/LinearAlgebraTests/Double/Solvers/Iterative/TFQMRTest.cs
new file mode 100644
index 00000000..20267552
--- /dev/null
+++ b/src/UnitTests/LinearAlgebraTests/Double/Solvers/Iterative/TFQMRTest.cs
@@ -0,0 +1,204 @@
+namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double.Solvers.Iterative
+{
+ using LinearAlgebra.Double;
+ using LinearAlgebra.Double.Solvers;
+ using LinearAlgebra.Double.Solvers.Iterative;
+ using LinearAlgebra.Double.Solvers.Status;
+ using LinearAlgebra.Double.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 input = new DenseVector(2);
+
+ var solver = new TFQMR();
+ solver.Solve(matrix, input);
+ }
+
+ [Test]
+ [ExpectedArgumentException]
+ public void SolveLongMatrix()
+ {
+ var matrix = new SparseMatrix(3, 2);
+ Vector input = new DenseVector(3);
+
+ var solver = new TFQMR();
+ solver.Solve(matrix, input);
+ }
+
+ [Test]
+ [MultipleAsserts]
+ public void SolveUnitMatrixAndBackMultiply()
+ {
+ // Create the identity matrix
+ Matrix matrix = new SparseMatrix(100);
+ for (var i = 0; i < matrix.RowCount; i++)
+ {
+ matrix[i, i] = 1.0;
+ }
+
+ // Create the y vector
+ Vector y = new DenseVector(matrix.RowCount, 1);
+
+ // Create an iteration monitor which will keep track of iterative convergence
+ var monitor = new Iterator(new IIterationStopCriterium[]
+ {
+ 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]).IsSmaller(ConvergenceBoundary, 1), "#05-" + i);
+ }
+ }
+
+ [Test]
+ [MultipleAsserts]
+ public void SolveScaledUnitMatrixAndBackMultiply()
+ {
+ // Create the identity matrix
+ Matrix matrix = new SparseMatrix(100);
+ for (var i = 0; i < matrix.RowCount; i++)
+ {
+ matrix[i, i] = 1.0;
+ }
+ // Scale it with a funny number
+ matrix.Multiply(System.Math.PI);
+
+ // Create the y vector
+ Vector y = new DenseVector(matrix.RowCount, 1);
+
+ // Create an iteration monitor which will keep track of iterative convergence
+ var monitor = new Iterator(new IIterationStopCriterium[]
+ {
+ 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]).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 y = new DenseVector(matrix.RowCount, 1);
+
+ // Create an iteration monitor which will keep track of iterative convergence
+ var monitor = new Iterator(new IIterationStopCriterium[]
+ {
+ 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(System.Math.Abs(y[i] - z[i]).IsSmaller(ConvergenceBoundary, 1), "#05-" + i);
+ }
+ }
+ }
+}
diff --git a/src/UnitTests/LinearAlgebraTests/Double/Solvers/IteratorTest.cs b/src/UnitTests/LinearAlgebraTests/Double/Solvers/IteratorTest.cs
new file mode 100644
index 00000000..680fbbdb
--- /dev/null
+++ b/src/UnitTests/LinearAlgebraTests/Double/Solvers/IteratorTest.cs
@@ -0,0 +1,353 @@
+namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double.Solvers
+{
+ using System;
+ using System.Collections.Generic;
+ using LinearAlgebra.Double;
+ using LinearAlgebra.Double.Solvers;
+ using LinearAlgebra.Double.Solvers.Status;
+ using LinearAlgebra.Double.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[] { });
+ 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[] { 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[]
+ {
+ new FailureStopCriterium(),
+ new FailureStopCriterium()
+ });
+ }
+
+ [Test]
+ [MultipleAsserts]
+ public void CreateWithCollection()
+ {
+ var criteria = new List
+ {
+ 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
+ {
+ 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
+ {
+ 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
+ {
+ 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
+ {
+ 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
+ {
+ 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
+ {
+ 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
+ {
+ 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
+ {
+ 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
+ {
+ 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
+ {
+ 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
+ {
+ 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");
+ }
+ }
+ }
+}
diff --git a/src/UnitTests/LinearAlgebraTests/Double/Solvers/Preconditioners/DiagonalTest.cs b/src/UnitTests/LinearAlgebraTests/Double/Solvers/Preconditioners/DiagonalTest.cs
new file mode 100644
index 00000000..f15492db
--- /dev/null
+++ b/src/UnitTests/LinearAlgebraTests/Double/Solvers/Preconditioners/DiagonalTest.cs
@@ -0,0 +1,30 @@
+namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double.Solvers.Preconditioners
+{
+ using LinearAlgebra.Double;
+ using LinearAlgebra.Double.Solvers.Preconditioners;
+ using MbUnit.Framework;
+
+ [TestFixture]
+ public sealed class DiagonalTest : PreconditionerTest
+ {
+ internal override IPreConditioner CreatePreconditioner()
+ {
+ return new Diagonal();
+ }
+
+ protected override void CheckResult(IPreConditioner preconditioner, SparseMatrix matrix, Vector vector, Vector result)
+ {
+ Assert.AreEqual(typeof(Diagonal), preconditioner.GetType(), "#01");
+
+ // Compute M * result = product
+ // compare vector and product. Should be equal
+ Vector product = new DenseVector(result.Count);
+ matrix.Multiply(result, product);
+
+ for (var i = 0; i < product.Count; i++)
+ {
+ Assert.IsTrue(vector[i].AlmostEqual(product[i], -Epsilon.Magnitude()), "#02-" + i);
+ }
+ }
+ }
+}
diff --git a/src/UnitTests/LinearAlgebraTests/Double/Solvers/Preconditioners/IluptElementSorterTest.cs b/src/UnitTests/LinearAlgebraTests/Double/Solvers/Preconditioners/IluptElementSorterTest.cs
new file mode 100644
index 00000000..649a6a34
--- /dev/null
+++ b/src/UnitTests/LinearAlgebraTests/Double/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.Double.Solvers.Preconditioners
+{
+ using LinearAlgebra.Double;
+ using LinearAlgebra.Double.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);
+ }
+ }
+ }
+}
diff --git a/src/UnitTests/LinearAlgebraTests/Double/Solvers/Preconditioners/IlutpTest.cs b/src/UnitTests/LinearAlgebraTests/Double/Solvers/Preconditioners/IlutpTest.cs
new file mode 100644
index 00000000..ce12fe37
--- /dev/null
+++ b/src/UnitTests/LinearAlgebraTests/Double/Solvers/Preconditioners/IlutpTest.cs
@@ -0,0 +1,248 @@
+namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double.Solvers.Preconditioners
+{
+ using System;
+ using System.Reflection;
+ using LinearAlgebra.Double;
+ using LinearAlgebra.Double.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(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(ilutp, "UpperTriangle");
+ }
+
+ private static SparseMatrix GetLowerTriangle(Ilutp ilutp)
+ {
+ return GetMethod(ilutp, "LowerTriangle");
+ }
+
+ private static int[] GetPivots(Ilutp ilutp)
+ {
+ return GetMethod(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 CreatePreconditioner()
+ {
+ _pivotTolerance = 0;
+ _dropTolerance = 0.0;
+ _fillLevel = 100;
+ return InternalCreatePreconditioner();
+ }
+
+ protected override void CheckResult(IPreConditioner preconditioner, SparseMatrix matrix, Vector vector, Vector result)
+ {
+ Assert.AreEqual(typeof(Ilutp), preconditioner.GetType(), "#01");
+
+ // Compute M * result = product
+ // compare vector and product. Should be equal
+ Vector product = new DenseVector(result.Count);
+ matrix.Multiply(result, product);
+ for (var i = 0; i < product.Count; i++)
+ {
+ Assert.IsTrue(vector[i].AlmostEqual(product[i], -Epsilon.Magnitude()), "#02-" + 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 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 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], -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], -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].AlmostEqual(original[i, j], -Epsilon.Magnitude()), "#03-" + 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].AlmostEqual(original[i, j], -Epsilon.Magnitude()), "#01-" + 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 result = new DenseVector(vector.Count);
+ preconditioner.Approximate(vector, result);
+ CheckResult(preconditioner, newMatrix, vector, result);
+ }
+ }
+}
diff --git a/src/UnitTests/LinearAlgebraTests/Double/Solvers/Preconditioners/IncompleteLUTest.cs b/src/UnitTests/LinearAlgebraTests/Double/Solvers/Preconditioners/IncompleteLUTest.cs
new file mode 100644
index 00000000..740e1c66
--- /dev/null
+++ b/src/UnitTests/LinearAlgebraTests/Double/Solvers/Preconditioners/IncompleteLUTest.cs
@@ -0,0 +1,82 @@
+namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double.Solvers.Preconditioners
+{
+ using System;
+ using System.Reflection;
+ using LinearAlgebra.Double;
+ using LinearAlgebra.Double.Solvers.Preconditioners;
+ using MbUnit.Framework;
+
+ [TestFixture]
+ public sealed class IncompleteLUFactorizationTest : PreconditionerTest
+ {
+ private static T GetMethod(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 GetUpperTriangle(IncompleteLU ilu)
+ {
+ return GetMethod(ilu, "UpperTriangle");
+ }
+
+ private static Matrix GetLowerTriangle(IncompleteLU ilu)
+ {
+ return GetMethod(ilu, "LowerTriangle");
+ }
+
+ internal override IPreConditioner CreatePreconditioner()
+ {
+ return new IncompleteLU();
+ }
+
+ protected override void CheckResult(IPreConditioner preconditioner, SparseMatrix matrix, Vector vector, Vector result)
+ {
+ Assert.AreEqual(typeof(IncompleteLU), preconditioner.GetType(), "#01");
+
+ // Compute M * result = product
+ // compare vector and product. Should be equal
+ Vector product = new DenseVector(result.Count);
+ matrix.Multiply(result, product);
+
+ for (var i = 0; i < product.Count; i++)
+ {
+ Assert.IsTrue(vector[i].AlmostEqual(product[i], -Epsilon.Magnitude()), "#02-" + 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].AlmostEqual(original[i, j], -Epsilon.Magnitude()), "#01-" + i + "-" + j);
+ }
+ }
+ }
+ }
+}
diff --git a/src/UnitTests/LinearAlgebraTests/Double/Solvers/Preconditioners/PreConditionerTest.cs b/src/UnitTests/LinearAlgebraTests/Double/Solvers/Preconditioners/PreConditionerTest.cs
new file mode 100644
index 00000000..26ff20be
--- /dev/null
+++ b/src/UnitTests/LinearAlgebraTests/Double/Solvers/Preconditioners/PreConditionerTest.cs
@@ -0,0 +1,126 @@
+namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double.Solvers.Preconditioners
+{
+ using LinearAlgebra.Double;
+ using LinearAlgebra.Double.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 CreateStandardBcVector(int size)
+ {
+ Vector vector = new DenseVector(size);
+ for (var i = 0; i < size; i++)
+ {
+ vector[i] = i + 1;
+ }
+
+ return vector;
+ }
+
+ internal abstract IPreConditioner CreatePreconditioner();
+
+ protected abstract void CheckResult(IPreConditioner preconditioner, SparseMatrix matrix, Vector vector, Vector 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 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 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 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 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);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/UnitTests/LinearAlgebraTests/Double/Solvers/Preconditioners/UnitPreconditionerTest.cs b/src/UnitTests/LinearAlgebraTests/Double/Solvers/Preconditioners/UnitPreconditionerTest.cs
new file mode 100644
index 00000000..28eec73c
--- /dev/null
+++ b/src/UnitTests/LinearAlgebraTests/Double/Solvers/Preconditioners/UnitPreconditionerTest.cs
@@ -0,0 +1,27 @@
+namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double.Solvers.Preconditioners
+{
+ using LinearAlgebra.Double;
+ using LinearAlgebra.Double.Solvers.Preconditioners;
+ using MbUnit.Framework;
+
+ [TestFixture]
+ public sealed class UnitPreconditionerTest : PreconditionerTest
+ {
+ internal override IPreConditioner CreatePreconditioner()
+ {
+ return new UnitPreconditioner();
+ }
+
+ protected override void CheckResult(IPreConditioner preconditioner, SparseMatrix matrix, Vector vector, Vector 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);
+ }
+ }
+ }
+}
diff --git a/src/UnitTests/LinearAlgebraTests/Double/Solvers/StopCriterium/DivergenceStopCriteriumTest.cs b/src/UnitTests/LinearAlgebraTests/Double/Solvers/StopCriterium/DivergenceStopCriteriumTest.cs
new file mode 100644
index 00000000..23df392f
--- /dev/null
+++ b/src/UnitTests/LinearAlgebraTests/Double/Solvers/StopCriterium/DivergenceStopCriteriumTest.cs
@@ -0,0 +1,233 @@
+namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double.Solvers.StopCriterium
+{
+ using LinearAlgebra.Double;
+ using LinearAlgebra.Double.Solvers.Status;
+ using LinearAlgebra.Double.Solvers.StopCriterium;
+ 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 [] { 1.0 }),
+ new DenseVector(new [] { 1.0 }),
+ new DenseVector(new [] { (i + 1) * (Increase + 0.1) }));
+
+ 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 [] { 1.0 }),
+ new DenseVector(new [] { 1.0 }),
+ new DenseVector(new [] { (i + 1) * (Increase - 0.01) }));
+
+ 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 [] { 1.0 }),
+ new DenseVector(new [] { 1.0 }),
+ new DenseVector(new [] { (i + 1) * (Increase - 0.01) }));
+
+ Assert.IsInstanceOfType(typeof(CalculationRunning), criterium.Status, "Status check fail.");
+ }
+
+ // Now make it fail by throwing in a NaN
+ criterium.DetermineStatus(Iterations,
+ new DenseVector(new [] { 1.0 }),
+ new DenseVector(new [] { 1.0 }),
+ new DenseVector(new [] { double.NaN }));
+
+ 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[] { 1.0 }),
+ new DenseVector(new[] { 1.0 }),
+ new DenseVector(new[] { previous }));
+
+ 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[] { 1.0 }),
+ new DenseVector(new[] { 1.0 }),
+ new DenseVector(new[] { previous }));
+
+ 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 [] { 1.0 }),
+ new DenseVector(new [] { 1.0 }),
+ new DenseVector(new [] { double.NaN }));
+
+ 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
+ }
+ }
+}
diff --git a/src/UnitTests/LinearAlgebraTests/Double/Solvers/StopCriterium/FailureStopCriteriumTest.cs b/src/UnitTests/LinearAlgebraTests/Double/Solvers/StopCriterium/FailureStopCriteriumTest.cs
new file mode 100644
index 00000000..81c790fb
--- /dev/null
+++ b/src/UnitTests/LinearAlgebraTests/Double/Solvers/StopCriterium/FailureStopCriteriumTest.cs
@@ -0,0 +1,133 @@
+namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double.Solvers.StopCriterium
+{
+ using LinearAlgebra.Double;
+ using LinearAlgebra.Double.Solvers.Status;
+ using LinearAlgebra.Double.Solvers.StopCriterium;
+ 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 [] { 1.0, 1.0, 2.0 });
+ var source = new DenseVector(new [] { 1001.0, 0, 2003.0 });
+ var residual = new DenseVector(new [] { 1000, double.NaN, 2001 });
+
+ 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[] { 1.0, 1.0, double.NaN });
+ var source = new DenseVector(new[] { 1001.0, 0.0, 2003.0 });
+ var residual = new DenseVector(new[] { 1000.0, 1000.0, 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[] { 3.0, 2.0, 1.0 });
+ var source = new DenseVector(new[] { 1001.0, 0.0, 2003.0 });
+ var residual = new DenseVector(new[] { 1.0, 2.0, 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[] { 1.0, 1.0, 2.0 });
+ var source = new DenseVector(new[] { 1001.0, 0.0, 2003.0 });
+ var residual = new DenseVector(new[] { 1000.0, 1000.0, 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");
+ }
+ }
+}
diff --git a/src/UnitTests/LinearAlgebraTests/Double/Solvers/StopCriterium/IterationCountStopCriteriumTest.cs b/src/UnitTests/LinearAlgebraTests/Double/Solvers/StopCriterium/IterationCountStopCriteriumTest.cs
new file mode 100644
index 00000000..ae95decc
--- /dev/null
+++ b/src/UnitTests/LinearAlgebraTests/Double/Solvers/StopCriterium/IterationCountStopCriteriumTest.cs
@@ -0,0 +1,96 @@
+namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double.Solvers.StopCriterium
+{
+ using LinearAlgebra.Double;
+ using LinearAlgebra.Double.Solvers.Status;
+ using LinearAlgebra.Double.Solvers.StopCriterium;
+ 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
+ }
+ }
+}
diff --git a/src/UnitTests/LinearAlgebraTests/Double/Solvers/StopCriterium/ResidualStopCriteriumTest.cs b/src/UnitTests/LinearAlgebraTests/Double/Solvers/StopCriterium/ResidualStopCriteriumTest.cs
new file mode 100644
index 00000000..51e8d893
--- /dev/null
+++ b/src/UnitTests/LinearAlgebraTests/Double/Solvers/StopCriterium/ResidualStopCriteriumTest.cs
@@ -0,0 +1,261 @@
+namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double.Solvers.StopCriterium
+{
+ using LinearAlgebra.Double;
+ using LinearAlgebra.Double.Solvers.Status;
+ using LinearAlgebra.Double.Solvers.StopCriterium;
+ 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[] { 1.0, 1.0, 2.0 });
+ var source = new DenseVector(new[] { 1.0, 1.0, double.NaN });
+ var residual = new DenseVector(new[] { 1000.0, 1000.0, 2001.0 });
+
+ 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[] { 1.0, 1.0, 2.0 });
+ var source = new DenseVector(new[] { 1.0, 1.0, 2.0 });
+ var residual = new DenseVector(new[] { 1000.0, double.NaN, 2001.0 });
+
+ 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[] { 1.0, 1.0, 1.0 });
+ var source = new DenseVector(new[] { 1.0, 1.0, 1.0 });
+ var residual = new DenseVector(new[] { 0.0, 0.0, 0.0 });
+
+ 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[] { double.NaN, double.NaN, double.NaN });
+
+ // Set the source values
+ var source = new DenseVector(new[] { 1.000, 1.000, 2.001 });
+
+ // Set the residual values
+ var residual = new DenseVector(new[] { 0.001, 0.001, 0.002 });
+
+ 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[] { 0.001, 0.001, 0.002 });
+ var source = new DenseVector(new[] { 0.001, 0.001, 0.002 });
+ var residual = new DenseVector(new[] { 1.000, 1.000, 2.001 });
+
+ 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
+ }
+ }
+}
diff --git a/src/UnitTests/LinearAlgebraTests/Double/SparseMatrixTests.cs b/src/UnitTests/LinearAlgebraTests/Double/SparseMatrixTests.cs
index 6c212960..3c8e37a9 100644
--- a/src/UnitTests/LinearAlgebraTests/Double/SparseMatrixTests.cs
+++ b/src/UnitTests/LinearAlgebraTests/Double/SparseMatrixTests.cs
@@ -70,7 +70,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
foreach (var name in testData.Keys)
{
- Assert.AreEqual(testMatrices[name], testData[name]);
+ Assert.AreEqual(TestMatrices[name], testData[name]);
}
}
@@ -87,9 +87,9 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[Test]
public void MatrixFrom2DArrayIsCopy()
{
- var matrix = new SparseMatrix(testData2D["Singular3x3"]);
+ var matrix = new SparseMatrix(TestData2D["Singular3x3"]);
matrix[0, 0] = 10.0;
- Assert.AreEqual(1.0, testData2D["Singular3x3"][0, 0]);
+ Assert.AreEqual(1.0, TestData2D["Singular3x3"][0, 0]);
}
[Test]
@@ -101,12 +101,12 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[Row("Wide2x3")]
public void CanCreateMatrixFrom2DArray(string name)
{
- var matrix = new SparseMatrix(testData2D[name]);
- for (var i = 0; i < testData2D[name].GetLength(0); i++)
+ 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++)
+ for (var j = 0; j < TestData2D[name].GetLength(1); j++)
{
- Assert.AreEqual(testData2D[name][i, j], matrix[i, j]);
+ Assert.AreEqual(TestData2D[name][i, j], matrix[i, j]);
}
}
}
@@ -143,7 +143,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
[ExpectedArgumentException]
public void IdentityFailsWithZeroOrNegativeOrder(int order)
{
- var matrix = SparseMatrix.Identity(order);
+ SparseMatrix.Identity(order);
}
[Test]
diff --git a/src/UnitTests/UnitTests.csproj b/src/UnitTests/UnitTests.csproj
index 5256c720..904d3373 100644
--- a/src/UnitTests/UnitTests.csproj
+++ b/src/UnitTests/UnitTests.csproj
@@ -90,6 +90,8 @@
+
+
@@ -99,6 +101,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+