136 changed files with 36718 additions and 3812 deletions
File diff suppressed because it is too large
File diff suppressed because it is too large
File diff suppressed because it is too large
@ -0,0 +1,710 @@ |
|||
// <copyright file="DenseMatrix.cs" company="Math.NET">
|
|||
// Math.NET Numerics, part of the Math.NET Project
|
|||
// http://numerics.mathdotnet.com
|
|||
// http://github.com/mathnet/mathnet-numerics
|
|||
// http://mathnetnumerics.codeplex.com
|
|||
// Copyright (c) 2009-2010 Math.NET
|
|||
// Permission is hereby granted, free of charge, to any person
|
|||
// obtaining a copy of this software and associated documentation
|
|||
// files (the "Software"), to deal in the Software without
|
|||
// restriction, including without limitation the rights to use,
|
|||
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|||
// copies of the Software, and to permit persons to whom the
|
|||
// Software is furnished to do so, subject to the following
|
|||
// conditions:
|
|||
// The above copyright notice and this permission notice shall be
|
|||
// included in all copies or substantial portions of the Software.
|
|||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|||
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|||
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|||
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|||
// OTHER DEALINGS IN THE SOFTWARE.
|
|||
// </copyright>
|
|||
|
|||
namespace MathNet.Numerics.LinearAlgebra.Single |
|||
{ |
|||
using System; |
|||
using Distributions; |
|||
using Generic; |
|||
using Properties; |
|||
using Threading; |
|||
|
|||
/// <summary>
|
|||
/// A Matrix class with dense storage. The underlying storage is a one dimensional array in column-major order.
|
|||
/// </summary>
|
|||
public class DenseMatrix : Matrix<float> |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="DenseMatrix"/> class. This matrix is square with a given size.
|
|||
/// </summary>
|
|||
/// <param name="order">the size of the square matrix.</param>
|
|||
/// <exception cref="ArgumentException">
|
|||
/// If <paramref name="order"/> is less than one.
|
|||
/// </exception>
|
|||
public DenseMatrix(int order) |
|||
: base(order) |
|||
{ |
|||
Data = new float[order * order]; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="DenseMatrix"/> class.
|
|||
/// </summary>
|
|||
/// <param name="rows">
|
|||
/// The number of rows.
|
|||
/// </param>
|
|||
/// <param name="columns">
|
|||
/// The number of columns.
|
|||
/// </param>
|
|||
public DenseMatrix(int rows, int columns) |
|||
: base(rows, columns) |
|||
{ |
|||
Data = new float[rows * columns]; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="DenseMatrix"/> class with all entries set to a particular value.
|
|||
/// </summary>
|
|||
/// <param name="rows">
|
|||
/// The number of rows.
|
|||
/// </param>
|
|||
/// <param name="columns">
|
|||
/// The number of columns.
|
|||
/// </param>
|
|||
/// <param name="value">The value which we assign to each element of the matrix.</param>
|
|||
public DenseMatrix(int rows, int columns, float value) |
|||
: base(rows, columns) |
|||
{ |
|||
Data = new float[rows * columns]; |
|||
for (var i = 0; i < Data.Length; i++) |
|||
{ |
|||
Data[i] = value; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="DenseMatrix"/> class from a one dimensional array. This constructor
|
|||
/// will reference the one dimensional array and not copy it.
|
|||
/// </summary>
|
|||
/// <param name="rows">The number of rows.</param>
|
|||
/// <param name="columns">The number of columns.</param>
|
|||
/// <param name="array">The one dimensional array to create this matrix from. This array should store the matrix in column-major order. <seealso cref="http://en.wikipedia.org/wiki/Row-major_order"/></param>
|
|||
public DenseMatrix(int rows, int columns, float[] array) |
|||
: base(rows, columns) |
|||
{ |
|||
Data = array; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="DenseMatrix"/> class from a 2D array. This constructor
|
|||
/// will allocate a completely new memory block for storing the dense matrix.
|
|||
/// </summary>
|
|||
/// <param name="array">The 2D array to create this matrix from.</param>
|
|||
public DenseMatrix(float[,] array) |
|||
: base(array.GetLength(0), array.GetLength(1)) |
|||
{ |
|||
var rows = array.GetLength(0); |
|||
var columns = array.GetLength(1); |
|||
Data = new float[rows * columns]; |
|||
for (var i = 0; i < rows; i++) |
|||
{ |
|||
for (var j = 0; j < columns; j++) |
|||
{ |
|||
Data[(j * rows) + i] = array[i, j]; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the matrix's data.
|
|||
/// </summary>
|
|||
/// <value>The matrix's data.</value>
|
|||
internal float[] Data |
|||
{ |
|||
get; |
|||
private set; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Creates a <c>DenseMatrix</c> for the given number of rows and columns.
|
|||
/// </summary>
|
|||
/// <param name="numberOfRows">
|
|||
/// The number of rows.
|
|||
/// </param>
|
|||
/// <param name="numberOfColumns">
|
|||
/// The number of columns.
|
|||
/// </param>
|
|||
/// <returns>
|
|||
/// A <c>DenseMatrix</c> with the given dimensions.
|
|||
/// </returns>
|
|||
public override Matrix<float> CreateMatrix(int numberOfRows, int numberOfColumns) |
|||
{ |
|||
return new DenseMatrix(numberOfRows, numberOfColumns); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Creates a <see cref="Vector{T}"/> with a the given dimension.
|
|||
/// </summary>
|
|||
/// <param name="size">The size of the vector.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Vector{T}"/> with the given dimension.
|
|||
/// </returns>
|
|||
public override Vector<float> CreateVector(int size) |
|||
{ |
|||
return new DenseVector(size); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Retrieves the requested element without range checking.
|
|||
/// </summary>
|
|||
/// <param name="row">
|
|||
/// The row of the element.
|
|||
/// </param>
|
|||
/// <param name="column">
|
|||
/// The column of the element.
|
|||
/// </param>
|
|||
/// <returns>
|
|||
/// The requested element.
|
|||
/// </returns>
|
|||
public override float At(int row, int column) |
|||
{ |
|||
return Data[(column * RowCount) + row]; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sets the value of the given element.
|
|||
/// </summary>
|
|||
/// <param name="row">
|
|||
/// The row of the element.
|
|||
/// </param>
|
|||
/// <param name="column">
|
|||
/// The column of the element.
|
|||
/// </param>
|
|||
/// <param name="value">
|
|||
/// The value to set the element to.
|
|||
/// </param>
|
|||
public override void At(int row, int column, float value) |
|||
{ |
|||
Data[(column * RowCount) + row] = value; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sets all values to zero.
|
|||
/// </summary>
|
|||
public override void Clear() |
|||
{ |
|||
Array.Clear(Data, 0, Data.Length); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns the transpose of this matrix.
|
|||
/// </summary>
|
|||
/// <returns>The transpose of this matrix.</returns>
|
|||
public override Matrix<float> Transpose() |
|||
{ |
|||
var ret = new DenseMatrix(ColumnCount, RowCount); |
|||
for (var j = 0; j < ColumnCount; j++) |
|||
{ |
|||
var index = j * RowCount; |
|||
for (var i = 0; i < RowCount; i++) |
|||
{ |
|||
ret.Data[(i * ColumnCount) + j] = Data[index + i]; |
|||
} |
|||
} |
|||
|
|||
return ret; |
|||
} |
|||
|
|||
/// <summary>Calculates the L1 norm.</summary>
|
|||
/// <returns>The L1 norm of the matrix.</returns>
|
|||
public override double L1Norm() |
|||
{ |
|||
var norm = 0.0; |
|||
for (var j = 0; j < ColumnCount; j++) |
|||
{ |
|||
var s = 0.0; |
|||
for (var i = 0; i < RowCount; i++) |
|||
{ |
|||
s += Math.Abs(Data[(j * RowCount) + i]); |
|||
} |
|||
|
|||
norm = Math.Max(norm, s); |
|||
} |
|||
|
|||
return norm; |
|||
} |
|||
|
|||
/// <summary>Calculates the Frobenius norm of this matrix.</summary>
|
|||
/// <returns>The Frobenius norm of this matrix.</returns>
|
|||
public override double FrobeniusNorm() |
|||
{ |
|||
var transpose = (DenseMatrix)Transpose(); |
|||
var aat = this * transpose; |
|||
|
|||
var norm = 0.0; |
|||
for (var i = 0; i < RowCount; i++) |
|||
{ |
|||
norm += Math.Abs(aat.Data[(i * RowCount) + i]); |
|||
} |
|||
|
|||
norm = Math.Sqrt(norm); |
|||
return norm; |
|||
} |
|||
|
|||
/// <summary>Calculates the infinity norm of this matrix.</summary>
|
|||
/// <returns>The infinity norm of this matrix.</returns>
|
|||
public override double InfinityNorm() |
|||
{ |
|||
var norm = 0.0; |
|||
for (var i = 0; i < RowCount; i++) |
|||
{ |
|||
var s = 0.0; |
|||
for (var j = 0; j < ColumnCount; j++) |
|||
{ |
|||
s += Math.Abs(Data[(j * RowCount) + i]); |
|||
} |
|||
|
|||
norm = Math.Max(norm, s); |
|||
} |
|||
|
|||
return norm; |
|||
} |
|||
|
|||
#region Elementary operations
|
|||
|
|||
/// <summary>
|
|||
/// Adds another matrix to this matrix. The result will be written into this matrix.
|
|||
/// </summary>
|
|||
/// <param name="other">The matrix to add to this matrix.</param>
|
|||
/// <exception cref="ArgumentNullException">If the other matrix is <see langword="null" />.</exception>
|
|||
/// <exception cref="ArgumentOutOfRangeException">If the two matrices don't have the same dimensions.</exception>
|
|||
public override void Add(Matrix<float> other) |
|||
{ |
|||
var m = other as DenseMatrix; |
|||
if (m == null) |
|||
{ |
|||
base.Add(other); |
|||
} |
|||
else |
|||
{ |
|||
Add(m); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Adds another <see cref="DenseMatrix"/> to this matrix. The result will be written into this matrix.
|
|||
/// </summary>
|
|||
/// <param name="other">The <see cref="DenseMatrix"/> to add to this matrix.</param>
|
|||
/// <exception cref="ArgumentNullException">If the other matrix is <see langword="null" />.</exception>
|
|||
/// <exception cref="ArgumentOutOfRangeException">If the two matrices don't have the same dimensions.</exception>
|
|||
public void Add(DenseMatrix other) |
|||
{ |
|||
if (other == null) |
|||
{ |
|||
throw new ArgumentNullException("other"); |
|||
} |
|||
|
|||
if (other.RowCount != RowCount || other.ColumnCount != ColumnCount) |
|||
{ |
|||
throw new ArgumentOutOfRangeException(Resources.ArgumentMatrixDimensions); |
|||
} |
|||
|
|||
Control.LinearAlgebraProvider.AddArrays(Data, other.Data, Data); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Subtracts another matrix from this matrix. The result will be written into this matrix.
|
|||
/// </summary>
|
|||
/// <param name="other">The matrix to subtract.</param>
|
|||
/// <exception cref="ArgumentNullException">If the other matrix is <see langword="null" />.</exception>
|
|||
/// <exception cref="ArgumentOutOfRangeException">If the two matrices don't have the same dimensions.</exception>
|
|||
public override void Subtract(Matrix<float> other) |
|||
{ |
|||
var m = other as DenseMatrix; |
|||
if (m == null) |
|||
{ |
|||
base.Subtract(other); |
|||
} |
|||
else |
|||
{ |
|||
Subtract(m); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Subtracts another <see cref="DenseMatrix"/> from this matrix. The result will be written into this matrix.
|
|||
/// </summary>
|
|||
/// <param name="other">The <see cref="DenseMatrix"/> to subtract.</param>
|
|||
/// <exception cref="ArgumentNullException">If the other matrix is <see langword="null" />.</exception>
|
|||
/// <exception cref="ArgumentOutOfRangeException">If the two matrices don't have the same dimensions.</exception>
|
|||
public void Subtract(DenseMatrix other) |
|||
{ |
|||
if (other == null) |
|||
{ |
|||
throw new ArgumentNullException("other"); |
|||
} |
|||
|
|||
if (other.RowCount != RowCount || other.ColumnCount != ColumnCount) |
|||
{ |
|||
throw new ArgumentOutOfRangeException(Resources.ArgumentMatrixDimensions); |
|||
} |
|||
|
|||
Control.LinearAlgebraProvider.SubtractArrays(Data, other.Data, Data); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Multiplies each element of this matrix with a scalar.
|
|||
/// </summary>
|
|||
/// <param name="scalar">The scalar to multiply with.</param>
|
|||
public override void Multiply(float scalar) |
|||
{ |
|||
Control.LinearAlgebraProvider.ScaleArray(scalar, Data); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Multiplies this dense matrix with another dense matrix and places the results into the result dense matrix.
|
|||
/// </summary>
|
|||
/// <param name="other">The matrix to multiply with.</param>
|
|||
/// <param name="result">The result of the multiplication.</param>
|
|||
/// <exception cref="ArgumentNullException">If the other matrix is <see langword="null" />.</exception>
|
|||
/// <exception cref="ArgumentNullException">If the result matrix is <see langword="null" />.</exception>
|
|||
/// <exception cref="ArgumentException">If <strong>this.Columns != other.Rows</strong>.</exception>
|
|||
/// <exception cref="ArgumentException">If the result matrix's dimensions are not the this.Rows x other.Columns.</exception>
|
|||
public override void Multiply(Matrix<float> other, Matrix<float> result) |
|||
{ |
|||
if (other == null) |
|||
{ |
|||
throw new ArgumentNullException("other"); |
|||
} |
|||
|
|||
if (result == null) |
|||
{ |
|||
throw new ArgumentNullException("result"); |
|||
} |
|||
|
|||
if (ColumnCount != other.RowCount) |
|||
{ |
|||
throw new ArgumentException(Resources.ArgumentMatrixDimensions); |
|||
} |
|||
|
|||
if (result.RowCount != RowCount || result.ColumnCount != other.ColumnCount) |
|||
{ |
|||
throw new ArgumentException(Resources.ArgumentMatrixDimensions); |
|||
} |
|||
|
|||
var m = other as DenseMatrix; |
|||
var r = result as DenseMatrix; |
|||
|
|||
if (m == null || r == null) |
|||
{ |
|||
base.Multiply(other, result); |
|||
} |
|||
else |
|||
{ |
|||
Control.LinearAlgebraProvider.MatrixMultiply( |
|||
Data, |
|||
RowCount, |
|||
ColumnCount, |
|||
m.Data, |
|||
m.RowCount, |
|||
m.ColumnCount, |
|||
r.Data); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Multiplies this matrix with another matrix and returns the result.
|
|||
/// </summary>
|
|||
/// <param name="other">The matrix to multiply with.</param>
|
|||
/// <exception cref="ArgumentException">If <strong>this.Columns != other.Rows</strong>.</exception>
|
|||
/// <exception cref="ArgumentNullException">If the other matrix is <see langword="null" />.</exception>
|
|||
/// <returns>The result of multiplication.</returns>
|
|||
public override Matrix<float> Multiply(Matrix<float> other) |
|||
{ |
|||
if (other == null) |
|||
{ |
|||
throw new ArgumentNullException("other"); |
|||
} |
|||
|
|||
if (ColumnCount != other.RowCount) |
|||
{ |
|||
throw new ArgumentException(Resources.ArgumentMatrixDimensions); |
|||
} |
|||
|
|||
var m = other as DenseMatrix; |
|||
if (m == null) |
|||
{ |
|||
return base.Multiply(other); |
|||
} |
|||
|
|||
var result = (DenseMatrix)CreateMatrix(RowCount, other.ColumnCount); |
|||
Multiply(other, result); |
|||
return result; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Multiplies this dense matrix with transpose of another dense matrix and places the results into the result dense matrix.
|
|||
/// </summary>
|
|||
/// <param name="other">The matrix to multiply with.</param>
|
|||
/// <param name="result">The result of the multiplication.</param>
|
|||
/// <exception cref="ArgumentNullException">If the other matrix is <see langword="null" />.</exception>
|
|||
/// <exception cref="ArgumentNullException">If the result matrix is <see langword="null" />.</exception>
|
|||
/// <exception cref="ArgumentException">If <strong>this.Columns != other.Rows</strong>.</exception>
|
|||
/// <exception cref="ArgumentException">If the result matrix's dimensions are not the this.Rows x other.Columns.</exception>
|
|||
public override void TransposeAndMultiply(Matrix<float> other, Matrix<float> 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.0f, |
|||
Data, |
|||
RowCount, |
|||
ColumnCount, |
|||
otherDense.Data, |
|||
otherDense.RowCount, |
|||
otherDense.ColumnCount, |
|||
1.0f, |
|||
resultDense.Data); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Multiplies this matrix with transpose of another matrix and returns the result.
|
|||
/// </summary>
|
|||
/// <param name="other">The matrix to multiply with.</param>
|
|||
/// <exception cref="ArgumentException">If <strong>this.Columns != other.Rows</strong>.</exception>
|
|||
/// <exception cref="ArgumentNullException">If the other matrix is <see langword="null" />.</exception>
|
|||
/// <returns>The result of multiplication.</returns>
|
|||
public override Matrix<float> TransposeAndMultiply(Matrix<float> other) |
|||
{ |
|||
var otherDense = other as DenseMatrix; |
|||
if (otherDense == null) |
|||
{ |
|||
return base.TransposeAndMultiply(other); |
|||
} |
|||
|
|||
if (ColumnCount != otherDense.ColumnCount) |
|||
{ |
|||
throw new ArgumentException(Resources.ArgumentMatrixDimensions); |
|||
} |
|||
|
|||
var result = (DenseMatrix)CreateMatrix(RowCount, other.RowCount); |
|||
TransposeAndMultiply(other, result); |
|||
return result; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Multiplies two dense matrices.
|
|||
/// </summary>
|
|||
/// <param name="leftSide">The left matrix to multiply.</param>
|
|||
/// <param name="rightSide">The right matrix to multiply.</param>
|
|||
/// <returns>The result of multiplication.</returns>
|
|||
/// <exception cref="ArgumentNullException">If <paramref name="leftSide"/> or <paramref name="rightSide"/> is <see langword="null" />.</exception>
|
|||
/// <exception cref="ArgumentException">If the dimensions of <paramref name="leftSide"/> or <paramref name="rightSide"/> don't conform.</exception>
|
|||
public static DenseMatrix operator *(DenseMatrix leftSide, DenseMatrix rightSide) |
|||
{ |
|||
if (leftSide == null) |
|||
{ |
|||
throw new ArgumentNullException("leftSide"); |
|||
} |
|||
|
|||
if (rightSide == null) |
|||
{ |
|||
throw new ArgumentNullException("rightSide"); |
|||
} |
|||
|
|||
if (leftSide.ColumnCount != rightSide.RowCount) |
|||
{ |
|||
throw new ArgumentException(Resources.ArgumentMatrixDimensions); |
|||
} |
|||
|
|||
return (DenseMatrix)leftSide.Multiply(rightSide); |
|||
} |
|||
|
|||
#endregion
|
|||
|
|||
#region Static constructors for special matrices.
|
|||
|
|||
/// <summary>
|
|||
/// Initializes a square <see cref="DenseMatrix"/> with all zero's except for ones on the diagonal.
|
|||
/// </summary>
|
|||
/// <param name="order">the size of the square matrix.</param>
|
|||
/// <returns>A dense identity matrix.</returns>
|
|||
/// <exception cref="ArgumentException">
|
|||
/// If <paramref name="order"/> is less than one.
|
|||
/// </exception>
|
|||
public static DenseMatrix Identity(int order) |
|||
{ |
|||
var m = new DenseMatrix(order); |
|||
for (var i = 0; i < order; i++) |
|||
{ |
|||
m[i, i] = 1.0f; |
|||
} |
|||
|
|||
return m; |
|||
} |
|||
|
|||
#endregion
|
|||
|
|||
/// <summary>
|
|||
/// Negates each element of this matrix.
|
|||
/// </summary>
|
|||
public override void Negate() |
|||
{ |
|||
Multiply(-1); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Generates matrix with random elements.
|
|||
/// </summary>
|
|||
/// <param name="numberOfRows">Number of rows.</param>
|
|||
/// <param name="numberOfColumns">Number of columns.</param>
|
|||
/// <param name="distribution">Continuous Random Distribution or Source</param>
|
|||
/// <returns>
|
|||
/// An <c>numberOfRows</c>-by-<c>numberOfColumns</c> matrix with elements distributed according to the provided distribution.
|
|||
/// </returns>
|
|||
/// <exception cref="ArgumentException">If the parameter <paramref name="numberOfRows"/> is not positive.</exception>
|
|||
/// <exception cref="ArgumentException">If the parameter <paramref name="numberOfColumns"/> is not positive.</exception>
|
|||
public override Matrix<float> Random(int numberOfRows, int numberOfColumns, IContinuousDistribution distribution) |
|||
{ |
|||
if (numberOfRows < 1) |
|||
{ |
|||
throw new ArgumentException(Resources.ArgumentMustBePositive, "numberOfRows"); |
|||
} |
|||
|
|||
if (numberOfColumns < 1) |
|||
{ |
|||
throw new ArgumentException(Resources.ArgumentMustBePositive, "numberOfColumns"); |
|||
} |
|||
|
|||
var matrix = CreateMatrix(numberOfRows, numberOfColumns); |
|||
CommonParallel.For( |
|||
0, |
|||
ColumnCount, |
|||
j => |
|||
{ |
|||
for (var i = 0; i < matrix.RowCount; i++) |
|||
{ |
|||
matrix[i, j] = (float)distribution.Sample(); |
|||
} |
|||
}); |
|||
|
|||
return matrix; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Generates matrix with random elements.
|
|||
/// </summary>
|
|||
/// <param name="numberOfRows">Number of rows.</param>
|
|||
/// <param name="numberOfColumns">Number of columns.</param>
|
|||
/// <param name="distribution">Continuous Random Distribution or Source</param>
|
|||
/// <returns>
|
|||
/// An <c>numberOfRows</c>-by-<c>numberOfColumns</c> matrix with elements distributed according to the provided distribution.
|
|||
/// </returns>
|
|||
/// <exception cref="ArgumentException">If the parameter <paramref name="numberOfRows"/> is not positive.</exception>
|
|||
/// <exception cref="ArgumentException">If the parameter <paramref name="numberOfColumns"/> is not positive.</exception>
|
|||
public override Matrix<float> Random(int numberOfRows, int numberOfColumns, IDiscreteDistribution distribution) |
|||
{ |
|||
if (numberOfRows < 1) |
|||
{ |
|||
throw new ArgumentException(Resources.ArgumentMustBePositive, "numberOfRows"); |
|||
} |
|||
|
|||
if (numberOfColumns < 1) |
|||
{ |
|||
throw new ArgumentException(Resources.ArgumentMustBePositive, "numberOfColumns"); |
|||
} |
|||
|
|||
var matrix = CreateMatrix(numberOfRows, numberOfColumns); |
|||
CommonParallel.For( |
|||
0, |
|||
ColumnCount, |
|||
j => |
|||
{ |
|||
for (var i = 0; i < matrix.RowCount; i++) |
|||
{ |
|||
matrix[i, j] = distribution.Sample(); |
|||
} |
|||
}); |
|||
|
|||
return matrix; |
|||
} |
|||
|
|||
#region Simple arithmetic of type T
|
|||
/// <summary>
|
|||
/// Add two values T+T
|
|||
/// </summary>
|
|||
/// <param name="val1">Left operand value</param>
|
|||
/// <param name="val2">Right operand value</param>
|
|||
/// <returns>Result of addition</returns>
|
|||
protected sealed override float AddT(float val1, float val2) |
|||
{ |
|||
return val1 + val2; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Subtract two values T-T
|
|||
/// </summary>
|
|||
/// <param name="val1">Left operand value</param>
|
|||
/// <param name="val2">Right operand value</param>
|
|||
/// <returns>Result of subtract</returns>
|
|||
protected sealed override float SubtractT(float val1, float val2) |
|||
{ |
|||
return val1 - val2; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Multiply two values T*T
|
|||
/// </summary>
|
|||
/// <param name="val1">Left operand value</param>
|
|||
/// <param name="val2">Right operand value</param>
|
|||
/// <returns>Result of multiplication</returns>
|
|||
protected sealed override float MultiplyT(float val1, float val2) |
|||
{ |
|||
return val1 * val2; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Divide two values T/T
|
|||
/// </summary>
|
|||
/// <param name="val1">Left operand value</param>
|
|||
/// <param name="val2">Right operand value</param>
|
|||
/// <returns>Result of divide</returns>
|
|||
protected sealed override float DivideT(float val1, float val2) |
|||
{ |
|||
return val1 / val2; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Take absolute value
|
|||
/// </summary>
|
|||
/// <param name="val1">Source alue</param>
|
|||
/// <returns>True if one; otherwise false</returns>
|
|||
protected sealed override double AbsoluteT(float val1) |
|||
{ |
|||
return Math.Abs(val1); |
|||
} |
|||
#endregion
|
|||
} |
|||
} |
|||
File diff suppressed because it is too large
File diff suppressed because it is too large
@ -0,0 +1,212 @@ |
|||
// <copyright file="DenseCholesky.cs" company="Math.NET">
|
|||
// Math.NET Numerics, part of the Math.NET Project
|
|||
// http://numerics.mathdotnet.com
|
|||
// http://github.com/mathnet/mathnet-numerics
|
|||
// http://mathnetnumerics.codeplex.com
|
|||
//
|
|||
// Copyright (c) 2009-2010 Math.NET
|
|||
//
|
|||
// Permission is hereby granted, free of charge, to any person
|
|||
// obtaining a copy of this software and associated documentation
|
|||
// files (the "Software"), to deal in the Software without
|
|||
// restriction, including without limitation the rights to use,
|
|||
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|||
// copies of the Software, and to permit persons to whom the
|
|||
// Software is furnished to do so, subject to the following
|
|||
// conditions:
|
|||
//
|
|||
// The above copyright notice and this permission notice shall be
|
|||
// included in all copies or substantial portions of the Software.
|
|||
//
|
|||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|||
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|||
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|||
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|||
// OTHER DEALINGS IN THE SOFTWARE.
|
|||
// </copyright>
|
|||
|
|||
namespace MathNet.Numerics.LinearAlgebra.Single.Factorization |
|||
{ |
|||
using System; |
|||
using Generic; |
|||
using Generic.Factorization; |
|||
using Properties; |
|||
|
|||
/// <summary>
|
|||
/// <para>A class which encapsulates the functionality of a Cholesky factorization for dense matrices.</para>
|
|||
/// <para>For a symmetric, positive definite matrix A, the Cholesky factorization
|
|||
/// is an lower triangular matrix L so that A = L*L'.</para>
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// The computation of the Cholesky factorization is done at construction time. If the matrix is not symmetric
|
|||
/// or positive definite, the constructor will throw an exception.
|
|||
/// </remarks>
|
|||
public class DenseCholesky : Cholesky<float> |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="DenseCholesky"/> class. This object will compute the
|
|||
/// Cholesky factorization when the constructor is called and cache it's factorization.
|
|||
/// </summary>
|
|||
/// <param name="matrix">The matrix to factor.</param>
|
|||
/// <exception cref="ArgumentNullException">If <paramref name="matrix"/> is <c>null</c>.</exception>
|
|||
/// <exception cref="ArgumentException">If <paramref name="matrix"/> is not a square matrix.</exception>
|
|||
/// <exception cref="ArgumentException">If <paramref name="matrix"/> is not positive definite.</exception>
|
|||
public DenseCholesky(DenseMatrix matrix) |
|||
{ |
|||
if (matrix == null) |
|||
{ |
|||
throw new ArgumentNullException("matrix"); |
|||
} |
|||
|
|||
if (matrix.RowCount != matrix.ColumnCount) |
|||
{ |
|||
throw new ArgumentException(Resources.ArgumentMatrixSquare); |
|||
} |
|||
|
|||
// Create a new matrix for the Cholesky factor, then perform factorization (while overwriting).
|
|||
var factor = (DenseMatrix)matrix.Clone(); |
|||
Control.LinearAlgebraProvider.CholeskyFactor(factor.Data, factor.RowCount); |
|||
CholeskyFactor = factor; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Solves a system of linear equations, <b>AX = B</b>, with A Cholesky factorized.
|
|||
/// </summary>
|
|||
/// <param name="input">The right hand side <see cref="Matrix{T}"/>, <b>B</b>.</param>
|
|||
/// <param name="result">The left hand side <see cref="Matrix{T}"/>, <b>X</b>.</param>
|
|||
public override void Solve(Matrix<float> input, Matrix<float> result) |
|||
{ |
|||
// Check for proper arguments.
|
|||
if (input == null) |
|||
{ |
|||
throw new ArgumentNullException("input"); |
|||
} |
|||
|
|||
if (result == null) |
|||
{ |
|||
throw new ArgumentNullException("result"); |
|||
} |
|||
|
|||
// Check for proper dimensions.
|
|||
if (result.RowCount != input.RowCount) |
|||
{ |
|||
throw new ArgumentException(Resources.ArgumentMatrixSameRowDimension); |
|||
} |
|||
|
|||
if (result.ColumnCount != input.ColumnCount) |
|||
{ |
|||
throw new ArgumentException(Resources.ArgumentMatrixSameColumnDimension); |
|||
} |
|||
|
|||
if (input.RowCount != CholeskyFactor.RowCount) |
|||
{ |
|||
throw new ArgumentException(Resources.ArgumentMatrixDimensions); |
|||
} |
|||
|
|||
var dinput = input as DenseMatrix; |
|||
if (dinput == null) |
|||
{ |
|||
throw new NotImplementedException("Can only do Cholesky factorization for dense matrices at the moment."); |
|||
} |
|||
|
|||
var dresult = result as DenseMatrix; |
|||
if (dresult == null) |
|||
{ |
|||
throw new NotImplementedException("Can only do Cholesky factorization for dense matrices at the moment."); |
|||
} |
|||
|
|||
// Copy the contents of input to result.
|
|||
Buffer.BlockCopy(dinput.Data, 0, dresult.Data, 0, dinput.Data.Length * Constants.SizeOfFloat); |
|||
|
|||
// Cholesky solve by overwriting result.
|
|||
var dfactor = (DenseMatrix)CholeskyFactor; |
|||
Control.LinearAlgebraProvider.CholeskySolveFactored(dfactor.Data, dfactor.RowCount, dresult.Data, dresult.RowCount, dresult.ColumnCount); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Solves a system of linear equations, <b>Ax = b</b>, with A Cholesky factorized.
|
|||
/// </summary>
|
|||
/// <param name="input">The right hand side vector, <b>b</b>.</param>
|
|||
/// <param name="result">The left hand side <see cref="Matrix{T}"/>, <b>x</b>.</param>
|
|||
public override void Solve(Vector<float> input, Vector<float> result) |
|||
{ |
|||
// Check for proper arguments.
|
|||
if (input == null) |
|||
{ |
|||
throw new ArgumentNullException("input"); |
|||
} |
|||
|
|||
if (result == null) |
|||
{ |
|||
throw new ArgumentNullException("result"); |
|||
} |
|||
|
|||
// Check for proper dimensions.
|
|||
if (input.Count != result.Count) |
|||
{ |
|||
throw new ArgumentException(Resources.ArgumentVectorsSameLength); |
|||
} |
|||
|
|||
if (input.Count != CholeskyFactor.RowCount) |
|||
{ |
|||
throw new ArgumentException(Resources.ArgumentMatrixDimensions); |
|||
} |
|||
|
|||
var dinput = input as DenseVector; |
|||
if (dinput == null) |
|||
{ |
|||
throw new NotImplementedException("Can only do Cholesky factorization for dense vectors at the moment."); |
|||
} |
|||
|
|||
var dresult = result as DenseVector; |
|||
if (dresult == null) |
|||
{ |
|||
throw new NotImplementedException("Can only do Cholesky factorization for dense vectors at the moment."); |
|||
} |
|||
|
|||
// Copy the contents of input to result.
|
|||
Buffer.BlockCopy(dinput.Data, 0, dresult.Data, 0, dinput.Data.Length * Constants.SizeOfFloat); |
|||
|
|||
// Cholesky solve by overwriting result.
|
|||
var dfactor = (DenseMatrix)CholeskyFactor; |
|||
Control.LinearAlgebraProvider.CholeskySolveFactored(dfactor.Data, dfactor.RowCount, dresult.Data, dresult.Count, 1); |
|||
} |
|||
|
|||
#region Simple arithmetic of type T
|
|||
/// <summary>
|
|||
/// Add two values T+T
|
|||
/// </summary>
|
|||
/// <param name="val1">Left operand value</param>
|
|||
/// <param name="val2">Right operand value</param>
|
|||
/// <returns>Result of addition</returns>
|
|||
protected sealed override float AddT(float val1, float val2) |
|||
{ |
|||
return val1 + val2; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Multiply two values T*T
|
|||
/// </summary>
|
|||
/// <param name="val1">Left operand value</param>
|
|||
/// <param name="val2">Right operand value</param>
|
|||
/// <returns>Result of multiplication</returns>
|
|||
protected sealed override float MultiplyT(float val1, float val2) |
|||
{ |
|||
return val1 * val2; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns the natural (base e) logarithm of a specified number.
|
|||
/// </summary>
|
|||
/// <param name="val1"> A number whose logarithm is to be found</param>
|
|||
/// <returns>Natural (base e) logarithm </returns>
|
|||
protected sealed override float LogT(float val1) |
|||
{ |
|||
return (float)Math.Log(val1); |
|||
} |
|||
#endregion
|
|||
} |
|||
} |
|||
@ -0,0 +1,204 @@ |
|||
// <copyright file="DenseLU.cs" company="Math.NET">
|
|||
// Math.NET Numerics, part of the Math.NET Project
|
|||
// http://numerics.mathdotnet.com
|
|||
// http://github.com/mathnet/mathnet-numerics
|
|||
// http://mathnetnumerics.codeplex.com
|
|||
//
|
|||
// Copyright (c) 2009-2010 Math.NET
|
|||
//
|
|||
// Permission is hereby granted, free of charge, to any person
|
|||
// obtaining a copy of this software and associated documentation
|
|||
// files (the "Software"), to deal in the Software without
|
|||
// restriction, including without limitation the rights to use,
|
|||
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|||
// copies of the Software, and to permit persons to whom the
|
|||
// Software is furnished to do so, subject to the following
|
|||
// conditions:
|
|||
//
|
|||
// The above copyright notice and this permission notice shall be
|
|||
// included in all copies or substantial portions of the Software.
|
|||
//
|
|||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|||
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|||
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|||
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|||
// OTHER DEALINGS IN THE SOFTWARE.
|
|||
// </copyright>
|
|||
|
|||
namespace MathNet.Numerics.LinearAlgebra.Single.Factorization |
|||
{ |
|||
using System; |
|||
using Generic; |
|||
using Generic.Factorization; |
|||
using Properties; |
|||
|
|||
/// <summary>
|
|||
/// <para>A class which encapsulates the functionality of an LU factorization.</para>
|
|||
/// <para>For a matrix A, the LU factorization is a pair of lower triangular matrix L and
|
|||
/// upper triangular matrix U so that A = L*U.</para>
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// The computation of the LU factorization is done at construction time.
|
|||
/// </remarks>
|
|||
public class DenseLU : LU<float> |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="DenseLU"/> class. This object will compute the
|
|||
/// LU factorization when the constructor is called and cache it's factorization.
|
|||
/// </summary>
|
|||
/// <param name="matrix">The matrix to factor.</param>
|
|||
/// <exception cref="ArgumentNullException">If <paramref name="matrix"/> is <c>null</c>.</exception>
|
|||
/// <exception cref="ArgumentException">If <paramref name="matrix"/> is not a square matrix.</exception>
|
|||
public DenseLU(DenseMatrix matrix) |
|||
{ |
|||
if (matrix == null) |
|||
{ |
|||
throw new ArgumentNullException("matrix"); |
|||
} |
|||
|
|||
if (matrix.RowCount != matrix.ColumnCount) |
|||
{ |
|||
throw new ArgumentException(Resources.ArgumentMatrixSquare); |
|||
} |
|||
|
|||
// Create an array for the pivot indices.
|
|||
Pivots = new int[matrix.RowCount]; |
|||
|
|||
// Create a new matrix for the LU factors, then perform factorization (while overwriting).
|
|||
var factors = (DenseMatrix)matrix.Clone(); |
|||
Control.LinearAlgebraProvider.LUFactor(factors.Data, factors.RowCount, Pivots); |
|||
Factors = factors; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Solves a system of linear equations, <c>AX = B</c>, with A LU factorized.
|
|||
/// </summary>
|
|||
/// <param name="input">The right hand side <see cref="Matrix{T}"/>, <c>B</c>.</param>
|
|||
/// <param name="result">The left hand side <see cref="Matrix{T}"/>, <c>X</c>.</param>
|
|||
public override void Solve(Matrix<float> input, Matrix<float> result) |
|||
{ |
|||
// Check for proper arguments.
|
|||
if (input == null) |
|||
{ |
|||
throw new ArgumentNullException("input"); |
|||
} |
|||
|
|||
if (result == null) |
|||
{ |
|||
throw new ArgumentNullException("result"); |
|||
} |
|||
|
|||
// Check for proper dimensions.
|
|||
if (result.RowCount != input.RowCount) |
|||
{ |
|||
throw new ArgumentException(Resources.ArgumentMatrixSameRowDimension); |
|||
} |
|||
|
|||
if (result.ColumnCount != input.ColumnCount) |
|||
{ |
|||
throw new ArgumentException(Resources.ArgumentMatrixSameColumnDimension); |
|||
} |
|||
|
|||
if (input.RowCount != Factors.RowCount) |
|||
{ |
|||
throw new ArgumentException(Resources.ArgumentMatrixDimensions); |
|||
} |
|||
|
|||
var dinput = input as DenseMatrix; |
|||
if (dinput == null) |
|||
{ |
|||
throw new NotImplementedException("Can only do LU factorization for dense matrices at the moment."); |
|||
} |
|||
|
|||
var dresult = result as DenseMatrix; |
|||
if (dresult == null) |
|||
{ |
|||
throw new NotImplementedException("Can only do LU factorization for dense matrices at the moment."); |
|||
} |
|||
|
|||
// Copy the contents of input to result.
|
|||
Buffer.BlockCopy(dinput.Data, 0, dresult.Data, 0, dinput.Data.Length * Constants.SizeOfFloat); |
|||
|
|||
// LU solve by overwriting result.
|
|||
var dfactors = (DenseMatrix)Factors; |
|||
Control.LinearAlgebraProvider.LUSolveFactored(input.ColumnCount, dfactors.Data, dfactors.RowCount, Pivots, dresult.Data); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Solves a system of linear equations, <c>Ax = b</c>, with A LU factorized.
|
|||
/// </summary>
|
|||
/// <param name="input">The right hand side vector, <c>b</c>.</param>
|
|||
/// <param name="result">The left hand side <see cref="Matrix{T}"/>, <c>x</c>.</param>
|
|||
public override void Solve(Vector<float> input, Vector<float> result) |
|||
{ |
|||
// Check for proper arguments.
|
|||
if (input == null) |
|||
{ |
|||
throw new ArgumentNullException("input"); |
|||
} |
|||
|
|||
if (result == null) |
|||
{ |
|||
throw new ArgumentNullException("result"); |
|||
} |
|||
|
|||
// Check for proper dimensions.
|
|||
if (input.Count != result.Count) |
|||
{ |
|||
throw new ArgumentException(Resources.ArgumentVectorsSameLength); |
|||
} |
|||
|
|||
if (input.Count != Factors.RowCount) |
|||
{ |
|||
throw new ArgumentException(Resources.ArgumentMatrixDimensions); |
|||
} |
|||
|
|||
var dinput = input as DenseVector; |
|||
if (dinput == null) |
|||
{ |
|||
throw new NotImplementedException("Can only do LU factorization for dense vectors at the moment."); |
|||
} |
|||
|
|||
var dresult = result as DenseVector; |
|||
if (dresult == null) |
|||
{ |
|||
throw new NotImplementedException("Can only do LU factorization for dense vectors at the moment."); |
|||
} |
|||
|
|||
// Copy the contents of input to result.
|
|||
Buffer.BlockCopy(dinput.Data, 0, dresult.Data, 0, dinput.Data.Length * Constants.SizeOfFloat); |
|||
|
|||
// LU solve by overwriting result.
|
|||
var dfactors = (DenseMatrix)Factors; |
|||
Control.LinearAlgebraProvider.LUSolveFactored(1, dfactors.Data, dfactors.RowCount, Pivots, dresult.Data); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns the inverse of this matrix. The inverse is calculated using LU decomposition.
|
|||
/// </summary>
|
|||
/// <returns>The inverse of this matrix.</returns>
|
|||
public override Matrix<float> Inverse() |
|||
{ |
|||
var result = (DenseMatrix)Factors.Clone(); |
|||
Control.LinearAlgebraProvider.LUInverseFactored(result.Data, result.RowCount, Pivots); |
|||
return result; |
|||
} |
|||
|
|||
#region Simple arithmetic of type T
|
|||
|
|||
/// <summary>
|
|||
/// Multiply two values T*T
|
|||
/// </summary>
|
|||
/// <param name="val1">Left operand value</param>
|
|||
/// <param name="val2">Right operand value</param>
|
|||
/// <returns>Result of multiplication</returns>
|
|||
protected sealed override float MultiplyT(float val1, float val2) |
|||
{ |
|||
return val1 * val2; |
|||
} |
|||
#endregion
|
|||
} |
|||
} |
|||
@ -0,0 +1,194 @@ |
|||
// <copyright file="DenseQR.cs" company="Math.NET">
|
|||
// Math.NET Numerics, part of the Math.NET Project
|
|||
// http://numerics.mathdotnet.com
|
|||
// http://github.com/mathnet/mathnet-numerics
|
|||
// http://mathnetnumerics.codeplex.com
|
|||
//
|
|||
// Copyright (c) 2009-2010 Math.NET
|
|||
//
|
|||
// Permission is hereby granted, free of charge, to any person
|
|||
// obtaining a copy of this software and associated documentation
|
|||
// files (the "Software"), to deal in the Software without
|
|||
// restriction, including without limitation the rights to use,
|
|||
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|||
// copies of the Software, and to permit persons to whom the
|
|||
// Software is furnished to do so, subject to the following
|
|||
// conditions:
|
|||
//
|
|||
// The above copyright notice and this permission notice shall be
|
|||
// included in all copies or substantial portions of the Software.
|
|||
//
|
|||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|||
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|||
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|||
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|||
// OTHER DEALINGS IN THE SOFTWARE.
|
|||
// </copyright>
|
|||
|
|||
namespace MathNet.Numerics.LinearAlgebra.Single.Factorization |
|||
{ |
|||
using System; |
|||
using Generic; |
|||
using Generic.Factorization; |
|||
using Properties; |
|||
|
|||
/// <summary>
|
|||
/// <para>A class which encapsulates the functionality of the QR decomposition.</para>
|
|||
/// <para>Any real square matrix A may be decomposed as A = QR where Q is an orthogonal matrix
|
|||
/// (its columns are orthogonal unit vectors meaning QTQ = I) and R is an upper triangular matrix
|
|||
/// (also called right triangular matrix).</para>
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// The computation of the QR decomposition is done at construction time by Householder transformation.
|
|||
/// </remarks>
|
|||
public class DenseQR : QR<float> |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="DenseQR"/> class. This object will compute the
|
|||
/// QR factorization when the constructor is called and cache it's factorization.
|
|||
/// </summary>
|
|||
/// <param name="matrix">The matrix to factor.</param>
|
|||
/// <exception cref="ArgumentNullException">If <paramref name="matrix"/> is <c>null</c>.</exception>
|
|||
/// <exception cref="ArgumentException">If <paramref name="matrix"/> row count is less then column count</exception>
|
|||
public DenseQR(DenseMatrix matrix) |
|||
{ |
|||
if (matrix == null) |
|||
{ |
|||
throw new ArgumentNullException("matrix"); |
|||
} |
|||
|
|||
if (matrix.RowCount < matrix.ColumnCount) |
|||
{ |
|||
throw new ArgumentException(Resources.ArgumentMatrixDimensions); |
|||
} |
|||
|
|||
MatrixR = matrix.Clone(); |
|||
MatrixQ = new DenseMatrix(matrix.RowCount); |
|||
Control.LinearAlgebraProvider.QRFactor(((DenseMatrix)MatrixR).Data, matrix.RowCount, matrix.ColumnCount, ((DenseMatrix)MatrixQ).Data); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Solves a system of linear equations, <b>AX = B</b>, with A QR factorized.
|
|||
/// </summary>
|
|||
/// <param name="input">The right hand side <see cref="Matrix{T}"/>, <b>B</b>.</param>
|
|||
/// <param name="result">The left hand side <see cref="Matrix{T}"/>, <b>X</b>.</param>
|
|||
public override void Solve(Matrix<float> input, Matrix<float> result) |
|||
{ |
|||
// Check for proper arguments.
|
|||
if (input == null) |
|||
{ |
|||
throw new ArgumentNullException("input"); |
|||
} |
|||
|
|||
if (result == null) |
|||
{ |
|||
throw new ArgumentNullException("result"); |
|||
} |
|||
|
|||
// The solution X should have the same number of columns as B
|
|||
if (input.ColumnCount != result.ColumnCount) |
|||
{ |
|||
throw new ArgumentException(Resources.ArgumentMatrixSameColumnDimension); |
|||
} |
|||
|
|||
// The dimension compatibility conditions for X = A\B require the two matrices A and B to have the same number of rows
|
|||
if (MatrixR.RowCount != input.RowCount) |
|||
{ |
|||
throw new ArgumentException(Resources.ArgumentMatrixSameRowDimension); |
|||
} |
|||
|
|||
// The solution X row dimension is equal to the column dimension of A
|
|||
if (MatrixR.ColumnCount != result.RowCount) |
|||
{ |
|||
throw new ArgumentException(Resources.ArgumentMatrixSameColumnDimension); |
|||
} |
|||
|
|||
var dinput = input as DenseMatrix; |
|||
if (dinput == null) |
|||
{ |
|||
throw new NotImplementedException("Can only do QR factorization for dense matrices at the moment."); |
|||
} |
|||
|
|||
var dresult = result as DenseMatrix; |
|||
if (dresult == null) |
|||
{ |
|||
throw new NotImplementedException("Can only do QR factorization for dense matrices at the moment."); |
|||
} |
|||
|
|||
Control.LinearAlgebraProvider.QRSolveFactored(((DenseMatrix)MatrixQ).Data, ((DenseMatrix)MatrixR).Data, MatrixR.RowCount, MatrixR.ColumnCount, dinput.Data, input.ColumnCount, dresult.Data); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Solves a system of linear equations, <b>Ax = b</b>, with A QR factorized.
|
|||
/// </summary>
|
|||
/// <param name="input">The right hand side vector, <b>b</b>.</param>
|
|||
/// <param name="result">The left hand side <see cref="Matrix{T}"/>, <b>x</b>.</param>
|
|||
public override void Solve(Vector<float> input, Vector<float> result) |
|||
{ |
|||
if (input == null) |
|||
{ |
|||
throw new ArgumentNullException("input"); |
|||
} |
|||
|
|||
if (result == null) |
|||
{ |
|||
throw new ArgumentNullException("result"); |
|||
} |
|||
|
|||
// Ax=b where A is an m x n matrix
|
|||
// Check that b is a column vector with m entries
|
|||
if (MatrixR.RowCount != input.Count) |
|||
{ |
|||
throw new ArgumentException(Resources.ArgumentVectorsSameLength); |
|||
} |
|||
|
|||
// Check that x is a column vector with n entries
|
|||
if (MatrixR.ColumnCount != result.Count) |
|||
{ |
|||
throw new ArgumentException(Resources.ArgumentMatrixDimensions); |
|||
} |
|||
|
|||
var dinput = input as DenseVector; |
|||
if (dinput == null) |
|||
{ |
|||
throw new NotImplementedException("Can only do QR factorization for dense vectors at the moment."); |
|||
} |
|||
|
|||
var dresult = result as DenseVector; |
|||
if (dresult == null) |
|||
{ |
|||
throw new NotImplementedException("Can only do QR factorization for dense vectors at the moment."); |
|||
} |
|||
|
|||
Control.LinearAlgebraProvider.QRSolveFactored(((DenseMatrix)MatrixQ).Data, ((DenseMatrix)MatrixR).Data, MatrixR.RowCount, MatrixR.ColumnCount, dinput.Data, 1, dresult.Data); |
|||
} |
|||
|
|||
#region Simple arithmetic of type T
|
|||
|
|||
/// <summary>
|
|||
/// Multiply two values T*T
|
|||
/// </summary>
|
|||
/// <param name="val1">Left operand value</param>
|
|||
/// <param name="val2">Right operand value</param>
|
|||
/// <returns>Result of multiplication</returns>
|
|||
protected sealed override float MultiplyT(float val1, float val2) |
|||
{ |
|||
return val1 * val2; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns the absolute value of a specified number.
|
|||
/// </summary>
|
|||
/// <param name="val1"> A number whose absolute is to be found</param>
|
|||
/// <returns>Absolute value </returns>
|
|||
protected sealed override double AbsoluteT(float val1) |
|||
{ |
|||
return Math.Abs(val1); |
|||
} |
|||
|
|||
#endregion
|
|||
} |
|||
} |
|||
@ -0,0 +1,206 @@ |
|||
// <copyright file="DenseSvd.cs" company="Math.NET">
|
|||
// Math.NET Numerics, part of the Math.NET Project
|
|||
// http://numerics.mathdotnet.com
|
|||
// http://github.com/mathnet/mathnet-numerics
|
|||
// http://mathnetnumerics.codeplex.com
|
|||
//
|
|||
// Copyright (c) 2009-2010 Math.NET
|
|||
//
|
|||
// Permission is hereby granted, free of charge, to any person
|
|||
// obtaining a copy of this software and associated documentation
|
|||
// files (the "Software"), to deal in the Software without
|
|||
// restriction, including without limitation the rights to use,
|
|||
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|||
// copies of the Software, and to permit persons to whom the
|
|||
// Software is furnished to do so, subject to the following
|
|||
// conditions:
|
|||
//
|
|||
// The above copyright notice and this permission notice shall be
|
|||
// included in all copies or substantial portions of the Software.
|
|||
//
|
|||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|||
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|||
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|||
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|||
// OTHER DEALINGS IN THE SOFTWARE.
|
|||
// </copyright>
|
|||
namespace MathNet.Numerics.LinearAlgebra.Single.Factorization |
|||
{ |
|||
using System; |
|||
using Generic; |
|||
using Generic.Factorization; |
|||
using Properties; |
|||
|
|||
/// <summary>
|
|||
/// <para>A class which encapsulates the functionality of the singular value decomposition (SVD) for <see cref="DenseMatrix"/>.</para>
|
|||
/// <para>Suppose M is an m-by-n matrix whose entries are real numbers.
|
|||
/// Then there exists a factorization of the form M = UΣVT where:
|
|||
/// - U is an m-by-m unitary matrix;
|
|||
/// - Σ is m-by-n diagonal matrix with nonnegative real numbers on the diagonal;
|
|||
/// - VT denotes transpose of V, an n-by-n unitary matrix;
|
|||
/// Such a factorization is called a singular-value decomposition of M. A common convention is to order the diagonal
|
|||
/// entries Σ(i,i) in descending order. In this case, the diagonal matrix Σ is uniquely determined
|
|||
/// by M (though the matrices U and V are not). The diagonal entries of Σ are known as the singular values of M.</para>
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// The computation of the singular value decomposition is done at construction time.
|
|||
/// </remarks>
|
|||
public class DenseSvd : Svd<float> |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="DenseSvd"/> class. This object will compute the
|
|||
/// the singular value decomposition when the constructor is called and cache it's decomposition.
|
|||
/// </summary>
|
|||
/// <param name="matrix">The matrix to factor.</param>
|
|||
/// <param name="computeVectors">Compute the singular U and VT vectors or not.</param>
|
|||
/// <exception cref="ArgumentNullException">If <paramref name="matrix"/> is <b>null</b>.</exception>
|
|||
/// <exception cref="ArgumentException">If SVD algorithm failed to converge with matrix <paramref name="matrix"/>.</exception>
|
|||
public DenseSvd(DenseMatrix matrix, bool computeVectors) |
|||
{ |
|||
if (matrix == null) |
|||
{ |
|||
throw new ArgumentNullException("matrix"); |
|||
} |
|||
|
|||
ComputeVectors = computeVectors; |
|||
var nm = Math.Min(matrix.RowCount, matrix.ColumnCount); |
|||
VectorS = new DenseVector(nm); |
|||
MatrixU = new DenseMatrix(matrix.RowCount); |
|||
MatrixVT = new DenseMatrix(matrix.ColumnCount); |
|||
Control.LinearAlgebraProvider.SingularValueDecomposition(computeVectors, ((DenseMatrix)matrix.Clone()).Data, matrix.RowCount, matrix.ColumnCount, ((DenseVector)VectorS).Data, ((DenseMatrix)MatrixU).Data, ((DenseMatrix)MatrixVT).Data); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Solves a system of linear equations, <b>AX = B</b>, with A SVD factorized.
|
|||
/// </summary>
|
|||
/// <param name="input">The right hand side <see cref="Matrix{T}"/>, <b>B</b>.</param>
|
|||
/// <param name="result">The left hand side <see cref="Matrix{T}"/>, <b>X</b>.</param>
|
|||
public override void Solve(Matrix<float> input, Matrix<float> result) |
|||
{ |
|||
// Check for proper arguments.
|
|||
if (input == null) |
|||
{ |
|||
throw new ArgumentNullException("input"); |
|||
} |
|||
|
|||
if (result == null) |
|||
{ |
|||
throw new ArgumentNullException("result"); |
|||
} |
|||
|
|||
if (!ComputeVectors) |
|||
{ |
|||
throw new InvalidOperationException(Resources.SingularVectorsNotComputed); |
|||
} |
|||
|
|||
// The solution X should have the same number of columns as B
|
|||
if (input.ColumnCount != result.ColumnCount) |
|||
{ |
|||
throw new ArgumentException(Resources.ArgumentMatrixSameColumnDimension); |
|||
} |
|||
|
|||
// The dimension compatibility conditions for X = A\B require the two matrices A and B to have the same number of rows
|
|||
if (MatrixU.RowCount != input.RowCount) |
|||
{ |
|||
throw new ArgumentException(Resources.ArgumentMatrixSameRowDimension); |
|||
} |
|||
|
|||
// The solution X row dimension is equal to the column dimension of A
|
|||
if (MatrixVT.ColumnCount != result.RowCount) |
|||
{ |
|||
throw new ArgumentException(Resources.ArgumentMatrixSameColumnDimension); |
|||
} |
|||
|
|||
var dinput = input as DenseMatrix; |
|||
if (dinput == null) |
|||
{ |
|||
throw new NotImplementedException("Can only do SVD factorization for dense matrices at the moment."); |
|||
} |
|||
|
|||
var dresult = result as DenseMatrix; |
|||
if (dresult == null) |
|||
{ |
|||
throw new NotImplementedException("Can only do SVD factorization for dense matrices at the moment."); |
|||
} |
|||
|
|||
Control.LinearAlgebraProvider.SvdSolveFactored(MatrixU.RowCount, MatrixVT.ColumnCount, ((DenseVector)VectorS).Data, ((DenseMatrix)MatrixU).Data, ((DenseMatrix)MatrixVT).Data, dinput.Data, input.ColumnCount, dresult.Data); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Solves a system of linear equations, <b>Ax = b</b>, with A SVD factorized.
|
|||
/// </summary>
|
|||
/// <param name="input">The right hand side vector, <b>b</b>.</param>
|
|||
/// <param name="result">The left hand side <see cref="Matrix{T}"/>, <b>x</b>.</param>
|
|||
public override void Solve(Vector<float> input, Vector<float> result) |
|||
{ |
|||
if (input == null) |
|||
{ |
|||
throw new ArgumentNullException("input"); |
|||
} |
|||
|
|||
if (result == null) |
|||
{ |
|||
throw new ArgumentNullException("result"); |
|||
} |
|||
|
|||
if (!ComputeVectors) |
|||
{ |
|||
throw new InvalidOperationException(Resources.SingularVectorsNotComputed); |
|||
} |
|||
|
|||
// Ax=b where A is an m x n matrix
|
|||
// Check that b is a column vector with m entries
|
|||
if (MatrixU.RowCount != input.Count) |
|||
{ |
|||
throw new ArgumentException(Resources.ArgumentVectorsSameLength); |
|||
} |
|||
|
|||
// Check that x is a column vector with n entries
|
|||
if (MatrixVT.ColumnCount != result.Count) |
|||
{ |
|||
throw new ArgumentException(Resources.ArgumentMatrixDimensions); |
|||
} |
|||
|
|||
var dinput = input as DenseVector; |
|||
if (dinput == null) |
|||
{ |
|||
throw new NotImplementedException("Can only do SVD factorization for dense vectors at the moment."); |
|||
} |
|||
|
|||
var dresult = result as DenseVector; |
|||
if (dresult == null) |
|||
{ |
|||
throw new NotImplementedException("Can only do SVD factorization for dense vectors at the moment."); |
|||
} |
|||
|
|||
Control.LinearAlgebraProvider.SvdSolveFactored(MatrixU.RowCount, MatrixVT.ColumnCount, ((DenseVector)VectorS).Data, ((DenseMatrix)MatrixU).Data, ((DenseMatrix)MatrixVT).Data, dinput.Data, 1, dresult.Data); |
|||
} |
|||
|
|||
#region Simple arithmetic of type T
|
|||
|
|||
/// <summary>
|
|||
/// Multiply two values T*T
|
|||
/// </summary>
|
|||
/// <param name="val1">Left operand value</param>
|
|||
/// <param name="val2">Right operand value</param>
|
|||
/// <returns>Result of multiplication</returns>
|
|||
protected sealed override float MultiplyT(float val1, float val2) |
|||
{ |
|||
return val1 * val2; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns the absolute value of a specified number.
|
|||
/// </summary>
|
|||
/// <param name="val1"> A number whose absolute is to be found</param>
|
|||
/// <returns>Absolute value </returns>
|
|||
protected sealed override double AbsoluteT(float val1) |
|||
{ |
|||
return Math.Abs(val1); |
|||
} |
|||
#endregion
|
|||
} |
|||
} |
|||
@ -0,0 +1,311 @@ |
|||
// <copyright file="GramSchmidt.cs" company="Math.NET">
|
|||
// Math.NET Numerics, part of the Math.NET Project
|
|||
// http://numerics.mathdotnet.com
|
|||
// http://github.com/mathnet/mathnet-numerics
|
|||
// http://mathnetnumerics.codeplex.com
|
|||
//
|
|||
// Copyright (c) 2009-2010 Math.NET
|
|||
//
|
|||
// Permission is hereby granted, free of charge, to any person
|
|||
// obtaining a copy of this software and associated documentation
|
|||
// files (the "Software"), to deal in the Software without
|
|||
// restriction, including without limitation the rights to use,
|
|||
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|||
// copies of the Software, and to permit persons to whom the
|
|||
// Software is furnished to do so, subject to the following
|
|||
// conditions:
|
|||
//
|
|||
// The above copyright notice and this permission notice shall be
|
|||
// included in all copies or substantial portions of the Software.
|
|||
//
|
|||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|||
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|||
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|||
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|||
// OTHER DEALINGS IN THE SOFTWARE.
|
|||
// </copyright>
|
|||
|
|||
namespace MathNet.Numerics.LinearAlgebra.Single.Factorization |
|||
{ |
|||
using System; |
|||
using Generic; |
|||
using Generic.Factorization; |
|||
using Properties; |
|||
|
|||
/// <summary>
|
|||
/// <para>A class which encapsulates the functionality of the QR decomposition Modified Gram-Schmidt Orthogonalization.</para>
|
|||
/// <para>Any 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.</para>
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// The computation of the QR decomposition is done at construction time by modified Gram-Schmidt Orthogonalization.
|
|||
/// </remarks>
|
|||
public class GramSchmidt : QR<float> |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="GramSchmidt"/> class. This object creates an orthogonal matrix
|
|||
/// using the modified Gram-Schmidt method.
|
|||
/// </summary>
|
|||
/// <param name="matrix">The matrix to factor.</param>
|
|||
/// <exception cref="ArgumentNullException">If <paramref name="matrix"/> is <c>null</c>.</exception>
|
|||
/// <exception cref="ArgumentException">If <paramref name="matrix"/> row count is less then column count</exception>
|
|||
/// <exception cref="ArgumentException">If <paramref name="matrix"/> is rank deficient</exception>
|
|||
public GramSchmidt(Matrix<float> 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 = (float)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); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets a value indicating whether the matrix is full rank or not.
|
|||
/// </summary>
|
|||
/// <value><c>true</c> if the matrix is full rank; otherwise <c>false</c>.</value>
|
|||
public override bool IsFullRank |
|||
{ |
|||
get |
|||
{ |
|||
return true; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the determinant of the matrix for which the QR matrix was computed.
|
|||
/// </summary>
|
|||
public override double Determinant |
|||
{ |
|||
get |
|||
{ |
|||
if (MatrixQ.RowCount != MatrixQ.ColumnCount) |
|||
{ |
|||
throw new ArgumentException(Resources.ArgumentMatrixSquare); |
|||
} |
|||
|
|||
var det = 1.0; |
|||
for (var i = 0; i < MatrixR.ColumnCount; i++) |
|||
{ |
|||
det *= MatrixR.At(i, i); |
|||
if (Math.Abs(MatrixR.At(i, i)).AlmostEqualInDecimalPlaces(0.0f, 7)) |
|||
{ |
|||
return 0; |
|||
} |
|||
} |
|||
|
|||
return Math.Abs(det); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Solves a system of linear equations, <b>AX = B</b>, with A QR factorized.
|
|||
/// </summary>
|
|||
/// <param name="input">The right hand side <see cref="Matrix{T}"/>, <b>B</b>.</param>
|
|||
/// <param name="result">The left hand side <see cref="Matrix{T}"/>, <b>X</b>.</param>
|
|||
public override void Solve(Matrix<float> input, Matrix<float> 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 float[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++) |
|||
{ |
|||
float 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)); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Solves a system of linear equations, <b>Ax = b</b>, with A QR factorized.
|
|||
/// </summary>
|
|||
/// <param name="input">The right hand side vector, <b>b</b>.</param>
|
|||
/// <param name="result">The left hand side <see cref="Matrix{T}"/>, <b>x</b>.</param>
|
|||
public override void Solve(Vector<float> input, Vector<float> 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 float[MatrixQ.RowCount]; |
|||
for (var k = 0; k < MatrixQ.RowCount; k++) |
|||
{ |
|||
column[k] = inputCopy[k]; |
|||
} |
|||
|
|||
for (var i = 0; i < MatrixQ.ColumnCount; i++) |
|||
{ |
|||
float 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]; |
|||
} |
|||
} |
|||
|
|||
#region Simple arithmetic of type T
|
|||
|
|||
/// <summary>
|
|||
/// Multiply two values T*T
|
|||
/// </summary>
|
|||
/// <param name="val1">Left operand value</param>
|
|||
/// <param name="val2">Right operand value</param>
|
|||
/// <returns>Result of multiplication</returns>
|
|||
protected sealed override float MultiplyT(float val1, float val2) |
|||
{ |
|||
return val1 * val2; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns the absolute value of a specified number.
|
|||
/// </summary>
|
|||
/// <param name="val1"> A number whose absolute is to be found</param>
|
|||
/// <returns>Absolute value </returns>
|
|||
protected sealed override double AbsoluteT(float val1) |
|||
{ |
|||
return Math.Abs(val1); |
|||
} |
|||
|
|||
#endregion
|
|||
} |
|||
} |
|||
@ -0,0 +1,259 @@ |
|||
// <copyright file="UserCholesky.cs" company="Math.NET">
|
|||
// Math.NET Numerics, part of the Math.NET Project
|
|||
// http://numerics.mathdotnet.com
|
|||
// http://github.com/mathnet/mathnet-numerics
|
|||
// http://mathnetnumerics.codeplex.com
|
|||
//
|
|||
// Copyright (c) 2009-2010 Math.NET
|
|||
//
|
|||
// Permission is hereby granted, free of charge, to any person
|
|||
// obtaining a copy of this software and associated documentation
|
|||
// files (the "Software"), to deal in the Software without
|
|||
// restriction, including without limitation the rights to use,
|
|||
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|||
// copies of the Software, and to permit persons to whom the
|
|||
// Software is furnished to do so, subject to the following
|
|||
// conditions:
|
|||
//
|
|||
// The above copyright notice and this permission notice shall be
|
|||
// included in all copies or substantial portions of the Software.
|
|||
//
|
|||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|||
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|||
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|||
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|||
// OTHER DEALINGS IN THE SOFTWARE.
|
|||
// </copyright>
|
|||
|
|||
namespace MathNet.Numerics.LinearAlgebra.Single.Factorization |
|||
{ |
|||
using System; |
|||
using Generic; |
|||
using Generic.Factorization; |
|||
using Properties; |
|||
|
|||
/// <summary>
|
|||
/// <para>A class which encapsulates the functionality of a Cholesky factorization for user matrices.</para>
|
|||
/// <para>For a symmetric, positive definite matrix A, the Cholesky factorization
|
|||
/// is an lower triangular matrix L so that A = L*L'.</para>
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// The computation of the Cholesky factorization is done at construction time. If the matrix is not symmetric
|
|||
/// or positive definite, the constructor will throw an exception.
|
|||
/// </remarks>
|
|||
public class UserCholesky : Cholesky<float> |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="UserCholesky"/> class. This object will compute the
|
|||
/// Cholesky factorization when the constructor is called and cache it's factorization.
|
|||
/// </summary>
|
|||
/// <param name="matrix">The matrix to factor.</param>
|
|||
/// <exception cref="ArgumentNullException">If <paramref name="matrix"/> is <c>null</c>.</exception>
|
|||
/// <exception cref="ArgumentException">If <paramref name="matrix"/> is not a square matrix.</exception>
|
|||
/// <exception cref="ArgumentException">If <paramref name="matrix"/> is not positive definite.</exception>
|
|||
public UserCholesky(Matrix<float> 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.0f; |
|||
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, (float)Math.Sqrt(d)); |
|||
for (var k = j + 1; k < CholeskyFactor.RowCount; k++) |
|||
{ |
|||
CholeskyFactor.At(j, k, 0.0f); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Solves a system of linear equations, <b>AX = B</b>, with A Cholesky factorized.
|
|||
/// </summary>
|
|||
/// <param name="input">The right hand side <see cref="Matrix{T}"/>, <b>B</b>.</param>
|
|||
/// <param name="result">The left hand side <see cref="Matrix{T}"/>, <b>X</b>.</param>
|
|||
public override void Solve(Matrix<float> input, Matrix<float> 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;
|
|||
float 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)); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Solves a system of linear equations, <b>Ax = b</b>, with A Cholesky factorized.
|
|||
/// </summary>
|
|||
/// <param name="input">The right hand side vector, <b>b</b>.</param>
|
|||
/// <param name="result">The left hand side <see cref="Matrix{T}"/>, <b>x</b>.</param>
|
|||
public override void Solve(Vector<float> input, Vector<float> 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;
|
|||
float 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); |
|||
} |
|||
} |
|||
|
|||
#region Simple T Mathematics
|
|||
/// <summary>
|
|||
/// Add two values T+T
|
|||
/// </summary>
|
|||
/// <param name="val1">Left operand value</param>
|
|||
/// <param name="val2">Right operand value</param>
|
|||
/// <returns>Result of addition</returns>
|
|||
protected sealed override float AddT(float val1, float val2) |
|||
{ |
|||
return val1 + val2; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Multiply two values T*T
|
|||
/// </summary>
|
|||
/// <param name="val1">Left operand value</param>
|
|||
/// <param name="val2">Right operand value</param>
|
|||
/// <returns>Result of multiplication</returns>
|
|||
protected sealed override float MultiplyT(float val1, float val2) |
|||
{ |
|||
return val1 * val2; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns the natural (base e) logarithm of a specified number.
|
|||
/// </summary>
|
|||
/// <param name="val1"> A number whose logarithm is to be found</param>
|
|||
/// <returns>Natural (base e) logarithm </returns>
|
|||
protected sealed override float LogT(float val1) |
|||
{ |
|||
return (float)Math.Log(val1); |
|||
} |
|||
|
|||
#endregion
|
|||
} |
|||
} |
|||
@ -0,0 +1,316 @@ |
|||
// <copyright file="UserLU.cs" company="Math.NET">
|
|||
// Math.NET Numerics, part of the Math.NET Project
|
|||
// http://numerics.mathdotnet.com
|
|||
// http://github.com/mathnet/mathnet-numerics
|
|||
// http://mathnetnumerics.codeplex.com
|
|||
//
|
|||
// Copyright (c) 2009-2010 Math.NET
|
|||
//
|
|||
// Permission is hereby granted, free of charge, to any person
|
|||
// obtaining a copy of this software and associated documentation
|
|||
// files (the "Software"), to deal in the Software without
|
|||
// restriction, including without limitation the rights to use,
|
|||
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|||
// copies of the Software, and to permit persons to whom the
|
|||
// Software is furnished to do so, subject to the following
|
|||
// conditions:
|
|||
//
|
|||
// The above copyright notice and this permission notice shall be
|
|||
// included in all copies or substantial portions of the Software.
|
|||
//
|
|||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|||
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|||
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|||
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|||
// OTHER DEALINGS IN THE SOFTWARE.
|
|||
// </copyright>
|
|||
|
|||
namespace MathNet.Numerics.LinearAlgebra.Single.Factorization |
|||
{ |
|||
using System; |
|||
using Generic; |
|||
using Generic.Factorization; |
|||
using Properties; |
|||
|
|||
/// <summary>
|
|||
/// <para>A class which encapsulates the functionality of an LU factorization.</para>
|
|||
/// <para>For a matrix A, the LU factorization is a pair of lower triangular matrix L and
|
|||
/// upper triangular matrix U so that A = L*U.</para>
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// The computation of the LU factorization is done at construction time.
|
|||
/// </remarks>
|
|||
public class UserLU : LU<float> |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="UserLU"/> class. This object will compute the
|
|||
/// LU factorization when the constructor is called and cache it's factorization.
|
|||
/// </summary>
|
|||
/// <param name="matrix">The matrix to factor.</param>
|
|||
/// <exception cref="ArgumentNullException">If <paramref name="matrix"/> is <c>null</c>.</exception>
|
|||
/// <exception cref="ArgumentException">If <paramref name="matrix"/> is not a square matrix.</exception>
|
|||
public UserLU(Matrix<float> 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 float[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.0f; |
|||
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))); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Solves a system of linear equations, <c>AX = B</c>, with A LU factorized.
|
|||
/// </summary>
|
|||
/// <param name="input">The right hand side <see cref="Matrix{T}"/>, <c>B</c>.</param>
|
|||
/// <param name="result">The left hand side <see cref="Matrix{T}"/>, <c>X</c>.</param>
|
|||
public override void Solve(Matrix<float> input, Matrix<float> result) |
|||
{ |
|||
// Check for proper arguments.
|
|||
if (input == null) |
|||
{ |
|||
throw new ArgumentNullException("input"); |
|||
} |
|||
|
|||
if (result == null) |
|||
{ |
|||
throw new ArgumentNullException("result"); |
|||
} |
|||
|
|||
// Check for proper dimensions.
|
|||
if (result.RowCount != input.RowCount) |
|||
{ |
|||
throw new ArgumentException(Resources.ArgumentMatrixSameRowDimension); |
|||
} |
|||
|
|||
if (result.ColumnCount != input.ColumnCount) |
|||
{ |
|||
throw new ArgumentException(Resources.ArgumentMatrixSameColumnDimension); |
|||
} |
|||
|
|||
if (input.RowCount != Factors.RowCount) |
|||
{ |
|||
throw new ArgumentException(Resources.ArgumentMatrixDimensions); |
|||
} |
|||
|
|||
// Copy the contents of input to result.
|
|||
input.CopyTo(result); |
|||
for (var i = 0; i < Pivots.Length; i++) |
|||
{ |
|||
if (Pivots[i] == i) |
|||
{ |
|||
continue; |
|||
} |
|||
|
|||
var p = Pivots[i]; |
|||
for (var j = 0; j < result.ColumnCount; j++) |
|||
{ |
|||
var temp = result.At(p, j); |
|||
result.At(p, j, result.At(i, j)); |
|||
result.At(i, j, temp); |
|||
} |
|||
} |
|||
|
|||
var order = Factors.RowCount; |
|||
|
|||
// Solve L*Y = P*B
|
|||
for (var k = 0; k < order; k++) |
|||
{ |
|||
for (var i = k + 1; i < order; i++) |
|||
{ |
|||
for (var j = 0; j < result.ColumnCount; j++) |
|||
{ |
|||
var temp = result.At(k, j) * Factors.At(i, k); |
|||
result.At(i, j, result.At(i, j) - temp); |
|||
} |
|||
} |
|||
} |
|||
|
|||
// Solve U*X = Y;
|
|||
for (var k = order - 1; k >= 0; k--) |
|||
{ |
|||
for (var j = 0; j < result.ColumnCount; j++) |
|||
{ |
|||
result.At(k, j, (result.At(k, j) / Factors.At(k, k))); |
|||
} |
|||
|
|||
for (var i = 0; i < k; i++) |
|||
{ |
|||
for (var j = 0; j < result.ColumnCount; j++) |
|||
{ |
|||
var temp = result.At(k, j) * Factors.At(i, k); |
|||
result.At(i, j, result.At(i, j) - temp); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Solves a system of linear equations, <c>Ax = b</c>, with A LU factorized.
|
|||
/// </summary>
|
|||
/// <param name="input">The right hand side vector, <c>b</c>.</param>
|
|||
/// <param name="result">The left hand side <see cref="Matrix{T}"/>, <c>x</c>.</param>
|
|||
public override void Solve(Vector<float> input, Vector<float> result) |
|||
{ |
|||
// Check for proper arguments.
|
|||
if (input == null) |
|||
{ |
|||
throw new ArgumentNullException("input"); |
|||
} |
|||
|
|||
if (result == null) |
|||
{ |
|||
throw new ArgumentNullException("result"); |
|||
} |
|||
|
|||
// Check for proper dimensions.
|
|||
if (input.Count != result.Count) |
|||
{ |
|||
throw new ArgumentException(Resources.ArgumentVectorsSameLength); |
|||
} |
|||
|
|||
if (input.Count != Factors.RowCount) |
|||
{ |
|||
throw new ArgumentException(Resources.ArgumentMatrixDimensions); |
|||
} |
|||
|
|||
// Copy the contents of input to result.
|
|||
input.CopyTo(result); |
|||
for (var i = 0; i < Pivots.Length; i++) |
|||
{ |
|||
if (Pivots[i] == i) |
|||
{ |
|||
continue; |
|||
} |
|||
|
|||
var p = Pivots[i]; |
|||
var temp = result[p]; |
|||
result[p] = result[i]; |
|||
result[i] = temp; |
|||
} |
|||
|
|||
var order = Factors.RowCount; |
|||
|
|||
// Solve L*Y = P*B
|
|||
for (var k = 0; k < order; k++) |
|||
{ |
|||
for (var i = k + 1; i < order; i++) |
|||
{ |
|||
result[i] -= result[k] * Factors.At(i, k); |
|||
} |
|||
} |
|||
|
|||
// Solve U*X = Y;
|
|||
for (var k = order - 1; k >= 0; k--) |
|||
{ |
|||
result[k] /= Factors.At(k, k); |
|||
for (var i = 0; i < k; i++) |
|||
{ |
|||
result[i] -= result[k] * Factors.At(i, k); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns the inverse of this matrix. The inverse is calculated using LU decomposition.
|
|||
/// </summary>
|
|||
/// <returns>The inverse of this matrix.</returns>
|
|||
public override Matrix<float> Inverse() |
|||
{ |
|||
var order = Factors.RowCount; |
|||
var inverse = Factors.CreateMatrix(order, order); |
|||
for (var i = 0; i < order; i++) |
|||
{ |
|||
inverse.At(i, i, 1.0f); |
|||
} |
|||
|
|||
return Solve(inverse); |
|||
} |
|||
|
|||
#region Simple arithmetic of type T
|
|||
|
|||
/// <summary>
|
|||
/// Multiply two values T*T
|
|||
/// </summary>
|
|||
/// <param name="val1">Left operand value</param>
|
|||
/// <param name="val2">Right operand value</param>
|
|||
/// <returns>Result of multiplication</returns>
|
|||
protected sealed override float MultiplyT(float val1, float val2) |
|||
{ |
|||
return val1 * val2; |
|||
} |
|||
#endregion
|
|||
} |
|||
} |
|||
@ -0,0 +1,357 @@ |
|||
// <copyright file="UserQR.cs" company="Math.NET">
|
|||
// Math.NET Numerics, part of the Math.NET Project
|
|||
// http://numerics.mathdotnet.com
|
|||
// http://github.com/mathnet/mathnet-numerics
|
|||
// http://mathnetnumerics.codeplex.com
|
|||
//
|
|||
// Copyright (c) 2009-2010 Math.NET
|
|||
//
|
|||
// Permission is hereby granted, free of charge, to any person
|
|||
// obtaining a copy of this software and associated documentation
|
|||
// files (the "Software"), to deal in the Software without
|
|||
// restriction, including without limitation the rights to use,
|
|||
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|||
// copies of the Software, and to permit persons to whom the
|
|||
// Software is furnished to do so, subject to the following
|
|||
// conditions:
|
|||
//
|
|||
// The above copyright notice and this permission notice shall be
|
|||
// included in all copies or substantial portions of the Software.
|
|||
//
|
|||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|||
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|||
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|||
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|||
// OTHER DEALINGS IN THE SOFTWARE.
|
|||
// </copyright>
|
|||
|
|||
namespace MathNet.Numerics.LinearAlgebra.Single.Factorization |
|||
{ |
|||
using System; |
|||
using System.Linq; |
|||
using Generic; |
|||
using Generic.Factorization; |
|||
using Properties; |
|||
|
|||
/// <summary>
|
|||
/// <para>A class which encapsulates the functionality of the QR decomposition.</para>
|
|||
/// <para>Any real square matrix A may be decomposed as A = QR where Q is an orthogonal matrix
|
|||
/// (its columns are orthogonal unit vectors meaning QTQ = I) and R is an upper triangular matrix
|
|||
/// (also called right triangular matrix).</para>
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// The computation of the QR decomposition is done at construction time by Householder transformation.
|
|||
/// </remarks>
|
|||
public class UserQR : QR<float> |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="UserQR"/> class. This object will compute the
|
|||
/// QR factorization when the constructor is called and cache it's factorization.
|
|||
/// </summary>
|
|||
/// <param name="matrix">The matrix to factor.</param>
|
|||
/// <exception cref="ArgumentNullException">If <paramref name="matrix"/> is <c>null</c>.</exception>
|
|||
public UserQR(Matrix<float> 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.0f); |
|||
} |
|||
|
|||
var minmn = Math.Min(matrix.RowCount, matrix.ColumnCount); |
|||
var u = new float[minmn][]; |
|||
for (var i = 0; i < minmn; i++) |
|||
{ |
|||
u[i] = GenerateColumn(MatrixR, i, matrix.RowCount - 1, i); |
|||
ComputeQR(u[i], MatrixR, i, matrix.RowCount - 1, i + 1, matrix.ColumnCount - 1); |
|||
} |
|||
|
|||
for (var i = minmn - 1; i >= 0; i--) |
|||
{ |
|||
ComputeQR(u[i], MatrixQ, i, matrix.RowCount - 1, i, matrix.RowCount - 1); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Generate column from initial matrix to work array
|
|||
/// </summary>
|
|||
/// <param name="a">Initial matrix</param>
|
|||
/// <param name="rowStart">The firts row</param>
|
|||
/// <param name="rowEnd">The last row</param>
|
|||
/// <param name="column">Column index</param>
|
|||
/// <returns>Generated vector</returns>
|
|||
private static float[] GenerateColumn(Matrix<float> a, int rowStart, int rowEnd, int column) |
|||
{ |
|||
var ru = rowEnd - rowStart + 1; |
|||
var u = new float[ru]; |
|||
|
|||
for (var i = rowStart; i <= rowEnd; i++) |
|||
{ |
|||
u[i - rowStart] = a.At(i, rowStart); |
|||
a.At(i, rowStart, 0.0f); |
|||
} |
|||
|
|||
var norm = u.Sum(t => t * t); |
|||
norm = (float)Math.Sqrt(norm); |
|||
|
|||
if (rowStart == rowEnd || norm == 0) |
|||
{ |
|||
a.At(rowStart, column, -u[0]); |
|||
u[0] = (float)Math.Sqrt(2.0); |
|||
return u; |
|||
} |
|||
|
|||
var scale = 1.0f / norm; |
|||
if (u[0] < 0.0) |
|||
{ |
|||
scale *= -1.0f; |
|||
} |
|||
|
|||
a.At(rowStart, column, -1.0f / scale); |
|||
|
|||
for (var i = 0; i < ru; i++) |
|||
{ |
|||
u[i] *= scale; |
|||
} |
|||
|
|||
u[0] += 1.0f; |
|||
var s = (float)Math.Sqrt(1.0 / u[0]); |
|||
|
|||
for (var i = 0; i < ru; i++) |
|||
{ |
|||
u[i] *= s; |
|||
} |
|||
|
|||
return u; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Perform calculation of Q or R
|
|||
/// </summary>
|
|||
/// <param name="u">Work array</param>
|
|||
/// <param name="a">Q or R matrices</param>
|
|||
/// <param name="rowStart">The first row</param>
|
|||
/// <param name="rowEnd">The last row</param>
|
|||
/// <param name="columnStart">The first column</param>
|
|||
/// <param name="columnEnd">The last column</param>
|
|||
private static void ComputeQR(float[] u, Matrix<float> a, int rowStart, int rowEnd, int columnStart, int columnEnd) |
|||
{ |
|||
if (rowEnd < rowStart || columnEnd < columnStart) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
var v = new float[columnEnd - columnStart + 1]; |
|||
for (var j = columnStart; j <= columnEnd; j++) |
|||
{ |
|||
v[j - columnStart] = 0.0f; |
|||
} |
|||
|
|||
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])); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Solves a system of linear equations, <b>AX = B</b>, with A QR factorized.
|
|||
/// </summary>
|
|||
/// <param name="input">The right hand side <see cref="Matrix{T}"/>, <b>B</b>.</param>
|
|||
/// <param name="result">The left hand side <see cref="Matrix{T}"/>, <b>X</b>.</param>
|
|||
public override void Solve(Matrix<float> input, Matrix<float> result) |
|||
{ |
|||
// Check for proper arguments.
|
|||
if (input == null) |
|||
{ |
|||
throw new ArgumentNullException("input"); |
|||
} |
|||
|
|||
if (result == null) |
|||
{ |
|||
throw new ArgumentNullException("result"); |
|||
} |
|||
|
|||
// The solution X should have the same number of columns as B
|
|||
if (input.ColumnCount != result.ColumnCount) |
|||
{ |
|||
throw new ArgumentException(Resources.ArgumentMatrixSameColumnDimension); |
|||
} |
|||
|
|||
// The dimension compatibility conditions for X = A\B require the two matrices A and B to have the same number of rows
|
|||
if (MatrixR.RowCount != input.RowCount) |
|||
{ |
|||
throw new ArgumentException(Resources.ArgumentMatrixSameRowDimension); |
|||
} |
|||
|
|||
// The solution X row dimension is equal to the column dimension of A
|
|||
if (MatrixR.ColumnCount != result.RowCount) |
|||
{ |
|||
throw new ArgumentException(Resources.ArgumentMatrixSameColumnDimension); |
|||
} |
|||
|
|||
var inputCopy = input.Clone(); |
|||
|
|||
// Compute Y = transpose(Q)*B
|
|||
var column = new float[MatrixR.RowCount]; |
|||
for (var j = 0; j < input.ColumnCount; j++) |
|||
{ |
|||
for (var k = 0; k < MatrixR.RowCount; k++) |
|||
{ |
|||
column[k] = inputCopy.At(k, j); |
|||
} |
|||
|
|||
for (var i = 0; i < MatrixR.RowCount; i++) |
|||
{ |
|||
float 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 < input.ColumnCount; j++) |
|||
{ |
|||
inputCopy.At(k, j, inputCopy.At(k, j) / MatrixR.At(k, k)); |
|||
} |
|||
|
|||
for (var i = 0; i < k; i++) |
|||
{ |
|||
for (var j = 0; j < input.ColumnCount; j++) |
|||
{ |
|||
inputCopy.At(i, j, inputCopy.At(i, j) - (inputCopy.At(k, j) * MatrixR.At(i, k))); |
|||
} |
|||
} |
|||
} |
|||
|
|||
for (var i = 0; i < MatrixR.ColumnCount; i++) |
|||
{ |
|||
for (var j = 0; j < inputCopy.ColumnCount; j++) |
|||
{ |
|||
result.At(i, j, inputCopy.At(i, j)); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Solves a system of linear equations, <b>Ax = b</b>, with A QR factorized.
|
|||
/// </summary>
|
|||
/// <param name="input">The right hand side vector, <b>b</b>.</param>
|
|||
/// <param name="result">The left hand side <see cref="Matrix{T}"/>, <b>x</b>.</param>
|
|||
public override void Solve(Vector<float> input, Vector<float> 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 float[MatrixR.RowCount]; |
|||
for (var k = 0; k < MatrixR.RowCount; k++) |
|||
{ |
|||
column[k] = inputCopy[k]; |
|||
} |
|||
|
|||
for (var i = 0; i < MatrixR.RowCount; i++) |
|||
{ |
|||
float 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]; |
|||
} |
|||
} |
|||
|
|||
#region Simple arithmetic of type T
|
|||
|
|||
/// <summary>
|
|||
/// Multiply two values T*T
|
|||
/// </summary>
|
|||
/// <param name="val1">Left operand value</param>
|
|||
/// <param name="val2">Right operand value</param>
|
|||
/// <returns>Result of multiplication</returns>
|
|||
protected sealed override float MultiplyT(float val1, float val2) |
|||
{ |
|||
return val1 * val2; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns the absolute value of a specified number.
|
|||
/// </summary>
|
|||
/// <param name="val1"> A number whose absolute is to be found</param>
|
|||
/// <returns>Absolute value </returns>
|
|||
protected sealed override double AbsoluteT(float val1) |
|||
{ |
|||
return Math.Abs(val1); |
|||
} |
|||
#endregion
|
|||
} |
|||
} |
|||
@ -0,0 +1,950 @@ |
|||
// <copyright file="UserSvd.cs" company="Math.NET">
|
|||
// Math.NET Numerics, part of the Math.NET Project
|
|||
// http://numerics.mathdotnet.com
|
|||
// http://github.com/mathnet/mathnet-numerics
|
|||
// http://mathnetnumerics.codeplex.com
|
|||
//
|
|||
// Copyright (c) 2009-2010 Math.NET
|
|||
//
|
|||
// Permission is hereby granted, free of charge, to any person
|
|||
// obtaining a copy of this software and associated documentation
|
|||
// files (the "Software"), to deal in the Software without
|
|||
// restriction, including without limitation the rights to use,
|
|||
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|||
// copies of the Software, and to permit persons to whom the
|
|||
// Software is furnished to do so, subject to the following
|
|||
// conditions:
|
|||
//
|
|||
// The above copyright notice and this permission notice shall be
|
|||
// included in all copies or substantial portions of the Software.
|
|||
//
|
|||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|||
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|||
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|||
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|||
// OTHER DEALINGS IN THE SOFTWARE.
|
|||
// </copyright>
|
|||
namespace MathNet.Numerics.LinearAlgebra.Single.Factorization |
|||
{ |
|||
using System; |
|||
using Generic; |
|||
using Generic.Factorization; |
|||
using Properties; |
|||
|
|||
/// <summary>
|
|||
/// <para>A class which encapsulates the functionality of the singular value decomposition (SVD) for <see cref="Matrix{T}"/>.</para>
|
|||
/// <para>Suppose M is an m-by-n matrix whose entries are real numbers.
|
|||
/// Then there exists a factorization of the form M = UΣVT where:
|
|||
/// - U is an m-by-m unitary matrix;
|
|||
/// - Σ is m-by-n diagonal matrix with nonnegative real numbers on the diagonal;
|
|||
/// - VT denotes transpose of V, an n-by-n unitary matrix;
|
|||
/// Such a factorization is called a singular-value decomposition of M. A common convention is to order the diagonal
|
|||
/// entries Σ(i,i) in descending order. In this case, the diagonal matrix Σ is uniquely determined
|
|||
/// by M (though the matrices U and V are not). The diagonal entries of Σ are known as the singular values of M.</para>
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// The computation of the singular value decomposition is done at construction time.
|
|||
/// </remarks>
|
|||
public class UserSvd : Svd<float> |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="UserSvd"/> class. This object will compute the
|
|||
/// the singular value decomposition when the constructor is called and cache it's decomposition.
|
|||
/// </summary>
|
|||
/// <param name="matrix">The matrix to factor.</param>
|
|||
/// <param name="computeVectors">Compute the singular U and VT vectors or not.</param>
|
|||
/// <exception cref="ArgumentNullException">If <paramref name="matrix"/> is <b>null</b>.</exception>
|
|||
/// <exception cref="ArgumentException">If SVD algorithm failed to converge with matrix <paramref name="matrix"/>.</exception>
|
|||
public UserSvd(Matrix<float> 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 float[matrixCopy.ColumnCount]; |
|||
var work = new float[matrixCopy.RowCount]; |
|||
|
|||
int i, j; |
|||
int l, lp1; |
|||
var cs = 0.0f; |
|||
var sn = 0.0f; |
|||
float 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.0f / VectorS[l]); |
|||
matrixCopy.At(l, l, (1.0f + 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.0f / e[l]); |
|||
e[lp1] = 1.0f + 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.0f; |
|||
} |
|||
|
|||
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.0f; |
|||
} |
|||
|
|||
if (nrtp1 < m) |
|||
{ |
|||
e[nrtp1 - 1] = matrixCopy.At((nrtp1 - 1), (m - 1)); |
|||
} |
|||
|
|||
e[m - 1] = 0.0f; |
|||
|
|||
// 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.0f); |
|||
} |
|||
|
|||
MatrixU.At(j, j, 1.0f); |
|||
} |
|||
|
|||
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.0f); |
|||
MatrixU.At(l, l, 1.0f + MatrixU.At(l, l)); |
|||
for (i = 0; i < l; i++) |
|||
{ |
|||
MatrixU.At(i, l, 0.0f); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
for (i = 0; i < matrixCopy.RowCount; i++) |
|||
{ |
|||
MatrixU.At(i, l, 0.0f); |
|||
} |
|||
|
|||
MatrixU.At(l, l, 1.0f); |
|||
} |
|||
} |
|||
} |
|||
|
|||
// 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.0f); |
|||
} |
|||
|
|||
MatrixVT.At(l, l, 1.0f); |
|||
} |
|||
} |
|||
|
|||
// Transform s and e so that they are float .
|
|||
for (i = 0; i < m; i++) |
|||
{ |
|||
float 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).
|
|||
float ztest; |
|||
float 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, 7)) |
|||
{ |
|||
e[l] = 0.0f; |
|||
break; |
|||
} |
|||
} |
|||
|
|||
int kase; |
|||
if (l == m - 2) |
|||
{ |
|||
kase = 4; |
|||
} |
|||
else |
|||
{ |
|||
int ls; |
|||
for (ls = m - 1; ls > l; ls--) |
|||
{ |
|||
test = 0.0f; |
|||
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, 7)) |
|||
{ |
|||
VectorS[ls] = 0.0f; |
|||
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; |
|||
float f; |
|||
switch (kase) |
|||
{ |
|||
// Deflate negligible VectorS[m].
|
|||
case 1: |
|||
f = e[m - 2]; |
|||
e[m - 2] = 0.0f; |
|||
float 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.0f; |
|||
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.0f; |
|||
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.0f; |
|||
var c = (sm * emm1) * (sm * emm1); |
|||
var shift = 0.0f; |
|||
if (b != 0.0 || c != 0.0) |
|||
{ |
|||
shift = (float) 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.0f); |
|||
} |
|||
} |
|||
|
|||
// 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; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Calculates absolute value of <paramref name="z1"/> multiplied on signum function of <paramref name="z2"/>
|
|||
/// </summary>
|
|||
/// <param name="z1">Double value z1</param>
|
|||
/// <param name="z2">Double value z2</param>
|
|||
/// <returns>Result multiplication of signum function and absolute value</returns>
|
|||
private static float Dsign(float z1, float z2) |
|||
{ |
|||
return Math.Abs(z1) * (z2 / Math.Abs(z2)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Swap column <paramref name="columnA"/> and <paramref name="columnB"/>
|
|||
/// </summary>
|
|||
/// <param name="a">Source matrix</param>
|
|||
/// <param name="rowCount">The number of rows in <paramref name="a"/></param>
|
|||
/// <param name="columnA">Column A index to swap</param>
|
|||
/// <param name="columnB">Column B index to swap</param>
|
|||
private static void Dswap(Matrix<float> a, int rowCount, int columnA, int columnB) |
|||
{ |
|||
for (var i = 0; i < rowCount; i++) |
|||
{ |
|||
var z = a.At(i, columnA); |
|||
a.At(i, columnA, a.At(i, columnB)); |
|||
a.At(i, columnB, z); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Scale column <paramref name="column"/> by <paramref name="z"/> starting from row <paramref name="rowStart"/>
|
|||
/// </summary>
|
|||
/// <param name="a">Source matrix</param>
|
|||
/// <param name="rowCount">The number of rows in <paramref name="a"/> </param>
|
|||
/// <param name="column">Column to scale</param>
|
|||
/// <param name="rowStart">Row to scale from</param>
|
|||
/// <param name="z">Scale value</param>
|
|||
private static void DscalColumn(Matrix<float> a, int rowCount, int column, int rowStart, float z) |
|||
{ |
|||
for (var i = rowStart; i < rowCount; i++) |
|||
{ |
|||
a.At(i, column, a.At(i, column) * z); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Scale vector <paramref name="a"/> by <paramref name="z"/> starting from index <paramref name="start"/>
|
|||
/// </summary>
|
|||
/// <param name="a">Source vector</param>
|
|||
/// <param name="start">Row to scale from</param>
|
|||
/// <param name="z">Scale value</param>
|
|||
private static void DscalVector(float[] a, int start, float z) |
|||
{ |
|||
for (var i = start; i < a.Length; i++) |
|||
{ |
|||
a[i] = a[i] * z; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Given the Cartesian coordinates (da, db) of a point p, these fucntion return the parameters da, db, c, and s
|
|||
/// associated with the Givens rotation that zeros the y-coordinate of the point.
|
|||
/// </summary>
|
|||
/// <param name="da">Provides the x-coordinate of the point p. On exit contains the parameter r associated with the Givens rotation</param>
|
|||
/// <param name="db">Provides the y-coordinate of the point p. On exit contains the parameter z associated with the Givens rotation</param>
|
|||
/// <param name="c">Contains the parameter c associated with the Givens rotation</param>
|
|||
/// <param name="s">Contains the parameter s associated with the Givens rotation</param>
|
|||
/// <remarks>This is equivalent to the DROTG LAPACK routine.</remarks>
|
|||
private static void Drotg(ref float da, ref float db, ref float c, ref float s) |
|||
{ |
|||
float 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.0f; |
|||
s = 0.0f; |
|||
r = 0.0f; |
|||
z = 0.0f; |
|||
} |
|||
else |
|||
{ |
|||
var sda = da / scale; |
|||
var sdb = db / scale; |
|||
r = scale * (float)Math.Sqrt((sda * sda) + (sdb * sdb)); |
|||
if (roe < 0.0) |
|||
{ |
|||
r = -r; |
|||
} |
|||
|
|||
c = da / r; |
|||
s = db / r; |
|||
z = 1.0f; |
|||
if (absda > absdb) |
|||
{ |
|||
z = s; |
|||
} |
|||
|
|||
if (absdb >= absda && c != 0.0) |
|||
{ |
|||
z = 1.0f / c; |
|||
} |
|||
} |
|||
|
|||
da = r; |
|||
db = z; |
|||
} |
|||
|
|||
/// <summary>dded
|
|||
/// Calculate Norm 2 of the column <paramref name="column"/> in matrix <paramref name="a"/> starting from row <paramref name="rowStart"/>
|
|||
/// </summary>
|
|||
/// <param name="a">Source matrix</param>
|
|||
/// <param name="rowCount">The number of rows in <paramref name="a"/></param>
|
|||
/// <param name="column">Column index</param>
|
|||
/// <param name="rowStart">Start row index</param>
|
|||
/// <returns>Norm2 (Euclidean norm) of trhe column</returns>
|
|||
private static float Dnrm2Column(Matrix<float> a, int rowCount, int column, int rowStart) |
|||
{ |
|||
float s = 0; |
|||
for (var i = rowStart; i < rowCount; i++) |
|||
{ |
|||
s += a.At(i, column) * a.At(i, column); |
|||
} |
|||
|
|||
return (float)Math.Sqrt(s); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Calculate Norm 2 of the vector <paramref name="a"/> starting from index <paramref name="rowStart"/>
|
|||
/// </summary>
|
|||
/// <param name="a">Source vector</param>
|
|||
/// <param name="rowStart">Start index</param>
|
|||
/// <returns>Norm2 (Euclidean norm) of the vector</returns>
|
|||
private static float Dnrm2Vector(float[] a, int rowStart) |
|||
{ |
|||
float s = 0; |
|||
for (var i = rowStart; i < a.Length; i++) |
|||
{ |
|||
s += a[i] * a[i]; |
|||
} |
|||
|
|||
return (float)Math.Sqrt(s); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Calculate dot product of <paramref name="columnA"/> and <paramref name="columnB"/>
|
|||
/// </summary>
|
|||
/// <param name="a">Source matrix</param>
|
|||
/// <param name="rowCount">The number of rows in <paramref name="a"/></param>
|
|||
/// <param name="columnA">Index of column A</param>
|
|||
/// <param name="columnB">Index of column B</param>
|
|||
/// <param name="rowStart">Starting row index</param>
|
|||
/// <returns>Dot product value</returns>
|
|||
private static float Ddot(Matrix<float> a, int rowCount, int columnA, int columnB, int rowStart) |
|||
{ |
|||
var z = 0.0f; |
|||
for (var i = rowStart; i < rowCount; i++) |
|||
{ |
|||
z += a.At(i, columnB) * a.At(i, columnA); |
|||
} |
|||
|
|||
return z; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Performs rotation of points in the plane. Given two vectors x <paramref name="columnA"/> and y <paramref name="columnB"/>,
|
|||
/// each vector element of these vectors is replaced as follows: x(i) = c*x(i) + s*y(i); y(i) = c*y(i) - s*x(i)
|
|||
/// </summary>
|
|||
/// <param name="a">Source matrix</param>
|
|||
/// <param name="rowCount">The number of rows in <paramref name="a"/></param>
|
|||
/// <param name="columnA">Index of column A</param>
|
|||
/// <param name="columnB">Index of column B</param>
|
|||
/// <param name="c">Scalar "c" value</param>
|
|||
/// <param name="s">Scalar "s" value</param>
|
|||
private static void Drot(Matrix<float> a, int rowCount, int columnA, int columnB, float c, float s) |
|||
{ |
|||
for (var i = 0; i < rowCount; i++) |
|||
{ |
|||
var z = (c * a.At(i, columnA)) + (s * a.At(i, columnB)); |
|||
var tmp = (c * a.At(i, columnB)) - (s * a.At(i, columnA)); |
|||
a.At(i, columnB, tmp); |
|||
a.At(i, columnA, z); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Solves a system of linear equations, <b>AX = B</b>, with A SVD factorized.
|
|||
/// </summary>
|
|||
/// <param name="input">The right hand side <see cref="Matrix{T}"/>, <b>B</b>.</param>
|
|||
/// <param name="result">The left hand side <see cref="Matrix{T}"/>, <b>X</b>.</param>
|
|||
public override void Solve(Matrix<float> input, Matrix<float> 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 float[MatrixVT.ColumnCount]; |
|||
|
|||
for (var k = 0; k < bn; k++) |
|||
{ |
|||
for (var j = 0; j < MatrixVT.ColumnCount; j++) |
|||
{ |
|||
float 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++) |
|||
{ |
|||
float value = 0; |
|||
for (var i = 0; i < MatrixVT.ColumnCount; i++) |
|||
{ |
|||
value += MatrixVT.At(i, j) * tmp[i]; |
|||
} |
|||
|
|||
result[j, k] = value; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Solves a system of linear equations, <b>Ax = b</b>, with A SVD factorized.
|
|||
/// </summary>
|
|||
/// <param name="input">The right hand side vector, <b>b</b>.</param>
|
|||
/// <param name="result">The left hand side <see cref="Matrix{T}"/>, <b>x</b>.</param>
|
|||
public override void Solve(Vector<float> input, Vector<float> 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 float[MatrixVT.ColumnCount]; |
|||
float 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; |
|||
} |
|||
} |
|||
|
|||
#region Simple arithmetic of type T
|
|||
|
|||
/// <summary>
|
|||
/// Multiply two values T*T
|
|||
/// </summary>
|
|||
/// <param name="val1">Left operand value</param>
|
|||
/// <param name="val2">Right operand value</param>
|
|||
/// <returns>Result of multiplication</returns>
|
|||
protected sealed override float MultiplyT(float val1, float val2) |
|||
{ |
|||
return val1 * val2; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns the absolute value of a specified number.
|
|||
/// </summary>
|
|||
/// <param name="val1"> A number whose absolute is to be found</param>
|
|||
/// <returns>Absolute value </returns>
|
|||
protected sealed override double AbsoluteT(float val1) |
|||
{ |
|||
return Math.Abs(val1); |
|||
} |
|||
|
|||
#endregion
|
|||
} |
|||
} |
|||
@ -0,0 +1,197 @@ |
|||
// <copyright file="DelimitedReader.cs" company="Math.NET">
|
|||
// Math.NET Numerics, part of the Math.NET Project
|
|||
// http://numerics.mathdotnet.com
|
|||
// http://github.com/mathnet/mathnet-numerics
|
|||
// http://mathnetnumerics.codeplex.com
|
|||
// Copyright (c) 2009-2010 Math.NET
|
|||
// Permission is hereby granted, free of charge, to any person
|
|||
// obtaining a copy of this software and associated documentation
|
|||
// files (the "Software"), to deal in the Software without
|
|||
// restriction, including without limitation the rights to use,
|
|||
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|||
// copies of the Software, and to permit persons to whom the
|
|||
// Software is furnished to do so, subject to the following
|
|||
// conditions:
|
|||
// The above copyright notice and this permission notice shall be
|
|||
// included in all copies or substantial portions of the Software.
|
|||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|||
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|||
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|||
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|||
// OTHER DEALINGS IN THE SOFTWARE.
|
|||
// </copyright>
|
|||
|
|||
namespace MathNet.Numerics.LinearAlgebra.Single.IO |
|||
{ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Globalization; |
|||
using System.IO; |
|||
using System.Reflection; |
|||
using System.Text.RegularExpressions; |
|||
using Generic; |
|||
|
|||
/// <summary>
|
|||
/// Creates a <see cref="Matrix{T}"/> from a delimited text file. If the user does not
|
|||
/// specify a delimiter, then any whitespace is used.
|
|||
/// </summary>
|
|||
/// <typeparam name="TMatrix">The type of the matrix to return.</typeparam>
|
|||
public class DelimitedReader<TMatrix> : MatrixReader<TMatrix> |
|||
where TMatrix : Matrix<float> |
|||
{ |
|||
/// <summary>
|
|||
/// Constructor to create matrix instance.
|
|||
/// </summary>
|
|||
private static readonly ConstructorInfo Constructor = typeof(TMatrix).GetConstructor(new[] { typeof(int), typeof(int) }); |
|||
|
|||
/// <summary>
|
|||
/// Whitespace regular expression.
|
|||
/// </summary>
|
|||
private static readonly Regex WhiteSpace = new Regex(@"[\s]+"); |
|||
|
|||
/// <summary>
|
|||
/// The delimiter to use.
|
|||
/// </summary>
|
|||
private readonly Regex _delimiter; |
|||
|
|||
/// <summary>
|
|||
/// The <see cref="CultureInfo"/> to use.
|
|||
/// </summary>
|
|||
private CultureInfo _cultureInfo = CultureInfo.CurrentCulture; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="DelimitedReader{TMatrix}"/> class using
|
|||
/// any whitespace as the delimiter.
|
|||
/// </summary>
|
|||
public DelimitedReader() |
|||
{ |
|||
_delimiter = WhiteSpace; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="DelimitedReader{TMatrix}"/> class.
|
|||
/// </summary>
|
|||
/// <param name="delimiter">The delimiter to use.</param>
|
|||
public DelimitedReader(char delimiter) |
|||
{ |
|||
_delimiter = new Regex("[" + new string(delimiter, 1) + "]+"); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="DelimitedReader{TMatrix}"/> class.
|
|||
/// </summary>
|
|||
/// <param name="delimiter">
|
|||
/// The delimiter to use.
|
|||
/// </param>
|
|||
/// <exception cref="ArgumentNullException">
|
|||
/// If <paramref name="delimiter"/> is <see langword="null"/>.
|
|||
/// </exception>
|
|||
public DelimitedReader(string delimiter) |
|||
{ |
|||
if (delimiter == null) |
|||
{ |
|||
throw new ArgumentNullException("delimiter"); |
|||
} |
|||
|
|||
_delimiter = new Regex("[" + delimiter + "]+"); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the <see cref="CultureInfo"/> to use when parsing the numbers.
|
|||
/// </summary>
|
|||
/// <value>The culture info.</value>
|
|||
/// <remarks>Defaults to <c>CultureInfo.CurrentCulture</c>.</remarks>
|
|||
public CultureInfo CultureInfo |
|||
{ |
|||
get |
|||
{ |
|||
return _cultureInfo; |
|||
} |
|||
|
|||
set |
|||
{ |
|||
if (value != null) |
|||
{ |
|||
_cultureInfo = value; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets a value indicating whether the files has a header row.
|
|||
/// </summary>
|
|||
/// <value>
|
|||
/// <c>true</c> if this instance has a header row; otherwise, <c>false</c>.
|
|||
/// </value>
|
|||
/// <remarks>Defaults to <see langword="false"/>.</remarks>
|
|||
public bool HasHeaderRow |
|||
{ |
|||
get; |
|||
set; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Performs the actual reading.
|
|||
/// </summary>
|
|||
/// <param name="stream">The <see cref="Stream"/> to read the matrix from.</param>
|
|||
/// <returns>
|
|||
/// A matrix containing the data from the <see cref="Stream"/>. <see langword="null"/> is returned if the <see cref="Stream"/> is empty.
|
|||
/// </returns>
|
|||
protected override TMatrix DoReadMatrix(Stream stream) |
|||
{ |
|||
var data = new List<string[]>(); |
|||
|
|||
// max is used to supports files like:
|
|||
// 1,2
|
|||
// 3,4,5,6
|
|||
// 7
|
|||
// this creates a 3x4 matrix:
|
|||
// 1, 2, 0 ,0
|
|||
// 3, 4, 5, 6
|
|||
// 7, 0, 0, 0
|
|||
var max = -1; |
|||
var reader = new StreamReader(stream); |
|||
var line = reader.ReadLine(); |
|||
if (HasHeaderRow) |
|||
{ |
|||
line = reader.ReadLine(); |
|||
} |
|||
|
|||
while (line != null) |
|||
{ |
|||
line = line.Trim(); |
|||
if (line.Length > 0) |
|||
{ |
|||
var row = _delimiter.Split(line); |
|||
max = Math.Max(max, row.Length); |
|||
data.Add(row); |
|||
} |
|||
|
|||
line = reader.ReadLine(); |
|||
} |
|||
|
|||
var ret = (TMatrix)Constructor.Invoke(new object[] { data.Count, max }); |
|||
|
|||
if (data.Count != 0) |
|||
{ |
|||
for (var i = 0; i < data.Count; i++) |
|||
{ |
|||
var row = data[i]; |
|||
for (var j = 0; j < row.Length; j++) |
|||
{ |
|||
// strip off quotes
|
|||
var value = row[j].Replace("'", string.Empty).Replace("\"", string.Empty); |
|||
ret[i, j] = float.Parse(value, NumberStyles.Any, _cultureInfo); |
|||
} |
|||
} |
|||
} |
|||
|
|||
reader.Close(); |
|||
reader.Dispose(); |
|||
return ret; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,168 @@ |
|||
// <copyright file="DelimitedWriter.cs" company="Math.NET">
|
|||
// Math.NET Numerics, part of the Math.NET Project
|
|||
// http://numerics.mathdotnet.com
|
|||
// http://github.com/mathnet/mathnet-numerics
|
|||
// http://mathnetnumerics.codeplex.com
|
|||
//
|
|||
// Copyright (c) 2009-2010 Math.NET
|
|||
//
|
|||
// Permission is hereby granted, free of charge, to any person
|
|||
// obtaining a copy of this software and associated documentation
|
|||
// files (the "Software"), to deal in the Software without
|
|||
// restriction, including without limitation the rights to use,
|
|||
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|||
// copies of the Software, and to permit persons to whom the
|
|||
// Software is furnished to do so, subject to the following
|
|||
// conditions:
|
|||
// The above copyright notice and this permission notice shall be
|
|||
// included in all copies or substantial portions of the Software.
|
|||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|||
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|||
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|||
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|||
// OTHER DEALINGS IN THE SOFTWARE.
|
|||
// </copyright>
|
|||
|
|||
namespace MathNet.Numerics.LinearAlgebra.Single.IO |
|||
{ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Globalization; |
|||
using System.IO; |
|||
using Generic; |
|||
|
|||
/// <summary>
|
|||
/// Writes an <see cref="Matrix{T}"/> to delimited text file. If the user does not
|
|||
/// specify a delimiter, a tab separator is used.
|
|||
/// </summary>
|
|||
public class DelimitedWriter : MatrixWriter |
|||
{ |
|||
/// <summary>
|
|||
/// The delimiter to use.
|
|||
/// </summary>
|
|||
private readonly string _delimiter; |
|||
|
|||
/// <summary>
|
|||
/// The <see cref="CultureInfo"/> to use.
|
|||
/// </summary>
|
|||
private CultureInfo _cultureInfo = CultureInfo.CurrentCulture; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="DelimitedWriter"/> class using
|
|||
/// a comma as the delimiter.
|
|||
/// </summary>
|
|||
public DelimitedWriter() |
|||
{ |
|||
_delimiter = ","; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="DelimitedWriter"/> class
|
|||
/// using the given delimiter.
|
|||
/// </summary>
|
|||
/// <param name="delimiter">
|
|||
/// the delimiter to use.
|
|||
/// </param>
|
|||
public DelimitedWriter(char delimiter) |
|||
{ |
|||
_delimiter = new string(delimiter, 1); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="DelimitedWriter"/> class
|
|||
/// using the given delimiter.
|
|||
/// </summary>
|
|||
/// <param name="delimiter">
|
|||
/// the delimiter to use.
|
|||
/// </param>
|
|||
public DelimitedWriter(string delimiter) |
|||
{ |
|||
_delimiter = delimiter; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the column header values.
|
|||
/// </summary>
|
|||
/// <value>The column header values.</value>
|
|||
/// <remarks>Will write the column headers if the list is not empty or <c>null</c>.</remarks>
|
|||
public IList<string> ColumnHeaders |
|||
{ |
|||
get; |
|||
set; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the <see cref="CultureInfo"/> to use when parsing the numbers.
|
|||
/// </summary>
|
|||
/// <value>The culture info.</value>
|
|||
/// <remarks>Defaults to <c>CultureInfo.CurrentCulture</c>.</remarks>
|
|||
public CultureInfo CultureInfo |
|||
{ |
|||
get |
|||
{ |
|||
return _cultureInfo; |
|||
} |
|||
|
|||
set |
|||
{ |
|||
if (value != null) |
|||
{ |
|||
_cultureInfo = value; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes the given <see cref="Matrix{T}"/> to the given <see cref="TextWriter"/>.
|
|||
/// </summary>
|
|||
/// <param name="matrix">The matrix to write.</param>
|
|||
/// <param name="writer">The <see cref="TextWriter"/> to write the matrix to.</param>
|
|||
/// <param name="format">The format to use on each element.</param>
|
|||
/// <exception cref="ArgumentNullException">If either <paramref name="matrix"/> or <paramref name="writer"/> is <c>null</c>.</exception>
|
|||
protected override void DoWriteMatrix(Matrix<float> matrix, TextWriter writer, string format) |
|||
{ |
|||
if (matrix == null) |
|||
{ |
|||
throw new ArgumentNullException("matrix"); |
|||
} |
|||
|
|||
if (writer == null) |
|||
{ |
|||
throw new ArgumentNullException("writer"); |
|||
} |
|||
|
|||
if (ColumnHeaders != null && ColumnHeaders.Count > 0) |
|||
{ |
|||
for (var i = 0; i < ColumnHeaders.Count - 1; i++) |
|||
{ |
|||
writer.Write(ColumnHeaders[i]); |
|||
writer.Write(_delimiter); |
|||
} |
|||
|
|||
writer.WriteLine(ColumnHeaders[ColumnHeaders.Count - 1]); |
|||
} |
|||
|
|||
var cols = matrix.ColumnCount - 1; |
|||
var rows = matrix.RowCount - 1; |
|||
for (var i = 0; i < matrix.RowCount; i++) |
|||
{ |
|||
for (var j = 0; j < matrix.ColumnCount; j++) |
|||
{ |
|||
writer.Write(matrix[i, j].ToString(format, _cultureInfo)); |
|||
if (j != cols) |
|||
{ |
|||
writer.Write(_delimiter); |
|||
} |
|||
} |
|||
|
|||
if (i != rows) |
|||
{ |
|||
writer.Write(Environment.NewLine); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,219 @@ |
|||
// <copyright file="MatlabFile.cs" company="Math.NET">
|
|||
// Math.NET Numerics, part of the Math.NET Project
|
|||
// http://numerics.mathdotnet.com
|
|||
// http://github.com/mathnet/mathnet-numerics
|
|||
// http://mathnetnumerics.codeplex.com
|
|||
//
|
|||
// Copyright (c) 2009-2010 Math.NET
|
|||
//
|
|||
// Permission is hereby granted, free of charge, to any person
|
|||
// obtaining a copy of this software and associated documentation
|
|||
// files (the "Software"), to deal in the Software without
|
|||
// restriction, including without limitation the rights to use,
|
|||
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|||
// copies of the Software, and to permit persons to whom the
|
|||
// Software is furnished to do so, subject to the following
|
|||
// conditions:
|
|||
// The above copyright notice and this permission notice shall be
|
|||
// included in all copies or substantial portions of the Software.
|
|||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|||
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|||
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|||
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|||
// OTHER DEALINGS IN THE SOFTWARE.
|
|||
// </copyright>
|
|||
|
|||
namespace MathNet.Numerics.LinearAlgebra.Single.IO.Matlab |
|||
{ |
|||
using System.Collections.Generic; |
|||
using Generic; |
|||
|
|||
/// <summary>
|
|||
/// Represents a Matlab file
|
|||
/// </summary>
|
|||
internal class MatlabFile |
|||
{ |
|||
/// <summary>
|
|||
/// Matrices in a matlab file stored as 1-D arrays
|
|||
/// </summary>
|
|||
private readonly IDictionary<string, Matrix<float>> _matrices = new SortedList<string, Matrix<float>>(); |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the header text.
|
|||
/// </summary>
|
|||
/// <value>The header text.</value>
|
|||
public string HeaderText { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the first name of the matrix.
|
|||
/// </summary>
|
|||
/// <value>The first name of the matrix.</value>
|
|||
public string FirstMatrixName { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the first matrix.
|
|||
/// </summary>
|
|||
/// <value>The first matrix.</value>
|
|||
public Matrix<float> FirstMatrix |
|||
{ |
|||
get |
|||
{ |
|||
if (string.IsNullOrEmpty(FirstMatrixName) || !_matrices.ContainsKey(FirstMatrixName)) |
|||
{ |
|||
return null; |
|||
} |
|||
|
|||
return _matrices[FirstMatrixName]; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the matrices.
|
|||
/// </summary>
|
|||
/// <value>The matrices.</value>
|
|||
public IDictionary<string, Matrix<float>> Matrices |
|||
{ |
|||
get { return _matrices; } |
|||
} |
|||
} |
|||
|
|||
/* |
|||
/// <summary>
|
|||
/// An
|
|||
/// </summary>
|
|||
/// <typeparam name="T"></typeparam>
|
|||
/// <typeparam name="K"></typeparam>
|
|||
internal class ListDictionary<T, K> : IDictionary<T, K> |
|||
{ |
|||
private readonly IList<T> _keys = new List<T>(); |
|||
private readonly IList<K> _values = new List<K>(); |
|||
|
|||
public void Add(T key, K value) |
|||
{ |
|||
_keys.Add(key); |
|||
_values.Add(value); |
|||
} |
|||
|
|||
public bool ContainsKey(T key) |
|||
{ |
|||
return _keys.Contains(key); |
|||
} |
|||
|
|||
public ICollection<T> Keys |
|||
{ |
|||
get { return _keys; } |
|||
} |
|||
|
|||
public bool Remove(T key) |
|||
{ |
|||
if (_keys.Contains(key)) |
|||
{ |
|||
int pos = _keys.IndexOf(key); |
|||
_values.RemoveAt(pos); |
|||
_values.RemoveAt(pos); |
|||
return true; |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
public bool TryGetValue(T key, out K value) |
|||
{ |
|||
if (_keys.Contains(key)) |
|||
{ |
|||
value = _values[_keys.IndexOf(key)]; |
|||
return true; |
|||
} |
|||
value = default(K); |
|||
return false; |
|||
} |
|||
|
|||
public ICollection<K> Values |
|||
{ |
|||
get { return _values; } |
|||
} |
|||
|
|||
public K this[T key] |
|||
{ |
|||
get |
|||
{ |
|||
if (_keys.Contains(key)) |
|||
{ |
|||
return _values[_keys.IndexOf(key)]; |
|||
} |
|||
throw new KeyNotFoundException(); |
|||
} |
|||
set |
|||
{ |
|||
if (_keys.Contains(key)) |
|||
{ |
|||
_values[_keys.IndexOf(key)] = value; |
|||
} |
|||
else |
|||
{ |
|||
Add(key, value); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public void Add(KeyValuePair<T, K> item) |
|||
{ |
|||
Add(item.Key, item.Value); |
|||
} |
|||
|
|||
public void Clear() |
|||
{ |
|||
_keys.Clear(); |
|||
_values.Clear(); |
|||
} |
|||
|
|||
public bool Contains(KeyValuePair<T, K> item) |
|||
{ |
|||
return _keys.Contains(item.Key) && _values[_keys.IndexOf(item.Key)].Equals(item.Value); |
|||
} |
|||
|
|||
public void CopyTo(KeyValuePair<T, K>[] array, int arrayIndex) |
|||
{ |
|||
for (int i = 0; i < _keys.Count; i++) |
|||
{ |
|||
array[arrayIndex + i] = new KeyValuePair<T, K>(_keys[i], _values[i]); |
|||
} |
|||
} |
|||
|
|||
public int Count |
|||
{ |
|||
get { return _keys.Count; } |
|||
} |
|||
|
|||
public bool IsReadOnly |
|||
{ |
|||
get { return false; } |
|||
} |
|||
|
|||
public bool Remove(KeyValuePair<T, K> item) |
|||
{ |
|||
if (Contains(item)) |
|||
{ |
|||
Remove(item.Key); |
|||
return true; |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
public IEnumerator<KeyValuePair<T, K>> GetEnumerator() |
|||
{ |
|||
for (int i = 0; i < _keys.Count; i++) |
|||
{ |
|||
yield return new KeyValuePair<T, K>(_keys[i], _values[i]); |
|||
} |
|||
} |
|||
|
|||
IEnumerator IEnumerable.GetEnumerator() |
|||
{ |
|||
return GetEnumerator(); |
|||
} |
|||
|
|||
}*/ |
|||
} |
|||
@ -0,0 +1,536 @@ |
|||
// <copyright file="MatlabParser.cs" company="Math.NET">
|
|||
// Math.NET Numerics, part of the Math.NET Project
|
|||
// http://numerics.mathdotnet.com
|
|||
// http://github.com/mathnet/mathnet-numerics
|
|||
// http://mathnetnumerics.codeplex.com
|
|||
// Copyright (c) 2009-2010 Math.NET
|
|||
// Permission is hereby granted, free of charge, to any person
|
|||
// obtaining a copy of this software and associated documentation
|
|||
// files (the "Software"), to deal in the Software without
|
|||
// restriction, including without limitation the rights to use,
|
|||
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|||
// copies of the Software, and to permit persons to whom the
|
|||
// Software is furnished to do so, subject to the following
|
|||
// conditions:
|
|||
// The above copyright notice and this permission notice shall be
|
|||
// included in all copies or substantial portions of the Software.
|
|||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|||
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|||
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|||
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|||
// OTHER DEALINGS IN THE SOFTWARE.
|
|||
// </copyright>
|
|||
|
|||
namespace MathNet.Numerics.LinearAlgebra.Single.IO.Matlab |
|||
{ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.IO; |
|||
using System.Text; |
|||
using Common.IO.Matlab; |
|||
using Generic; |
|||
using Properties; |
|||
using zlib; |
|||
|
|||
/// <summary>
|
|||
/// Parse a Matlab file
|
|||
/// </summary>
|
|||
internal class MatlabParser |
|||
{ |
|||
/// <summary>
|
|||
/// Large Block Size
|
|||
/// </summary>
|
|||
private const int LargeBlockSize = 8; |
|||
|
|||
/// <summary>
|
|||
/// Little Endian Indicator
|
|||
/// </summary>
|
|||
private const byte LittleEndianIndicator = 0x49; |
|||
|
|||
/// <summary>
|
|||
/// Small Block Size
|
|||
/// </summary>
|
|||
private const int SmallBlockSize = 4; |
|||
|
|||
/// <summary>
|
|||
/// Holds the names of the matrices in the file.
|
|||
/// </summary>
|
|||
private readonly IList<string> _names = new List<string>(); |
|||
|
|||
/// <summary>
|
|||
/// The stream to read the matlab file from.
|
|||
/// </summary>
|
|||
private readonly Stream _stream; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="MatlabParser"/> class.
|
|||
/// </summary>
|
|||
/// <param name="fileName">Name of the file.</param>
|
|||
public MatlabParser(string fileName) |
|||
: this(fileName, new string[0]) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="MatlabParser"/> class.
|
|||
/// </summary>
|
|||
/// <param name="stream">The stream to read from.</param>
|
|||
public MatlabParser(Stream stream) |
|||
: this(stream, new string[0]) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="MatlabParser"/> class.
|
|||
/// </summary>
|
|||
/// <param name="stream">The stream to read from.</param>
|
|||
/// <param name="objectNames">The name of the objects to retrieve.</param>
|
|||
public MatlabParser(Stream stream, IEnumerable<string> objectNames) |
|||
{ |
|||
if (stream == null) |
|||
{ |
|||
throw new ArgumentNullException("stream"); |
|||
} |
|||
|
|||
_stream = stream; |
|||
SetNames(objectNames); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="MatlabParser"/> class.
|
|||
/// </summary>
|
|||
/// <param name="fileName">Name of the file.</param>
|
|||
/// <param name="objectNames">The name of the objects to retrieve.</param>
|
|||
public MatlabParser(string fileName, IEnumerable<string> objectNames) |
|||
{ |
|||
if (string.IsNullOrEmpty(fileName)) |
|||
{ |
|||
throw new ArgumentException(Resources.StringNullOrEmpty, "filename"); |
|||
} |
|||
|
|||
_stream = File.OpenRead(fileName); |
|||
SetNames(objectNames); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Copies the names of the objects to retrieve to a local field.
|
|||
/// </summary>
|
|||
/// <param name="objectNames">The name of the objects to retrieve.</param>
|
|||
private void SetNames(IEnumerable<string> objectNames) |
|||
{ |
|||
foreach (var name in objectNames) |
|||
{ |
|||
_names.Add(name); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Parses the file.
|
|||
/// </summary>
|
|||
/// <returns>The parsed Matlab file as a <see cref="MatlabFile"/> object.</returns>
|
|||
public MatlabFile Parse() |
|||
{ |
|||
var file = new MatlabFile(); |
|||
|
|||
using (var reader = new BinaryReader(_stream)) |
|||
{ |
|||
file.HeaderText = Encoding.ASCII.GetString(reader.ReadBytes(116)); |
|||
|
|||
// skipping subsystem offsets
|
|||
reader.BaseStream.Position = 126; |
|||
|
|||
if (reader.ReadByte() != LittleEndianIndicator) |
|||
{ |
|||
throw new NotSupportedException(Resources.BigEndianNotSupported); |
|||
} |
|||
|
|||
// skip version since it is always 0x0100.
|
|||
reader.BaseStream.Position = 128; |
|||
var length = _stream.Length; |
|||
|
|||
// for each data block add a matlab object to the file.
|
|||
while (reader.BaseStream.Position < length) |
|||
{ |
|||
var type = (DataType)reader.ReadInt16(); |
|||
int size = reader.ReadInt16(); |
|||
var smallBlock = true; |
|||
if (size == 0) |
|||
{ |
|||
size = reader.ReadInt32(); |
|||
smallBlock = false; |
|||
} |
|||
|
|||
byte[] data; |
|||
if (type == DataType.Compressed) |
|||
{ |
|||
data = DecompressBlock(reader.ReadBytes(size), ref type); |
|||
} |
|||
else |
|||
{ |
|||
data = new byte[size]; |
|||
reader.Read(data, 0, size); |
|||
AlignData(reader.BaseStream, size, smallBlock); |
|||
} |
|||
|
|||
if (type == DataType.Matrix) |
|||
{ |
|||
AddMatrix(data, file); |
|||
} |
|||
else |
|||
{ |
|||
throw new NotSupportedException(string.Format(Resources.NotSupportedType, type)); |
|||
} |
|||
} |
|||
} |
|||
|
|||
return file; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Aligns the data.
|
|||
/// </summary>
|
|||
/// <param name="stream">The stream.</param>
|
|||
/// <param name="size">The size of the array.</param>
|
|||
/// <param name="smallBlock">if set to <c>true</c> if reading from a small block.</param>
|
|||
private static void AlignData(Stream stream, int size, bool smallBlock) |
|||
{ |
|||
var blockSize = smallBlock ? SmallBlockSize : LargeBlockSize; |
|||
var offset = 0; |
|||
var mod = size % blockSize; |
|||
if (mod != 0) |
|||
{ |
|||
offset = blockSize - mod; |
|||
} |
|||
|
|||
stream.Seek(offset, SeekOrigin.Current); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Decompresses the block.
|
|||
/// </summary>
|
|||
/// <param name="compressed">The compressed data.</param>
|
|||
/// <param name="type">The type data type contained in the block.</param>
|
|||
/// <returns>The decompressed block.</returns>
|
|||
private static byte[] DecompressBlock(byte[] compressed, ref DataType type) |
|||
{ |
|||
byte[] data; |
|||
using (var decompressed = new MemoryStream()) |
|||
{ |
|||
using (var decompressor = new ZOutputStream(decompressed)) |
|||
{ |
|||
decompressor.Write(compressed, 0, compressed.Length); |
|||
decompressed.Position = 0; |
|||
var buf = new byte[4]; |
|||
decompressed.Read(buf, 0, 4); |
|||
type = (DataType)BitConverter.ToInt32(buf, 0); |
|||
decompressed.Read(buf, 0, 4); |
|||
var size = BitConverter.ToInt32(buf, 0); |
|||
data = new byte[size]; |
|||
decompressed.Read(data, 0, size); |
|||
} |
|||
} |
|||
|
|||
return data; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Adds a matrix from the actual file into our presentation of a matlab file.
|
|||
/// </summary>
|
|||
/// <param name="data">The data of the matrix.</param>
|
|||
/// <param name="file">The <see cref="MatlabFile"/> instance.</param>
|
|||
private void AddMatrix(byte[] data, MatlabFile file) |
|||
{ |
|||
using (var ms = new MemoryStream(data)) |
|||
{ |
|||
using (var reader = new BinaryReader(ms)) |
|||
{ |
|||
// skip tag - doesn't tell us anything we don't already know
|
|||
reader.BaseStream.Seek(8, SeekOrigin.Current); |
|||
|
|||
var arrayClass = (ArrayClass)reader.ReadByte(); |
|||
var flags = reader.ReadByte(); |
|||
var isComplex = (flags & (byte)ArrayFlags.Complex) == (byte)ArrayFlags.Complex; |
|||
|
|||
if (isComplex) |
|||
{ |
|||
throw new NotSupportedException(Resources.ComplexMatricesNotSupported); |
|||
} |
|||
|
|||
// skip unneeded bytes
|
|||
reader.BaseStream.Seek(10, SeekOrigin.Current); |
|||
|
|||
var numDimensions = reader.ReadInt32() / 8; |
|||
if (numDimensions > 2) |
|||
{ |
|||
throw new NotSupportedException(Resources.MoreThan2D); |
|||
} |
|||
|
|||
var rows = reader.ReadInt32(); |
|||
var columns = reader.ReadInt32(); |
|||
|
|||
// skip unneeded bytes
|
|||
reader.BaseStream.Seek(2, SeekOrigin.Current); |
|||
int size = reader.ReadInt16(); |
|||
var smallBlock = true; |
|||
if (size == 0) |
|||
{ |
|||
size = reader.ReadInt32(); |
|||
smallBlock = false; |
|||
} |
|||
|
|||
var name = Encoding.ASCII.GetString(reader.ReadBytes(size)); |
|||
AlignData(reader.BaseStream, size, smallBlock); |
|||
|
|||
// only grab wanted objects
|
|||
if (_names.Count != 0 && !_names.Contains(name)) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
var type = (DataType)reader.ReadInt16(); |
|||
size = reader.ReadInt16(); |
|||
if (size == 0) |
|||
{ |
|||
size = reader.ReadInt32(); |
|||
} |
|||
|
|||
Matrix<float> matrix; |
|||
switch (arrayClass) |
|||
{ |
|||
case ArrayClass.Sparse: |
|||
matrix = PopulateSparseMatrix(reader, rows, columns, size); |
|||
break; |
|||
case ArrayClass.Function: |
|||
case ArrayClass.Character: |
|||
case ArrayClass.Object: |
|||
case ArrayClass.Structure: |
|||
case ArrayClass.Cell: |
|||
case ArrayClass.Unknown: |
|||
throw new NotImplementedException(); |
|||
default: |
|||
matrix = PopulateDenseMatrix(type, reader, rows, columns); |
|||
break; |
|||
} |
|||
|
|||
file.Matrices.Add(name, matrix); |
|||
if (file.FirstMatrixName == null) |
|||
{ |
|||
file.FirstMatrixName = name; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Populates a sparse matrix.
|
|||
/// </summary>
|
|||
/// <param name="reader">The reader.</param>
|
|||
/// <param name="rows">The number of rows.</param>
|
|||
/// <param name="columns">The number of columns.</param>
|
|||
/// <param name="size">The size of the block.</param>
|
|||
/// <returns>A populated sparse matrix.</returns>
|
|||
private static Matrix<float> PopulateSparseMatrix(BinaryReader reader, int rows, int columns, int size) |
|||
{ |
|||
// populate the row data array
|
|||
var ir = new int[size / 4]; |
|||
for (var i = 0; i < ir.Length; i++) |
|||
{ |
|||
ir[i] = reader.ReadInt32(); |
|||
} |
|||
|
|||
AlignData(reader.BaseStream, size, false); |
|||
|
|||
// skip data type since it will always be int32
|
|||
reader.BaseStream.Seek(4, SeekOrigin.Current); |
|||
|
|||
// populate the column data array
|
|||
var jcsize = reader.ReadInt32(); |
|||
var jc = new int[jcsize / 4]; |
|||
for (var j = 0; j < jc.Length; j++) |
|||
{ |
|||
jc[j] = reader.ReadInt32(); |
|||
} |
|||
|
|||
AlignData(reader.BaseStream, jcsize, false); |
|||
|
|||
var type = (DataType)reader.ReadInt32(); |
|||
|
|||
// skip length since we already no it for the number of rows
|
|||
reader.BaseStream.Seek(4, SeekOrigin.Current); |
|||
|
|||
Matrix<float> matrix = new SparseMatrix(rows, columns); |
|||
var col = 0; |
|||
for (var i = 0; i < ir.Length; i++) |
|||
{ |
|||
var row = ir[i]; |
|||
if (jc[col + 1] == i) |
|||
{ |
|||
col++; |
|||
} |
|||
|
|||
switch (type) |
|||
{ |
|||
case DataType.Int8: |
|||
matrix[row, col] = reader.ReadSByte(); |
|||
break; |
|||
case DataType.UInt8: |
|||
matrix[row, col] = reader.ReadByte(); |
|||
break; |
|||
case DataType.Int16: |
|||
matrix[row, col] = reader.ReadInt16(); |
|||
break; |
|||
|
|||
case DataType.UInt16: |
|||
matrix[row, col] = reader.ReadUInt16(); |
|||
break; |
|||
case DataType.Int32: |
|||
matrix[row, col] = reader.ReadInt32(); |
|||
break; |
|||
|
|||
case DataType.UInt32: |
|||
matrix[row, col] = reader.ReadUInt32(); |
|||
break; |
|||
case DataType.Single: |
|||
matrix[row, col] = reader.ReadSingle(); |
|||
break; |
|||
case DataType.Int64: |
|||
matrix[row, col] = reader.ReadInt64(); |
|||
break; |
|||
case DataType.UInt64: |
|||
matrix[row, col] = reader.ReadUInt64(); |
|||
break; |
|||
case DataType.Double: |
|||
matrix[row, col] = (float)reader.ReadDouble(); |
|||
break; |
|||
default: |
|||
throw new NotSupportedException(); |
|||
} |
|||
} |
|||
|
|||
return matrix; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Populates a dense matrix.
|
|||
/// </summary>
|
|||
/// <param name="type">The type of data.</param>
|
|||
/// <param name="reader">The reader.</param>
|
|||
/// <param name="rows">The number of rows.</param>
|
|||
/// <param name="columns">The number of columns.</param>
|
|||
/// <returns>Returns a populated dense matrix.</returns>
|
|||
private static Matrix<float> PopulateDenseMatrix(DataType type, BinaryReader reader, int rows, int columns) |
|||
{ |
|||
Matrix<float> matrix = new DenseMatrix(rows, columns); |
|||
switch (type) |
|||
{ |
|||
case DataType.Int8: |
|||
for (var j = 0; j < columns; j++) |
|||
{ |
|||
for (var i = 0; i < rows; i++) |
|||
{ |
|||
matrix[i, j] = reader.ReadSByte(); |
|||
} |
|||
} |
|||
|
|||
break; |
|||
case DataType.UInt8: |
|||
for (var j = 0; j < columns; j++) |
|||
{ |
|||
for (var i = 0; i < rows; i++) |
|||
{ |
|||
matrix[i, j] = reader.ReadByte(); |
|||
} |
|||
} |
|||
|
|||
break; |
|||
case DataType.Int16: |
|||
for (var j = 0; j < columns; j++) |
|||
{ |
|||
for (var i = 0; i < rows; i++) |
|||
{ |
|||
matrix[i, j] = reader.ReadInt16(); |
|||
} |
|||
} |
|||
|
|||
break; |
|||
case DataType.UInt16: |
|||
for (var j = 0; j < columns; j++) |
|||
{ |
|||
for (var i = 0; i < rows; i++) |
|||
{ |
|||
matrix[i, j] = reader.ReadUInt16(); |
|||
} |
|||
} |
|||
|
|||
break; |
|||
case DataType.Int32: |
|||
for (var j = 0; j < columns; j++) |
|||
{ |
|||
for (var i = 0; i < rows; i++) |
|||
{ |
|||
matrix[i, j] = reader.ReadInt32(); |
|||
} |
|||
} |
|||
|
|||
break; |
|||
case DataType.UInt32: |
|||
for (var j = 0; j < columns; j++) |
|||
{ |
|||
for (var i = 0; i < rows; i++) |
|||
{ |
|||
matrix[i, j] = reader.ReadUInt32(); |
|||
} |
|||
} |
|||
|
|||
break; |
|||
case DataType.Single: |
|||
for (var j = 0; j < columns; j++) |
|||
{ |
|||
for (var i = 0; i < rows; i++) |
|||
{ |
|||
matrix[i, j] = reader.ReadSingle(); |
|||
} |
|||
} |
|||
|
|||
break; |
|||
case DataType.Int64: |
|||
for (var j = 0; j < columns; j++) |
|||
{ |
|||
for (var i = 0; i < rows; i++) |
|||
{ |
|||
matrix[i, j] = reader.ReadInt64(); |
|||
} |
|||
} |
|||
|
|||
break; |
|||
case DataType.UInt64: |
|||
for (var j = 0; j < columns; j++) |
|||
{ |
|||
for (var i = 0; i < rows; i++) |
|||
{ |
|||
matrix[i, j] = reader.ReadUInt64(); |
|||
} |
|||
} |
|||
|
|||
break; |
|||
case DataType.Double: |
|||
for (var j = 0; j < columns; j++) |
|||
{ |
|||
for (var i = 0; i < rows; i++) |
|||
{ |
|||
matrix[i, j] = (float)reader.ReadDouble(); |
|||
} |
|||
} |
|||
|
|||
break; |
|||
default: |
|||
throw new NotSupportedException(); |
|||
} |
|||
|
|||
return matrix; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,190 @@ |
|||
// <copyright file="MatlabReader.cs" company="Math.NET">
|
|||
// Math.NET Numerics, part of the Math.NET Project
|
|||
// http://numerics.mathdotnet.com
|
|||
// http://github.com/mathnet/mathnet-numerics
|
|||
// http://mathnetnumerics.codeplex.com
|
|||
//
|
|||
// Copyright (c) 2009-2010 Math.NET
|
|||
//
|
|||
// Permission is hereby granted, free of charge, to any person
|
|||
// obtaining a copy of this software and associated documentation
|
|||
// files (the "Software"), to deal in the Software without
|
|||
// restriction, including without limitation the rights to use,
|
|||
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|||
// copies of the Software, and to permit persons to whom the
|
|||
// Software is furnished to do so, subject to the following
|
|||
// conditions:
|
|||
// The above copyright notice and this permission notice shall be
|
|||
// included in all copies or substantial portions of the Software.
|
|||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|||
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|||
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|||
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|||
// OTHER DEALINGS IN THE SOFTWARE.
|
|||
// </copyright>
|
|||
|
|||
namespace MathNet.Numerics.LinearAlgebra.Single.IO |
|||
{ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.IO; |
|||
using Generic; |
|||
using Matlab; |
|||
using Properties; |
|||
|
|||
/// <summary>
|
|||
/// Creates matrices from Matlab files.
|
|||
/// </summary>
|
|||
public class MatlabMatrixReader |
|||
{ |
|||
/// <summary>
|
|||
/// The name of the file to read from.
|
|||
/// </summary>
|
|||
private readonly string _filename; |
|||
|
|||
/// <summary>
|
|||
/// The stream to read from if we are not reading from a file directly.
|
|||
/// </summary>
|
|||
private readonly Stream _stream; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="MatlabMatrixReader"/> class.
|
|||
/// </summary>
|
|||
/// <param name="filename">Name of the file to read matrices from.</param>
|
|||
public MatlabMatrixReader(string filename) |
|||
{ |
|||
if (string.IsNullOrEmpty(filename)) |
|||
{ |
|||
throw new ArgumentException(Resources.StringNullOrEmpty, "filename"); |
|||
} |
|||
|
|||
if (!File.Exists(filename)) |
|||
{ |
|||
throw new FileNotFoundException(Resources.FileDoesNotExist, "filename"); |
|||
} |
|||
|
|||
_filename = filename; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="MatlabMatrixReader"/> class.
|
|||
/// </summary>
|
|||
/// <param name="stream">The stream to reader matrices from.</param>
|
|||
public MatlabMatrixReader(Stream stream) |
|||
{ |
|||
if (stream == null) |
|||
{ |
|||
throw new ArgumentNullException("stream"); |
|||
} |
|||
|
|||
_stream = stream; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reads the first matrix from the file or stream.
|
|||
/// </summary>
|
|||
/// <returns>
|
|||
/// If the matrix is stored as a sparse matrix, then a <see cref="SparseMatrix"/> is returned. Otherwise, a <see cref="DenseMatrix"/>
|
|||
/// is returned.
|
|||
/// </returns>
|
|||
public Matrix<float> ReadMatrix() |
|||
{ |
|||
return ReadMatrix(null); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reads the named matrix from the file or stream.
|
|||
/// </summary>
|
|||
/// <param name="matrixName">The name of the matrix to read.</param>
|
|||
/// <returns>
|
|||
/// If the matrix is stored as a sparse matrix, then a <see cref="SparseMatrix"/> is returned. Otherwise, a <see cref="DenseMatrix"/>
|
|||
/// is returned. <see langword="null"/> is returned if a matrix with the requests name doesn't exist.
|
|||
/// </returns>
|
|||
public Matrix<float> ReadMatrix(string matrixName) |
|||
{ |
|||
Stream stream; |
|||
if (_filename == null) |
|||
{ |
|||
stream = _stream; |
|||
_stream.Seek(0, SeekOrigin.Begin); |
|||
} |
|||
else |
|||
{ |
|||
stream = new FileStream(_filename, FileMode.Open, FileAccess.Read); |
|||
} |
|||
|
|||
var names = string.IsNullOrEmpty(matrixName) ? new string[] { } : new[] { matrixName }; |
|||
var parser = new MatlabParser(stream, names); |
|||
var file = parser.Parse(); |
|||
|
|||
Matrix<float> matrix = null; |
|||
if (string.IsNullOrEmpty(matrixName)) |
|||
{ |
|||
matrix = file.FirstMatrix; |
|||
} |
|||
else if (file.Matrices.ContainsKey(matrixName)) |
|||
{ |
|||
matrix = file.Matrices[matrixName]; |
|||
} |
|||
|
|||
if (_filename != null) |
|||
{ |
|||
stream.Close(); |
|||
stream.Dispose(); |
|||
} |
|||
|
|||
return matrix; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reads all matrices from the file or stream.
|
|||
/// </summary>
|
|||
/// <returns>All matrices from the file or stream.</returns>
|
|||
public Matrix<float>[] ReadMatrices() |
|||
{ |
|||
return ReadMatrices(new string[] { }); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reads the named matrices from the file or stream.
|
|||
/// </summary>
|
|||
/// <param name="names">The names of the matrices to retrieve.</param>
|
|||
/// <returns>
|
|||
/// The named matrices from the file or stream.
|
|||
/// </returns>
|
|||
public Matrix<float>[] ReadMatrices(IEnumerable<string> names) |
|||
{ |
|||
Stream stream; |
|||
if (_filename == null) |
|||
{ |
|||
stream = _stream; |
|||
_stream.Seek(0, SeekOrigin.Begin); |
|||
} |
|||
else |
|||
{ |
|||
stream = new BufferedStream(new FileStream(_filename, FileMode.Open, FileAccess.Read)); |
|||
} |
|||
|
|||
var parser = new MatlabParser(stream, names); |
|||
var file = parser.Parse(); |
|||
|
|||
var matrices = new Matrix<float>[file.Matrices.Count]; |
|||
var i = 0; |
|||
foreach (var matrix in file.Matrices.Values) |
|||
{ |
|||
matrices[i++] = matrix; |
|||
} |
|||
|
|||
if (_filename != null) |
|||
{ |
|||
stream.Close(); |
|||
stream.Dispose(); |
|||
} |
|||
|
|||
return matrices; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,84 @@ |
|||
// <copyright file="MatrixReader.cs" company="Math.NET">
|
|||
// Math.NET Numerics, part of the Math.NET Project
|
|||
// http://numerics.mathdotnet.com
|
|||
// http://github.com/mathnet/mathnet-numerics
|
|||
// http://mathnetnumerics.codeplex.com
|
|||
// Copyright (c) 2009-2010 Math.NET
|
|||
// Permission is hereby granted, free of charge, to any person
|
|||
// obtaining a copy of this software and associated documentation
|
|||
// files (the "Software"), to deal in the Software without
|
|||
// restriction, including without limitation the rights to use,
|
|||
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|||
// copies of the Software, and to permit persons to whom the
|
|||
// Software is furnished to do so, subject to the following
|
|||
// conditions:
|
|||
// The above copyright notice and this permission notice shall be
|
|||
// included in all copies or substantial portions of the Software.
|
|||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|||
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|||
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|||
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|||
// OTHER DEALINGS IN THE SOFTWARE.
|
|||
// </copyright>
|
|||
|
|||
namespace MathNet.Numerics.LinearAlgebra.Single.IO |
|||
{ |
|||
using System; |
|||
using System.IO; |
|||
using Generic; |
|||
using Properties; |
|||
|
|||
/// <summary>
|
|||
/// Base class to read a single <see cref="Matrix{T}"/> from a file or stream.
|
|||
/// </summary>
|
|||
/// <typeparam name="TMatrix">The type of Matrix to return.</typeparam>
|
|||
public abstract class MatrixReader<TMatrix> where TMatrix : Matrix<float> |
|||
{ |
|||
/// <summary>
|
|||
/// Reads a <see cref="Matrix{T}"/> from a file.
|
|||
/// </summary>
|
|||
/// <param name="file">The file to read the matrix from.</param>
|
|||
/// <returns>A <see cref="Matrix{T}"/> containing the data from the file. <see langword="null" /> is returned if the file is empty.</returns>
|
|||
/// <exception cref="ArgumentNullException">If <paramref name="file"/> is <see langword="null" />.</exception>
|
|||
/// <exception cref="IOException">If the file doesn't exist.</exception>
|
|||
/// <exception cref="FormatException">If a value is not a number or not in a valid format.</exception>
|
|||
/// <exception cref="OverflowException">If a value represents a number less than <see cref="Double.MinValue"/> or greater than <see cref="Double.MaxValue"/>.</exception>
|
|||
public Matrix<float> ReadMatrix(string file) |
|||
{ |
|||
if (file == null) |
|||
{ |
|||
throw new ArgumentNullException("file", Resources.StringNullOrEmpty); |
|||
} |
|||
|
|||
return ReadMatrix(File.OpenRead(file)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reads a <see cref="Matrix{T}"/> from a <see cref="Stream"/>.
|
|||
/// </summary>
|
|||
/// <param name="stream">The <see cref="Stream"/> to read the matrix from.</param>
|
|||
/// <returns>A matrix containing the data from the <see cref="Stream"/>. <see langword="null" /> is returned if the <see cref="Stream"/> is empty.</returns>
|
|||
/// <exception cref="ArgumentNullException">If <paramref name="stream"/> is <see langword="null" />.</exception>
|
|||
/// <exception cref="FormatException">If a value is not a number or not in a valid format.</exception>
|
|||
/// <exception cref="OverflowException">If a value represents a number less than <see cref="Double.MinValue"/> or greater than <see cref="Double.MaxValue"/>.</exception>
|
|||
public TMatrix ReadMatrix(Stream stream) |
|||
{ |
|||
if (stream == null) |
|||
{ |
|||
throw new ArgumentNullException("stream"); |
|||
} |
|||
|
|||
return DoReadMatrix(stream); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Subclasses override this method to do the actual reading.
|
|||
/// </summary>
|
|||
/// <param name="stream">The <see cref="Stream"/> to read the matrix from.</param>
|
|||
/// <returns>A matrix containing the data from the <see cref="Stream"/>. <see langword="null" /> is returned if the <see cref="Stream"/> is empty.</returns>
|
|||
protected abstract TMatrix DoReadMatrix(Stream stream); |
|||
} |
|||
} |
|||
@ -0,0 +1,197 @@ |
|||
// <copyright file="MatrixWriter.cs" company="Math.NET">
|
|||
// Math.NET Numerics, part of the Math.NET Project
|
|||
// http://numerics.mathdotnet.com
|
|||
// http://github.com/mathnet/mathnet-numerics
|
|||
// http://mathnetnumerics.codeplex.com
|
|||
//
|
|||
// Copyright (c) 2009-2010 Math.NET
|
|||
//
|
|||
// Permission is hereby granted, free of charge, to any person
|
|||
// obtaining a copy of this software and associated documentation
|
|||
// files (the "Software"), to deal in the Software without
|
|||
// restriction, including without limitation the rights to use,
|
|||
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|||
// copies of the Software, and to permit persons to whom the
|
|||
// Software is furnished to do so, subject to the following
|
|||
// conditions:
|
|||
// The above copyright notice and this permission notice shall be
|
|||
// included in all copies or substantial portions of the Software.
|
|||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|||
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|||
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|||
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|||
// OTHER DEALINGS IN THE SOFTWARE.
|
|||
// </copyright>
|
|||
|
|||
using System; |
|||
using System.IO; |
|||
|
|||
namespace MathNet.Numerics.LinearAlgebra.Single.IO |
|||
{ |
|||
using Generic; |
|||
|
|||
/// <summary>
|
|||
/// Base class to write a single <see cref="Matrix{T}"/> to a file or stream.
|
|||
/// </summary>
|
|||
public abstract class MatrixWriter |
|||
{ |
|||
/// <summary>
|
|||
/// Writes the given <see cref="Matrix{T}"/> to the given file. If the file already exists,
|
|||
/// the file will be overwritten.
|
|||
/// </summary>
|
|||
/// <param name="matrix">The matrix to write.</param>
|
|||
/// <param name="file">The file to write the matrix to.</param>
|
|||
/// <exception cref="ArgumentNullException">If either <paramref name="matrix"/> or <paramref name="file"/> is <c>null</c>.</exception>
|
|||
public void WriteMatrix(Matrix<float> matrix, string file) |
|||
{ |
|||
if (matrix == null) |
|||
{ |
|||
throw new ArgumentNullException("matrix"); |
|||
} |
|||
|
|||
if (file == null) |
|||
{ |
|||
throw new ArgumentNullException("file"); |
|||
} |
|||
|
|||
using (var writer = new StreamWriter(file)) |
|||
{ |
|||
DoWriteMatrix(matrix, writer, null); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes the given <see cref="Matrix{T}"/> to the given file. If the file already exists,
|
|||
/// the file will be overwritten.
|
|||
/// </summary>
|
|||
/// <param name="matrix">the matrix to write.</param>
|
|||
/// <param name="file">The file to write the matrix to.</param>
|
|||
/// <param name="format">The format to use on each element.</param>
|
|||
/// <exception cref="ArgumentNullException">If either <paramref name="matrix"/> or <paramref name="file"/> is <c>null</c>.</exception>
|
|||
public void WriteMatrix(Matrix<float> matrix, string file, string format) |
|||
{ |
|||
if (matrix == null) |
|||
{ |
|||
throw new ArgumentNullException("matrix"); |
|||
} |
|||
|
|||
if (file == null) |
|||
{ |
|||
throw new ArgumentNullException("file"); |
|||
} |
|||
|
|||
if (File.Exists(file)) |
|||
{ |
|||
File.Delete(file); |
|||
} |
|||
|
|||
using (var writer = new StreamWriter(file)) |
|||
{ |
|||
DoWriteMatrix(matrix, writer, format); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes the given <see cref="Matrix{T}"/> to the given stream.
|
|||
/// </summary>
|
|||
/// <param name="matrix">The matrix to write.</param>
|
|||
/// <param name="stream">The <see cref="Stream"/> to write the matrix to.</param>
|
|||
/// <exception cref="ArgumentNullException">If either <paramref name="matrix"/> or <paramref name="stream"/> is <c>null</c>.</exception>
|
|||
public void WriteMatrix(Matrix<float> matrix, Stream stream) |
|||
{ |
|||
if (matrix == null) |
|||
{ |
|||
throw new ArgumentNullException("matrix"); |
|||
} |
|||
|
|||
if (stream == null) |
|||
{ |
|||
throw new ArgumentNullException("stream"); |
|||
} |
|||
|
|||
using (var writer = new StreamWriter(stream)) |
|||
{ |
|||
DoWriteMatrix(matrix, writer, null); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes the given <see cref="Matrix{T}"/> to the given stream.
|
|||
/// </summary>
|
|||
/// <param name="matrix">The <see cref="TextWriter"/> to write.</param>
|
|||
/// <param name="stream">The <see cref="Stream"/> to write the matrix to.</param>
|
|||
/// <param name="format">The format to use on each element.</param>
|
|||
/// <exception cref="ArgumentNullException">If either <paramref name="matrix"/> or <paramref name="stream"/> is <c>null</c>.</exception>
|
|||
public void WriteMatrix(Matrix<float> matrix, Stream stream, string format) |
|||
{ |
|||
if (matrix == null) |
|||
{ |
|||
throw new ArgumentNullException("matrix"); |
|||
} |
|||
|
|||
if (stream == null) |
|||
{ |
|||
throw new ArgumentNullException("stream"); |
|||
} |
|||
|
|||
using (var writer = new StreamWriter(stream)) |
|||
{ |
|||
DoWriteMatrix(matrix, writer, format); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes the given <see cref="Matrix{T}"/> to the given <see cref="TextWriter"/>.
|
|||
/// </summary>
|
|||
/// <param name="matrix">The matrix to write.</param>
|
|||
/// <param name="writer">The <see cref="TextWriter"/> to write the matrix to.</param>
|
|||
/// <exception cref="ArgumentNullException">If either <paramref name="matrix"/> or <paramref name="writer"/> is <c>null</c>.</exception>
|
|||
public void WriteMatrix(Matrix<float> matrix, TextWriter writer) |
|||
{ |
|||
if (matrix == null) |
|||
{ |
|||
throw new ArgumentNullException("matrix"); |
|||
} |
|||
|
|||
if (writer == null) |
|||
{ |
|||
throw new ArgumentNullException("writer"); |
|||
} |
|||
|
|||
DoWriteMatrix(matrix, writer, null); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes the given <see cref="Matrix{T}"/> to the given <see cref="TextWriter"/>.
|
|||
/// </summary>
|
|||
/// <param name="matrix">The matrix to write.</param>
|
|||
/// <param name="writer">The <see cref="TextWriter"/> to write the matrix to.</param>
|
|||
/// <param name="format">The format to use on each element.</param>
|
|||
/// <exception cref="ArgumentNullException">If either <paramref name="matrix"/> or <paramref name="writer"/> is <c>null</c>.</exception>
|
|||
public void WriteMatrix(Matrix<float> matrix, TextWriter writer, string format) |
|||
{ |
|||
if (matrix == null) |
|||
{ |
|||
throw new ArgumentNullException("matrix"); |
|||
} |
|||
|
|||
if (writer == null) |
|||
{ |
|||
throw new ArgumentNullException("writer"); |
|||
} |
|||
|
|||
DoWriteMatrix(matrix, writer, format); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Subclasses must implement this method to do the actually writing.
|
|||
/// </summary>
|
|||
/// <param name="matrix">The matrix to serialize.</param>
|
|||
/// <param name="writer">The <see cref="TextWriter"/> to write the matrix to.</param>
|
|||
/// <param name="format">The format for the new matrix.</param>
|
|||
protected abstract void DoWriteMatrix(Matrix<float> matrix, TextWriter writer, string format); |
|||
} |
|||
} |
|||
@ -0,0 +1,520 @@ |
|||
// <copyright file="BiCgStab.cs" company="Math.NET">
|
|||
// Math.NET Numerics, part of the Math.NET Project
|
|||
// http://numerics.mathdotnet.com
|
|||
// http://github.com/mathnet/mathnet-numerics
|
|||
// http://mathnetnumerics.codeplex.com
|
|||
//
|
|||
// Copyright (c) 2009-2010 Math.NET
|
|||
//
|
|||
// Permission is hereby granted, free of charge, to any person
|
|||
// obtaining a copy of this software and associated documentation
|
|||
// files (the "Software"), to deal in the Software without
|
|||
// restriction, including without limitation the rights to use,
|
|||
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|||
// copies of the Software, and to permit persons to whom the
|
|||
// Software is furnished to do so, subject to the following
|
|||
// conditions:
|
|||
//
|
|||
// The above copyright notice and this permission notice shall be
|
|||
// included in all copies or substantial portions of the Software.
|
|||
//
|
|||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|||
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|||
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|||
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|||
// OTHER DEALINGS IN THE SOFTWARE.
|
|||
// </copyright>
|
|||
|
|||
namespace MathNet.Numerics.LinearAlgebra.Single.Solvers.Iterative |
|||
{ |
|||
using System; |
|||
using Generic; |
|||
using Generic.Solvers; |
|||
using Generic.Solvers.Preconditioners; |
|||
using Generic.Solvers.Status; |
|||
using Preconditioners; |
|||
using Properties; |
|||
|
|||
/// <summary>
|
|||
/// A Bi-Conjugate Gradient stabilized iterative matrix solver.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// <para>
|
|||
/// The Bi-Conjugate Gradient Stabilized (BiCGStab) solver is an 'improvement'
|
|||
/// of the standard Conjugate Gradient (CG) solver. Unlike the CG solver the
|
|||
/// BiCGStab can be used on non-symmetric matrices. <br/>
|
|||
/// Note that much of the success of the solver depends on the selection of the
|
|||
/// proper preconditioner.
|
|||
/// </para>
|
|||
/// <para>
|
|||
/// The Bi-CGSTAB algorithm was taken from: <br/>
|
|||
/// Templates for the solution of linear systems: Building blocks
|
|||
/// for iterative methods
|
|||
/// <br/>
|
|||
/// Richard Barrett, Michael Berry, Tony F. Chan, James Demmel,
|
|||
/// June M. Donato, Jack Dongarra, Victor Eijkhout, Roldan Pozo,
|
|||
/// Charles Romine and Henk van der Vorst
|
|||
/// <br/>
|
|||
/// Url: <a href="http://www.netlib.org/templates/Templates.html">http://www.netlib.org/templates/Templates.html</a>
|
|||
/// <br/>
|
|||
/// Algorithm is described in Chapter 2, section 2.3.8, page 27
|
|||
/// </para>
|
|||
/// <para>
|
|||
/// The example code below provides an indication of the possible use of the
|
|||
/// solver.
|
|||
/// </para>
|
|||
/// </remarks>
|
|||
public sealed class BiCgStab : IIterativeSolver<float> |
|||
{ |
|||
/// <summary>
|
|||
/// The status used if there is no status, i.e. the solver hasn't run yet and there is no
|
|||
/// iterator.
|
|||
/// </summary>
|
|||
private static readonly ICalculationStatus DefaultStatus = new CalculationIndetermined(); |
|||
|
|||
/// <summary>
|
|||
/// The preconditioner that will be used. Can be set to <see langword="null" />, in which case the default
|
|||
/// pre-conditioner will be used.
|
|||
/// </summary>
|
|||
private IPreConditioner<float> _preconditioner; |
|||
|
|||
/// <summary>
|
|||
/// The iterative process controller.
|
|||
/// </summary>
|
|||
private IIterator<float> _iterator; |
|||
|
|||
/// <summary>
|
|||
/// Indicates if the user has stopped the solver.
|
|||
/// </summary>
|
|||
private bool _hasBeenStopped; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="BiCgStab"/> class.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// When using this constructor the solver will use the <see cref="IIterator{T}"/> with
|
|||
/// the standard settings and a default preconditioner.
|
|||
/// </remarks>
|
|||
public BiCgStab() : this(null, null) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="BiCgStab"/> class.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// <para>
|
|||
/// When using this constructor the solver will use a default preconditioner.
|
|||
/// </para>
|
|||
/// <para>
|
|||
/// The main advantages of using a user defined <see cref="IIterator{T}"/> are:
|
|||
/// <list type="number">
|
|||
/// <item>It is possible to set the desired convergence limits.</item>
|
|||
/// <item>
|
|||
/// It is possible to check the reason for which the solver finished
|
|||
/// the iterative procedure by calling the <see cref="IIterator{T}.Status"/> property.
|
|||
/// </item>
|
|||
/// </list>
|
|||
/// </para>
|
|||
/// </remarks>
|
|||
/// <param name="iterator">The <see cref="IIterator{T}"/> that will be used to monitor the iterative process. </param>
|
|||
public BiCgStab(IIterator<float> iterator) : this(null, iterator) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="BiCgStab"/> class.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// When using this constructor the solver will use the <see cref="IIterator{T}"/> with
|
|||
/// the standard settings.
|
|||
/// </remarks>
|
|||
/// <param name="preconditioner">The <see cref="IPreConditioner{T}"/> that will be used to precondition the matrix equation.</param>
|
|||
public BiCgStab(IPreConditioner<float> preconditioner) : this(preconditioner, null) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="BiCgStab"/> class.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// <para>
|
|||
/// The main advantages of using a user defined <see cref="IIterator{T}"/> are:
|
|||
/// <list type="number">
|
|||
/// <item>It is possible to set the desired convergence limits.</item>
|
|||
/// <item>
|
|||
/// It is possible to check the reason for which the solver finished
|
|||
/// the iterative procedure by calling the <see cref="IIterator{T}.Status"/> property.
|
|||
/// </item>
|
|||
/// </list>
|
|||
/// </para>
|
|||
/// </remarks>
|
|||
/// <param name="preconditioner">The <see cref="IPreConditioner{T}"/> that will be used to precondition the matrix equation. </param>
|
|||
/// <param name="iterator">The <see cref="IIterator{T}"/> that will be used to monitor the iterative process. </param>
|
|||
public BiCgStab(IPreConditioner<float> preconditioner, IIterator<float> iterator) |
|||
{ |
|||
_iterator = iterator; |
|||
_preconditioner = preconditioner; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sets the <see cref="IPreConditioner{T}"/> that will be used to precondition the iterative process.
|
|||
/// </summary>
|
|||
/// <param name="preconditioner">The preconditioner.</param>
|
|||
public void SetPreconditioner(IPreConditioner<float> preconditioner) |
|||
{ |
|||
_preconditioner = preconditioner; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sets the <see cref="IIterator{T}"/> that will be used to track the iterative process.
|
|||
/// </summary>
|
|||
/// <param name="iterator">The iterator.</param>
|
|||
public void SetIterator(IIterator<float> iterator) |
|||
{ |
|||
_iterator = iterator; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the status of the iteration once the calculation is finished.
|
|||
/// </summary>
|
|||
public ICalculationStatus IterationResult |
|||
{ |
|||
get |
|||
{ |
|||
return (_iterator != null) ? _iterator.Status : DefaultStatus; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Stops the solve process.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// Note that it may take an indetermined amount of time for the solver to actually stop the process.
|
|||
/// </remarks>
|
|||
public void StopSolve() |
|||
{ |
|||
_hasBeenStopped = true; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Solves the matrix equation Ax = b, where A is the coefficient matrix, b is the
|
|||
/// solution vector and x is the unknown vector.
|
|||
/// </summary>
|
|||
/// <param name="matrix">The coefficient <see cref="Matrix{T}"/>, <c>A</c>.</param>
|
|||
/// <param name="vector">The solution <see cref="Vector{T}"/>, <c>b</c>.</param>
|
|||
/// <returns>The result <see cref="Vector{T}"/>, <c>x</c>.</returns>
|
|||
public Vector<float> Solve(Matrix<float> matrix, Vector<float> vector) |
|||
{ |
|||
if (vector == null) |
|||
{ |
|||
throw new ArgumentNullException(); |
|||
} |
|||
|
|||
Vector<float> result = new DenseVector(matrix.RowCount); |
|||
Solve(matrix, vector, result); |
|||
return result; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Solves the matrix equation Ax = b, where A is the coefficient matrix, b is the
|
|||
/// solution vector and x is the unknown vector.
|
|||
/// </summary>
|
|||
/// <param name="matrix">The coefficient <see cref="Matrix{T}"/>, <c>A</c>.</param>
|
|||
/// <param name="input">The solution <see cref="Vector{T}"/>, <c>b</c>.</param>
|
|||
/// <param name="result">The result <see cref="Vector{T}"/>, <c>x</c>.</param>
|
|||
public void Solve(Matrix<float> matrix, Vector<float> input, Vector<float> 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<float> 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<float> vecP = new DenseVector(residuals.Count); |
|||
Vector<float> vecPdash = new DenseVector(residuals.Count); |
|||
Vector<float> nu = new DenseVector(residuals.Count); |
|||
Vector<float> vecS = new DenseVector(residuals.Count); |
|||
Vector<float> vecSdash = new DenseVector(residuals.Count); |
|||
Vector<float> t = new DenseVector(residuals.Count); |
|||
|
|||
// create some temporary float variables that are needed
|
|||
// to hold values in between iterations
|
|||
float currentRho = 0; |
|||
float alpha = 0; |
|||
float 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))
|
|||
vecP.Add(nu.Multiply(-omega), vecP); |
|||
|
|||
vecP.Multiply(beta, vecP); |
|||
vecP.Add(residuals, vecP); |
|||
} |
|||
else |
|||
{ |
|||
// p_i = r_(i-1)
|
|||
residuals.CopyTo(vecP); |
|||
} |
|||
|
|||
// SOLVE Mp~ = p_i // M = preconditioner
|
|||
_preconditioner.Approximate(vecP, vecPdash); |
|||
|
|||
// nu_i = Ap~
|
|||
matrix.Multiply(vecPdash, nu); |
|||
|
|||
// alpha_i = rho_(i-1)/ (r~^T nu_i) = rho / dotproduct(r~ and nu_i)
|
|||
alpha = currentRho * 1 / tempResiduals.DotProduct(nu); |
|||
|
|||
// s = r_(i-1) - alpha_i nu_i
|
|||
residuals.Add(nu.Multiply(-alpha), vecS); |
|||
|
|||
// Check if we're converged. If so then stop. Otherwise continue;
|
|||
// Calculate the temporary result.
|
|||
// Be careful not to change any of the temp vectors, except for
|
|||
// temp. Others will be used in the calculation later on.
|
|||
// x_i = x_(i-1) + alpha_i * p^_i + s^_i
|
|||
vecPdash.Multiply(alpha, temp); |
|||
temp.Add(vecSdash, temp); |
|||
temp.Add(result, temp); |
|||
|
|||
// Check convergence and stop if we are converged.
|
|||
if (!ShouldContinue(iterationNumber, temp, input, vecS)) |
|||
{ |
|||
temp.CopyTo(result); |
|||
|
|||
// Calculate the true residual
|
|||
CalculateTrueResidual(matrix, residuals, result, input); |
|||
|
|||
// Now recheck the convergence
|
|||
if (!ShouldContinue(iterationNumber, result, input, residuals)) |
|||
{ |
|||
// We're all good now.
|
|||
return; |
|||
} |
|||
|
|||
// Continue the calculation
|
|||
iterationNumber++; |
|||
continue; |
|||
} |
|||
|
|||
// SOLVE Ms~ = s
|
|||
_preconditioner.Approximate(vecS, vecSdash); |
|||
|
|||
// temp = As~
|
|||
matrix.Multiply(vecSdash, t); |
|||
|
|||
// omega_i = temp^T s / temp^T temp
|
|||
omega = t.DotProduct(vecS) / t.DotProduct(t); |
|||
|
|||
// x_i = x_(i-1) + alpha_i p^ + omega_i s^
|
|||
result.Add(vecSdash.Multiply(omega), result); |
|||
result.Add(vecPdash.Multiply(alpha), result); |
|||
|
|||
t.Multiply(-omega, residuals); |
|||
residuals.Add(vecS, residuals); |
|||
|
|||
// for continuation it is necessary that omega_i != 0.0
|
|||
// If omega is only 1 ULP from zero then we fail.
|
|||
if (omega.AlmostEqual(0, 1)) |
|||
{ |
|||
// Omega-type breakdown
|
|||
throw new Exception("Iterative solver experience a numerical break down"); |
|||
} |
|||
|
|||
if (!ShouldContinue(iterationNumber, result, input, residuals)) |
|||
{ |
|||
// Recalculate the residuals and go round again. This is done to ensure that
|
|||
// we have the proper residuals.
|
|||
// The residual calculation based on omega_i * s can be off by a factor 10. So here
|
|||
// we calculate the real residual (which can be expensive) but we only do it if we're
|
|||
// sufficiently close to the finish.
|
|||
CalculateTrueResidual(matrix, residuals, result, input); |
|||
} |
|||
|
|||
iterationNumber++; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Calculates the true residual of the matrix equation Ax = b according to: residual = b - Ax
|
|||
/// </summary>
|
|||
/// <param name="matrix">Instance of the <see cref="Matrix{T}"/> A.</param>
|
|||
/// <param name="residual">Residual values in <see cref="Vector{T}"/>.</param>
|
|||
/// <param name="x">Instance of the <see cref="Vector{T}"/> x.</param>
|
|||
/// <param name="b">Instance of the <see cref="Vector{T}"/> b.</param>
|
|||
private static void CalculateTrueResidual(Matrix<float> matrix, Vector<float> residual, Vector<float> x, Vector<float> b) |
|||
{ |
|||
// -Ax = residual
|
|||
matrix.Multiply(x, residual); |
|||
|
|||
// Do not use residual = residual.Negate() because it creates another object
|
|||
residual.Multiply(-1, residual); |
|||
|
|||
// residual + b
|
|||
residual.Add(b, residual); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Determine if calculation should continue
|
|||
/// </summary>
|
|||
/// <param name="iterationNumber">Number of iterations passed</param>
|
|||
/// <param name="result">Result <see cref="Vector{T}"/>.</param>
|
|||
/// <param name="source">Source <see cref="Vector{T}"/>.</param>
|
|||
/// <param name="residuals">Residual <see cref="Vector{T}"/>.</param>
|
|||
/// <returns><c>true</c> if continue, otherwise <c>false</c></returns>
|
|||
private bool ShouldContinue(int iterationNumber, Vector<float> result, Vector<float> source, Vector<float> residuals) |
|||
{ |
|||
if (_hasBeenStopped) |
|||
{ |
|||
_iterator.IterationCancelled(); |
|||
return true; |
|||
} |
|||
|
|||
_iterator.DetermineStatus(iterationNumber, result, source, residuals); |
|||
var status = _iterator.Status; |
|||
|
|||
// We stop if either:
|
|||
// - the user has stopped the calculation
|
|||
// - the calculation needs to be stopped from a numerical point of view (divergence, convergence etc.)
|
|||
return (!status.TerminatesCalculation) && (!_hasBeenStopped); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Solves the matrix equation AX = B, where A is the coefficient matrix, B is the
|
|||
/// solution matrix and X is the unknown matrix.
|
|||
/// </summary>
|
|||
/// <param name="matrix">The coefficient <see cref="Matrix{T}"/>, <c>A</c>.</param>
|
|||
/// <param name="input">The solution <see cref="Matrix{T}"/>, <c>B</c>.</param>
|
|||
/// <returns>The result <see cref="Matrix{T}"/>, <c>X</c>.</returns>
|
|||
public Matrix<float> Solve(Matrix<float> matrix, Matrix<float> input) |
|||
{ |
|||
if (matrix == null) |
|||
{ |
|||
throw new ArgumentNullException("matrix"); |
|||
} |
|||
|
|||
if (input == null) |
|||
{ |
|||
throw new ArgumentNullException("input"); |
|||
} |
|||
|
|||
var result = matrix.CreateMatrix(input.RowCount, input.ColumnCount); |
|||
Solve(matrix, input, result); |
|||
return result; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Solves the matrix equation AX = B, where A is the coefficient matrix, B is the
|
|||
/// solution matrix and X is the unknown matrix.
|
|||
/// </summary>
|
|||
/// <param name="matrix">The coefficient <see cref="Matrix{T}"/>, <c>A</c>.</param>
|
|||
/// <param name="input">The solution <see cref="Matrix{T}"/>, <c>B</c>.</param>
|
|||
/// <param name="result">The result <see cref="Matrix{T}"/>, <c>X</c></param>
|
|||
public void Solve(Matrix<float> matrix, Matrix<float> input, Matrix<float> 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); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,629 @@ |
|||
// <copyright file="CompositeSolver.cs" company="Math.NET">
|
|||
// Math.NET Numerics, part of the Math.NET Project
|
|||
// http://numerics.mathdotnet.com
|
|||
// http://github.com/mathnet/mathnet-numerics
|
|||
// http://mathnetnumerics.codeplex.com
|
|||
//
|
|||
// Copyright (c) 2009-2010 Math.NET
|
|||
//
|
|||
// Permission is hereby granted, free of charge, to any person
|
|||
// obtaining a copy of this software and associated documentation
|
|||
// files (the "Software"), to deal in the Software without
|
|||
// restriction, including without limitation the rights to use,
|
|||
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|||
// copies of the Software, and to permit persons to whom the
|
|||
// Software is furnished to do so, subject to the following
|
|||
// conditions:
|
|||
//
|
|||
// The above copyright notice and this permission notice shall be
|
|||
// included in all copies or substantial portions of the Software.
|
|||
//
|
|||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|||
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|||
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|||
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|||
// OTHER DEALINGS IN THE SOFTWARE.
|
|||
// </copyright>
|
|||
|
|||
namespace MathNet.Numerics.LinearAlgebra.Single.Solvers.Iterative |
|||
{ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.IO; |
|||
using System.Linq; |
|||
using System.Reflection; |
|||
using Generic; |
|||
using Generic.Solvers; |
|||
using Generic.Solvers.Status; |
|||
using Properties; |
|||
|
|||
/// <summary>
|
|||
/// A composite matrix solver. The actual solver is made by a sequence of
|
|||
/// matrix solvers.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// <para>
|
|||
/// Solver based on:<br />
|
|||
/// Faster PDE-based simulations using robust composite linear solvers<br />
|
|||
/// S. Bhowmicka, P. Raghavan a,*, L. McInnes b, B. Norris<br />
|
|||
/// Future Generation Computer Systems, Vol 20, 2004, pp 373–387<br />
|
|||
/// </para>
|
|||
/// <para>
|
|||
/// Note that if an iterator is passed to this solver it will be used for all the sub-solvers.
|
|||
/// </para>
|
|||
/// </remarks>
|
|||
public sealed class CompositeSolver : IIterativeSolver<float> |
|||
{ |
|||
#region Internal class - DoubleComparer
|
|||
/// <summary>
|
|||
/// An <c>IComparer</c> used to compare double precision floating points.
|
|||
/// </summary>
|
|||
/// NOTE: The instance of this class is used only in <see cref="SolverSetups"/>. If C# suppports interface inheritence
|
|||
/// NOTE: and methods in anonymous types, then this class should be deleted and anonymous type implemented with IComaprer support
|
|||
/// NOTE: in <see cref="SolverSetups"/> constructor
|
|||
public sealed class DoubleComparer : IComparer<double> |
|||
{ |
|||
/// <summary>
|
|||
/// Compares two double values based on the selected comparison method.
|
|||
/// </summary>
|
|||
/// <param name="x">The first double to compare.</param>
|
|||
/// <param name="y">The second double to compare.</param>
|
|||
/// <returns>
|
|||
/// A 32-bit signed integer that indicates the relative order of the objects being compared. The return
|
|||
/// value has the following meanings:
|
|||
/// Value Meaning Less than zero This object is less than the other parameter.
|
|||
/// Zero This object is equal to other.
|
|||
/// Greater than zero This object is greater than other.
|
|||
/// </returns>
|
|||
public int Compare(double x, double y) |
|||
{ |
|||
return x.CompareTo(y, 1); |
|||
} |
|||
} |
|||
#endregion
|
|||
|
|||
/// <summary>
|
|||
/// The default status used if the solver is not running.
|
|||
/// </summary>
|
|||
private static readonly ICalculationStatus NonRunningStatus = new CalculationIndetermined(); |
|||
|
|||
/// <summary>
|
|||
/// The default status used if the solver is running.
|
|||
/// </summary>
|
|||
private static readonly ICalculationStatus RunningStatus = new CalculationRunning(); |
|||
|
|||
#if SILVERLIGHT
|
|||
private static readonly Dictionary<double, List<IIterativeSolverSetup<float>>> SolverSetups = new Dictionary<double, List<IIterativeSolverSetup<float>>>(); |
|||
#else
|
|||
/// <summary>
|
|||
/// The collection of iterative solver setups. Stored based on the
|
|||
/// ratio between the relative speed and relative accuracy.
|
|||
/// </summary>
|
|||
private static readonly SortedList<double, List<IIterativeSolverSetup<float>>> SolverSetups = new SortedList<double, List<IIterativeSolverSetup<float>>>(new DoubleComparer()); |
|||
#endif
|
|||
|
|||
#region Solver information loading methods
|
|||
|
|||
/// <summary>
|
|||
/// Loads all the available <see cref="IIterativeSolverSetup{T}"/> objects from the MathNet.Numerics assembly.
|
|||
/// </summary>
|
|||
public static void LoadSolverInformation() |
|||
{ |
|||
LoadSolverInformation(new Type[0]); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Loads the available <see cref="IIterativeSolverSetup{T}"/> objects from the MathNet.Numerics assembly.
|
|||
/// </summary>
|
|||
/// <param name="typesToExclude">The <see cref="IIterativeSolver{T}"/> types that should not be loaded.</param>
|
|||
public static void LoadSolverInformation(Type[] typesToExclude) |
|||
{ |
|||
LoadSolverInformationFromAssembly(Assembly.GetExecutingAssembly(), typesToExclude); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Loads the available <see cref="IIterativeSolverSetup{T}"/> objects from the assembly specified by the file location.
|
|||
/// </summary>
|
|||
/// <param name="assemblyLocation">The fully qualified path to the assembly.</param>
|
|||
public static void LoadSolverInformationFromAssembly(string assemblyLocation) |
|||
{ |
|||
LoadSolverInformationFromAssembly(assemblyLocation, new Type[0]); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Loads the available <see cref="IIterativeSolverSetup{T}"/> objects from the assembly specified by the file location.
|
|||
/// </summary>
|
|||
/// <param name="assemblyLocation">The fully qualified path to the assembly.</param>
|
|||
/// <param name="typesToExclude">The <see cref="IIterativeSolver{T}"/> types that should not be loaded. </param>
|
|||
public static void LoadSolverInformationFromAssembly(string assemblyLocation, params Type[] typesToExclude) |
|||
{ |
|||
if (assemblyLocation == null) |
|||
{ |
|||
throw new ArgumentNullException("assemblyLocation"); |
|||
} |
|||
|
|||
if (assemblyLocation.Length == 0) |
|||
{ |
|||
throw new ArgumentException(); |
|||
} |
|||
|
|||
if (!File.Exists(assemblyLocation)) |
|||
{ |
|||
throw new FileNotFoundException(); |
|||
} |
|||
|
|||
// Get the assembly name
|
|||
var assemblyFileName = Path.GetFileNameWithoutExtension(assemblyLocation); |
|||
|
|||
// Now load the assembly with an AssemblyName
|
|||
var assemblyName = new AssemblyName(assemblyFileName); |
|||
var assembly = Assembly.Load(assemblyName); |
|||
|
|||
// <ay throws:
|
|||
// ArgumentNullException --> Can't get this because we checked that the file exists.
|
|||
// FileNotFoundException --> Can't get this because we checked that the file exists.
|
|||
// FileLoadException
|
|||
// BadImageFormatException
|
|||
// Now we can load the solver information.
|
|||
LoadSolverInformationFromAssembly(assembly, typesToExclude); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Loads the available <see cref="IIterativeSolverSetup{T}"/> objects from the assembly specified by the assembly name.
|
|||
/// </summary>
|
|||
/// <param name="assemblyName">The <see cref="AssemblyName"/> of the assembly that should be searched for setup objects. </param>
|
|||
public static void LoadSolverInformationFromAssembly(AssemblyName assemblyName) |
|||
{ |
|||
LoadSolverInformationFromAssembly(assemblyName, new Type[0]); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Loads the available <see cref="IIterativeSolverSetup{T}"/> objects from the assembly specified by the assembly name.
|
|||
/// </summary>
|
|||
/// <param name="assemblyName">The <see cref="AssemblyName"/> of the assembly that should be searched for setup objects.</param>
|
|||
/// <param name="typesToExclude">The <see cref="IIterativeSolver{T}"/> types that should not be loaded.</param>
|
|||
public static void LoadSolverInformationFromAssembly(AssemblyName assemblyName, params Type[] typesToExclude) |
|||
{ |
|||
if (assemblyName == null) |
|||
{ |
|||
throw new ArgumentNullException("assemblyName"); |
|||
} |
|||
|
|||
var assembly = Assembly.Load(assemblyName); |
|||
|
|||
// May throw:
|
|||
// ArgumentNullException --> Can't get this because we checked it.
|
|||
// FileNotFoundException
|
|||
// FileLoadException
|
|||
// BadImageFormatException
|
|||
// Now we can load the solver information.
|
|||
LoadSolverInformationFromAssembly(assembly, typesToExclude); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Loads the available <see cref="IIterativeSolverSetup{T}"/> objects from the assembly specified by the type.
|
|||
/// </summary>
|
|||
/// <param name="typeInAssembly">The type in the assembly which should be searched for setup objects.</param>
|
|||
public static void LoadSolverInformationFromAssembly(Type typeInAssembly) |
|||
{ |
|||
LoadSolverInformationFromAssembly(typeInAssembly, new Type[0]); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Loads the available <see cref="IIterativeSolverSetup{T}"/> objects from the assembly specified by the type.
|
|||
/// </summary>
|
|||
/// <param name="typeInAssembly">The type in the assembly which should be searched for setup objects.</param>
|
|||
/// <param name="typesToExclude">The <see cref="IIterativeSolver{T}"/> types that should not be loaded.</param>
|
|||
public static void LoadSolverInformationFromAssembly(Type typeInAssembly, params Type[] typesToExclude) |
|||
{ |
|||
if (typeInAssembly == null) |
|||
{ |
|||
throw new ArgumentNullException("typeInAssembly"); |
|||
} |
|||
|
|||
LoadSolverInformationFromAssembly(typeInAssembly.Assembly, typesToExclude); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Loads the available <see cref="IIterativeSolverSetup{T}"/> objects from the specified assembly.
|
|||
/// </summary>
|
|||
/// <param name="assembly">The assembly which will be searched for setup objects.</param>
|
|||
public static void LoadSolverInformationFromAssembly(Assembly assembly) |
|||
{ |
|||
LoadSolverInformationFromAssembly(assembly, new Type[0]); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Loads the available <see cref="IIterativeSolverSetup{T}"/> objects from the specified assembly.
|
|||
/// </summary>
|
|||
/// <param name="assembly">The assembly which will be searched for setup objects.</param>
|
|||
/// <param name="typesToExclude">The <see cref="IIterativeSolver{T}"/> types that should not be loaded.</param>
|
|||
public static void LoadSolverInformationFromAssembly(Assembly assembly, params Type[] typesToExclude) |
|||
{ |
|||
if (assembly == null) |
|||
{ |
|||
throw new ArgumentNullException("Assembly"); |
|||
} |
|||
|
|||
if (typesToExclude == null) |
|||
{ |
|||
throw new ArgumentNullException("typesToExclude"); |
|||
} |
|||
|
|||
var excludedTypes = new List<Type>(typesToExclude); |
|||
|
|||
// Load all the types in the assembly
|
|||
// Find all the types that implement IIterativeSolverSetup
|
|||
// Create an object of each of these types
|
|||
// Get the type of the iterative solver that will be instantiated by the setup object
|
|||
// Check if it's on the excluding list, if so throw the setup object away otherwise keep it.
|
|||
var interfaceTypes = new List<Type>(); |
|||
foreach (var type in assembly.GetTypes().Where(type => (!type.IsAbstract && !type.IsEnum && !type.IsInterface && type.IsVisible))) |
|||
{ |
|||
interfaceTypes.AddRange(type.GetInterfaces()); |
|||
if (!interfaceTypes.Any(match => typeof(IIterativeSolverSetup<double>).IsAssignableFrom(match))) |
|||
{ |
|||
continue; |
|||
} |
|||
|
|||
// See if we actually want this type of iterative solver
|
|||
IIterativeSolverSetup<float> 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<float>)Activator.CreateInstance(type); |
|||
} |
|||
catch (ArgumentException) |
|||
{ |
|||
continue; |
|||
} |
|||
catch (NotSupportedException) |
|||
{ |
|||
continue; |
|||
} |
|||
catch (TargetInvocationException) |
|||
{ |
|||
continue; |
|||
} |
|||
catch (MethodAccessException) |
|||
{ |
|||
continue; |
|||
} |
|||
catch (MissingMethodException) |
|||
{ |
|||
continue; |
|||
} |
|||
catch (MemberAccessException) |
|||
{ |
|||
continue; |
|||
} |
|||
catch (TypeLoadException) |
|||
{ |
|||
continue; |
|||
} |
|||
|
|||
if (excludedTypes.Any(match => match.IsAssignableFrom(setup.SolverType) || |
|||
match.IsAssignableFrom(setup.PreconditionerType))) |
|||
{ |
|||
continue; |
|||
} |
|||
|
|||
// Ok we want the solver, so store the object
|
|||
var ratio = setup.SolutionSpeed / setup.Reliability; |
|||
if (!SolverSetups.ContainsKey(ratio)) |
|||
{ |
|||
SolverSetups.Add(ratio, new List<IIterativeSolverSetup<float>>()); |
|||
} |
|||
|
|||
var list = SolverSetups[ratio]; |
|||
list.Add(setup); |
|||
} |
|||
} |
|||
|
|||
#endregion
|
|||
|
|||
/// <summary>
|
|||
/// The collection of solvers that will be used to
|
|||
/// </summary>
|
|||
private readonly List<IIterativeSolver<float>> _solvers = new List<IIterativeSolver<float>>(); |
|||
|
|||
/// <summary>
|
|||
/// The status of the calculation.
|
|||
/// </summary>
|
|||
private ICalculationStatus _status = NonRunningStatus; |
|||
|
|||
/// <summary>
|
|||
/// The iterator that is used to control the iteration process.
|
|||
/// </summary>
|
|||
private IIterator<float> _iterator; |
|||
|
|||
/// <summary>
|
|||
/// A flag indicating if the solver has been stopped or not.
|
|||
/// </summary>
|
|||
private bool _hasBeenStopped; |
|||
|
|||
/// <summary>
|
|||
/// The solver that is currently running. Reference is used to be able to stop the
|
|||
/// solver if the user cancels the solve process.
|
|||
/// </summary>
|
|||
private IIterativeSolver<float> _currentSolver; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="CompositeSolver"/> class with the default iterator.
|
|||
/// </summary>
|
|||
public CompositeSolver() : this(null) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="CompositeSolver"/> class with the specified iterator.
|
|||
/// </summary>
|
|||
/// <param name="iterator">The iterator that will be used to control the iteration process. </param>
|
|||
public CompositeSolver(IIterator<float> iterator) |
|||
{ |
|||
_iterator = iterator; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sets the <see cref="IIterator{T}"/> that will be used to track the iterative process.
|
|||
/// </summary>
|
|||
/// <param name="iterator">The iterator.</param>
|
|||
public void SetIterator(IIterator<float> iterator) |
|||
{ |
|||
_iterator = iterator; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the status of the iteration once the calculation is finished.
|
|||
/// </summary>
|
|||
public ICalculationStatus IterationResult |
|||
{ |
|||
get |
|||
{ |
|||
return _status; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Stops the solve process.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// Note that it may take an indetermined amount of time for the solver to actually stop the process.
|
|||
/// </remarks>
|
|||
public void StopSolve() |
|||
{ |
|||
_hasBeenStopped = true; |
|||
if (_currentSolver != null) |
|||
{ |
|||
_currentSolver.StopSolve(); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Solves the matrix equation Ax = b, where A is the coefficient matrix, b is the
|
|||
/// solution vector and x is the unknown vector.
|
|||
/// </summary>
|
|||
/// <param name="matrix">The coefficient matrix, <c>A</c>.</param>
|
|||
/// <param name="vector">The solution vector, <c>b</c>.</param>
|
|||
/// <returns>The result vector, <c>x</c>.</returns>
|
|||
public Vector<float> Solve(Matrix<float> matrix, Vector<float> vector) |
|||
{ |
|||
if (vector == null) |
|||
{ |
|||
throw new ArgumentNullException(); |
|||
} |
|||
|
|||
Vector<float> result = new DenseVector(matrix.RowCount); |
|||
Solve(matrix, vector, result); |
|||
return result; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Solves the matrix equation Ax = b, where A is the coefficient matrix, b is the
|
|||
/// solution vector and x is the unknown vector.
|
|||
/// </summary>
|
|||
/// <param name="matrix">The coefficient matrix, <c>A</c>.</param>
|
|||
/// <param name="input">The solution vector, <c>b</c></param>
|
|||
/// <param name="result">The result vector, <c>x</c></param>
|
|||
public void Solve(Matrix<float> matrix, Vector<float> input, Vector<float> result) |
|||
{ |
|||
// If we were stopped before, we are no longer
|
|||
// We're doing this at the start of the method to ensure
|
|||
// that we can use these fields immediately.
|
|||
_hasBeenStopped = false; |
|||
_currentSolver = null; |
|||
|
|||
// Error checks
|
|||
if (matrix == null) |
|||
{ |
|||
throw new ArgumentNullException("matrix"); |
|||
} |
|||
|
|||
if (matrix.RowCount != matrix.ColumnCount) |
|||
{ |
|||
throw new ArgumentException(Resources.ArgumentMatrixSquare, "matrix"); |
|||
} |
|||
|
|||
if (input == null) |
|||
{ |
|||
throw new ArgumentNullException("input"); |
|||
} |
|||
|
|||
if (result == null) |
|||
{ |
|||
throw new ArgumentNullException("result"); |
|||
} |
|||
|
|||
if (result.Count != input.Count) |
|||
{ |
|||
throw new ArgumentException(Resources.ArgumentVectorsSameLength); |
|||
} |
|||
|
|||
// Initialize the solver fields
|
|||
// Set the convergence monitor
|
|||
if (_iterator == null) |
|||
{ |
|||
_iterator = Iterator.CreateDefault(); |
|||
} |
|||
|
|||
// Load the solvers into our own internal data structure
|
|||
// Once we have solvers we can always reuse them.
|
|||
if (_solvers.Count == 0) |
|||
{ |
|||
LoadSolvers(); |
|||
} |
|||
|
|||
// Create a copy of the solution and result vectors so we can use them
|
|||
// later on
|
|||
var internalInput = input.Clone(); |
|||
var internalResult = result.Clone(); |
|||
|
|||
foreach (var solver in _solvers.TakeWhile(solver => !_hasBeenStopped)) |
|||
{ |
|||
// Store a reference to the solver so we can stop it.
|
|||
_currentSolver = solver; |
|||
|
|||
try |
|||
{ |
|||
// Reset the iterator and pass it to the solver
|
|||
_iterator.ResetToPrecalculationState(); |
|||
solver.SetIterator(_iterator); |
|||
|
|||
// Start the solver
|
|||
solver.Solve(matrix, internalInput, internalResult); |
|||
} |
|||
catch (Exception) |
|||
{ |
|||
// The solver broke down.
|
|||
// Log a message about this
|
|||
// Switch to the next preconditioner.
|
|||
// Reset the solution vector to the previous solution
|
|||
input.CopyTo(internalInput); |
|||
_status = RunningStatus; |
|||
continue; |
|||
} |
|||
|
|||
// There was no fatal breakdown so check the status
|
|||
if (_iterator.Status is CalculationConverged) |
|||
{ |
|||
// We're done
|
|||
break; |
|||
} |
|||
|
|||
// We're not done
|
|||
// Either:
|
|||
// - calculation finished without convergence
|
|||
if (_iterator.Status is CalculationStoppedWithoutConvergence) |
|||
{ |
|||
// Copy the internal result to the result vector and
|
|||
// continue with the calculation.
|
|||
internalInput.CopyTo(input); |
|||
} |
|||
else |
|||
{ |
|||
// - calculation failed --> restart with the original vector
|
|||
// - calculation diverged --> restart with the original vector
|
|||
// - Some unknown status occurred --> To be safe restart.
|
|||
input.CopyTo(internalInput); |
|||
} |
|||
} |
|||
|
|||
// Inside the loop we already copied the final results (if there are any)
|
|||
// So no need to do that again.
|
|||
|
|||
// Clean up
|
|||
// No longer need the current solver
|
|||
_currentSolver = null; |
|||
|
|||
// Set the final status
|
|||
_status = _iterator.Status; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Load solvers
|
|||
/// </summary>
|
|||
private void LoadSolvers() |
|||
{ |
|||
if (SolverSetups.Count == 0) |
|||
{ |
|||
throw new Exception("IIterativeSolverSetup objects not found"); |
|||
} |
|||
|
|||
#if SILVERLIGHT
|
|||
foreach (var setup in SolverSetups.OrderBy(solver => solver.Key, new DoubleComparer()).Select(pair => pair.Value).SelectMany(setups => setups)) |
|||
#else
|
|||
foreach (var setup in SolverSetups.Select(pair => pair.Value).SelectMany(setups => setups)) |
|||
#endif
|
|||
{ |
|||
_solvers.Add(setup.CreateNew()); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Solves the matrix equation AX = B, where A is the coefficient matrix, B is the
|
|||
/// solution matrix and X is the unknown matrix.
|
|||
/// </summary>
|
|||
/// <param name="matrix">The coefficient matrix, <c>A</c>.</param>
|
|||
/// <param name="input">The solution matrix, <c>B</c>.</param>
|
|||
/// <returns>The result matrix, <c>X</c>.</returns>
|
|||
public Matrix<float> Solve(Matrix<float> matrix, Matrix<float> input) |
|||
{ |
|||
if (matrix == null) |
|||
{ |
|||
throw new ArgumentNullException("matrix"); |
|||
} |
|||
|
|||
if (input == null) |
|||
{ |
|||
throw new ArgumentNullException("input"); |
|||
} |
|||
|
|||
var result = matrix.CreateMatrix(input.RowCount, input.ColumnCount); |
|||
Solve(matrix, input, result); |
|||
return result; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Solves the matrix equation AX = B, where A is the coefficient matrix, B is the
|
|||
/// solution matrix and X is the unknown matrix.
|
|||
/// </summary>
|
|||
/// <param name="matrix">The coefficient matrix, <c>A</c>.</param>
|
|||
/// <param name="input">The solution matrix, <c>B</c>.</param>
|
|||
/// <param name="result">The result matrix, <c>X</c></param>
|
|||
public void Solve(Matrix<float> matrix, Matrix<float> input, Matrix<float> 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); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,624 @@ |
|||
// <copyright file="GpBiCg.cs" company="Math.NET">
|
|||
// Math.NET Numerics, part of the Math.NET Project
|
|||
// http://numerics.mathdotnet.com
|
|||
// http://github.com/mathnet/mathnet-numerics
|
|||
// http://mathnetnumerics.codeplex.com
|
|||
//
|
|||
// Copyright (c) 2009-2010 Math.NET
|
|||
//
|
|||
// Permission is hereby granted, free of charge, to any person
|
|||
// obtaining a copy of this software and associated documentation
|
|||
// files (the "Software"), to deal in the Software without
|
|||
// restriction, including without limitation the rights to use,
|
|||
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|||
// copies of the Software, and to permit persons to whom the
|
|||
// Software is furnished to do so, subject to the following
|
|||
// conditions:
|
|||
//
|
|||
// The above copyright notice and this permission notice shall be
|
|||
// included in all copies or substantial portions of the Software.
|
|||
//
|
|||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|||
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|||
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|||
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|||
// OTHER DEALINGS IN THE SOFTWARE.
|
|||
// </copyright>
|
|||
|
|||
namespace MathNet.Numerics.LinearAlgebra.Single.Solvers.Iterative |
|||
{ |
|||
using System; |
|||
using Generic; |
|||
using Generic.Solvers; |
|||
using Generic.Solvers.Preconditioners; |
|||
using Generic.Solvers.Status; |
|||
using Preconditioners; |
|||
using Properties; |
|||
|
|||
/// <summary>
|
|||
/// A Generalized Product Bi-Conjugate Gradient iterative matrix solver.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// <para>
|
|||
/// The Generalized Product Bi-Conjugate Gradient (GPBiCG) solver is an
|
|||
/// alternative version of the Bi-Conjugate Gradient stabilized (CG) solver.
|
|||
/// Unlike the CG solver the GPBiCG solver can be used on
|
|||
/// non-symmetric matrices. <br/>
|
|||
/// Note that much of the success of the solver depends on the selection of the
|
|||
/// proper preconditioner.
|
|||
/// </para>
|
|||
/// <para>
|
|||
/// The GPBiCG algorithm was taken from: <br/>
|
|||
/// GPBiCG(m,l): A hybrid of BiCGSTAB and GPBiCG methods with
|
|||
/// efficiency and robustness
|
|||
/// <br/>
|
|||
/// S. Fujino
|
|||
/// <br/>
|
|||
/// Applied Numerical Mathematics, Volume 41, 2002, pp 107 - 117
|
|||
/// <br/>
|
|||
/// </para>
|
|||
/// <para>
|
|||
/// The example code below provides an indication of the possible use of the
|
|||
/// solver.
|
|||
/// </para>
|
|||
/// </remarks>
|
|||
public sealed class GpBiCg : IIterativeSolver<float> |
|||
{ |
|||
/// <summary>
|
|||
/// The status used if there is no status, i.e. the solver hasn't run yet and there is no
|
|||
/// iterator.
|
|||
/// </summary>
|
|||
private static readonly ICalculationStatus DefaultStatus = new CalculationIndetermined(); |
|||
|
|||
/// <summary>
|
|||
/// The preconditioner that will be used. Can be set to <c>null</c>, in which case the default
|
|||
/// pre-conditioner will be used.
|
|||
/// </summary>
|
|||
private IPreConditioner<float> _preconditioner; |
|||
|
|||
/// <summary>
|
|||
/// The iterative process controller.
|
|||
/// </summary>
|
|||
private IIterator<float> _iterator; |
|||
|
|||
/// <summary>
|
|||
/// Indicates the number of <c>BiCGStab</c> steps should be taken
|
|||
/// before switching.
|
|||
/// </summary>
|
|||
private int _numberOfBiCgStabSteps = 1; |
|||
|
|||
/// <summary>
|
|||
/// Indicates the number of <c>GPBiCG</c> steps should be taken
|
|||
/// before switching.
|
|||
/// </summary>
|
|||
private int _numberOfGpbiCgSteps = 4; |
|||
|
|||
/// <summary>
|
|||
/// Indicates if the user has stopped the solver.
|
|||
/// </summary>
|
|||
private bool _hasBeenStopped; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="GpBiCg"/> class.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// When using this constructor the solver will use the <see cref="IIterator{T}"/> with
|
|||
/// the standard settings and a default preconditioner.
|
|||
/// </remarks>
|
|||
public GpBiCg() : this(null, null) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="GpBiCg"/> class.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// <para>
|
|||
/// When using this constructor the solver will use a default preconditioner.
|
|||
/// </para>
|
|||
/// <para>
|
|||
/// The main advantages of using a user defined <see cref="IIterator{T}"/> are:
|
|||
/// <list type="number">
|
|||
/// <item>It is possible to set the desired convergence limits.</item>
|
|||
/// <item>
|
|||
/// It is possible to check the reason for which the solver finished
|
|||
/// the iterative procedure by calling the <see cref="IIterator{T}.Status"/> property.
|
|||
/// </item>
|
|||
/// </list>
|
|||
/// </para>
|
|||
/// </remarks>
|
|||
/// <param name="iterator">The <see cref="IIterator{T}"/> that will be used to monitor the iterative process.</param>
|
|||
public GpBiCg(IIterator<float> iterator) : this(null, iterator) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="GpBiCg"/> class.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// When using this constructor the solver will use the <see cref="IIterator{T}"/> with
|
|||
/// the standard settings.
|
|||
/// </remarks>
|
|||
/// <param name="preconditioner">The <see cref="IPreConditioner{T}"/> that will be used to precondition the matrix equation.</param>
|
|||
public GpBiCg(IPreConditioner<float> preconditioner) : this(preconditioner, null) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="GpBiCg"/> class.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// <para>
|
|||
/// The main advantages of using a user defined <see cref="IIterator{T}"/> are:
|
|||
/// <list type="number">
|
|||
/// <item>It is possible to set the desired convergence limits.</item>
|
|||
/// <item>
|
|||
/// It is possible to check the reason for which the solver finished
|
|||
/// the iterative procedure by calling the <see cref="IIterator{T}.Status"/> property.
|
|||
/// </item>
|
|||
/// </list>
|
|||
/// </para>
|
|||
/// </remarks>
|
|||
/// <param name="preconditioner">The <see cref="IPreConditioner{T}"/> that will be used to precondition the matrix equation.</param>
|
|||
/// <param name="iterator">The <see cref="IIterator{T}"/> that will be used to monitor the iterative process.</param>
|
|||
public GpBiCg(IPreConditioner<float> preconditioner, IIterator<float> iterator) |
|||
{ |
|||
_iterator = iterator; |
|||
_preconditioner = preconditioner; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the number of steps taken with the <c>BiCgStab</c> algorithm
|
|||
/// before switching over to the <c>GPBiCG</c> algorithm.
|
|||
/// </summary>
|
|||
public int NumberOfBiCgStabSteps |
|||
{ |
|||
get |
|||
{ |
|||
return _numberOfBiCgStabSteps; |
|||
} |
|||
|
|||
set |
|||
{ |
|||
if (value < 0) |
|||
{ |
|||
throw new ArgumentOutOfRangeException("value"); |
|||
} |
|||
|
|||
_numberOfBiCgStabSteps = value; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the number of steps taken with the <c>GPBiCG</c> algorithm
|
|||
/// before switching over to the <c>BiCgStab</c> algorithm.
|
|||
/// </summary>
|
|||
public int NumberOfGpBiCgSteps |
|||
{ |
|||
get |
|||
{ |
|||
return _numberOfGpbiCgSteps; |
|||
} |
|||
|
|||
set |
|||
{ |
|||
if (value < 0) |
|||
{ |
|||
throw new ArgumentOutOfRangeException("value"); |
|||
} |
|||
|
|||
_numberOfGpbiCgSteps = value; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sets the <see cref="IPreConditioner{T}"/> that will be used to precondition the iterative process.
|
|||
/// </summary>
|
|||
/// <param name="preconditioner">The preconditioner.</param>
|
|||
public void SetPreconditioner(IPreConditioner<float> preconditioner) |
|||
{ |
|||
_preconditioner = preconditioner; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sets the <see cref="IIterator{T}"/> that will be used to track the iterative process.
|
|||
/// </summary>
|
|||
/// <param name="iterator">The iterator.</param>
|
|||
public void SetIterator(IIterator<float> iterator) |
|||
{ |
|||
_iterator = iterator; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the status of the iteration once the calculation is finished.
|
|||
/// </summary>
|
|||
public ICalculationStatus IterationResult |
|||
{ |
|||
get |
|||
{ |
|||
return (_iterator != null) ? _iterator.Status : DefaultStatus; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Stops the solve process.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// Note that it may take an indetermined amount of time for the solver to actually
|
|||
/// stop the process.
|
|||
/// </remarks>
|
|||
public void StopSolve() |
|||
{ |
|||
_hasBeenStopped = true; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Solves the matrix equation Ax = b, where A is the coefficient matrix, b is the
|
|||
/// solution vector and x is the unknown vector.
|
|||
/// </summary>
|
|||
/// <param name="matrix">The coefficient matrix, <c>A</c>.</param>
|
|||
/// <param name="vector">The solution vector, <c>b</c>.</param>
|
|||
/// <returns>The result vector, <c>x</c>.</returns>
|
|||
public Vector<float> Solve(Matrix<float> matrix, Vector<float> vector) |
|||
{ |
|||
if (vector == null) |
|||
{ |
|||
throw new ArgumentNullException(); |
|||
} |
|||
|
|||
Vector<float> result = new DenseVector(matrix.RowCount); |
|||
Solve(matrix, vector, result); |
|||
return result; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Solves the matrix equation Ax = b, where A is the coefficient matrix, b is the
|
|||
/// solution vector and x is the unknown vector.
|
|||
/// </summary>
|
|||
/// <param name="matrix">The coefficient matrix, <c>A</c>.</param>
|
|||
/// <param name="input">The solution vector, <c>b</c></param>
|
|||
/// <param name="result">The result vector, <c>x</c></param>
|
|||
public void Solve(Matrix<float> matrix, Vector<float> input, Vector<float> 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<float> xtemp = new DenseVector(input.Count); |
|||
|
|||
// r_0 = b - Ax_0
|
|||
// This is basically a SAXPY so it could be made a lot faster
|
|||
Vector<float> residuals = new DenseVector(matrix.RowCount); |
|||
CalculateTrueResidual(matrix, residuals, xtemp, input); |
|||
|
|||
// Define the temporary scalars
|
|||
float beta = 0; |
|||
float sigma; |
|||
|
|||
// Define the temporary vectors
|
|||
// rDash_0 = r_0
|
|||
Vector<float> rdash = new DenseVector(residuals); |
|||
|
|||
// t_-1 = 0
|
|||
Vector<float> t = new DenseVector(residuals.Count); |
|||
Vector<float> t0 = new DenseVector(residuals.Count); |
|||
|
|||
// w_-1 = 0
|
|||
Vector<float> w = new DenseVector(residuals.Count); |
|||
|
|||
// Define the remaining temporary vectors
|
|||
Vector<float> c = new DenseVector(residuals.Count); |
|||
Vector<float> p = new DenseVector(residuals.Count); |
|||
Vector<float> s = new DenseVector(residuals.Count); |
|||
Vector<float> u = new DenseVector(residuals.Count); |
|||
Vector<float> y = new DenseVector(residuals.Count); |
|||
Vector<float> z = new DenseVector(residuals.Count); |
|||
|
|||
Vector<float> temp = new DenseVector(residuals.Count); |
|||
|
|||
// for (k = 0, 1, .... )
|
|||
var iterationNumber = 0; |
|||
while (ShouldContinue(iterationNumber, xtemp, input, residuals)) |
|||
{ |
|||
// p_k = r_k + beta_(k-1) * (p_(k-1) - u_(k-1))
|
|||
p.Subtract(u, temp); |
|||
|
|||
residuals.Add(temp.Multiply(beta), p); |
|||
|
|||
// Solve M b_k = p_k
|
|||
_preconditioner.Approximate(p, temp); |
|||
|
|||
// s_k = A b_k
|
|||
matrix.Multiply(temp, s); |
|||
|
|||
// alpha_k = (r*_0 * r_k) / (r*_0 * s_k)
|
|||
var alpha = rdash.DotProduct(residuals) / rdash.DotProduct(s); |
|||
|
|||
// y_k = t_(k-1) - r_k - alpha_k * w_(k-1) + alpha_k s_k
|
|||
s.Subtract(w, temp); |
|||
t.Subtract(residuals, y); |
|||
|
|||
y.Add(temp.Multiply(alpha), y); |
|||
|
|||
// Store the old value of t in t0
|
|||
t.CopyTo(t0); |
|||
|
|||
// t_k = r_k - alpha_k s_k
|
|||
residuals.Add(s.Multiply(-alpha), t); |
|||
|
|||
// Solve M d_k = t_k
|
|||
_preconditioner.Approximate(t, temp); |
|||
|
|||
// c_k = A d_k
|
|||
matrix.Multiply(temp, c); |
|||
var cdot = c.DotProduct(c); |
|||
|
|||
// cDot can only be zero if c is a zero vector
|
|||
// We'll set cDot to 1 if it is zero to prevent NaN's
|
|||
// Note that the calculation should continue fine because
|
|||
// c.DotProduct(t) will be zero and so will c.DotProduct(y)
|
|||
if (cdot.AlmostEqual(0, 1)) |
|||
{ |
|||
cdot = 1.0f; |
|||
} |
|||
|
|||
// 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); |
|||
float 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.0f; |
|||
} |
|||
|
|||
var ytdot = y.DotProduct(t); |
|||
var cydot = c.DotProduct(y); |
|||
|
|||
var denom = (cdot * ydot) - (cydot * cydot); |
|||
|
|||
// sigma_k = ((y_k * y_k)(c_k * t_k) - (y_k * t_k)(c_k * y_k)) / ((c_k * c_k)(y_k * y_k) - (y_k * c_k)(c_k * y_k))
|
|||
sigma = ((ydot * ctdot) - (ytdot * cydot)) / denom; |
|||
|
|||
// eta_k = ((c_k * c_k)(y_k * t_k) - (y_k * c_k)(c_k * t_k)) / ((c_k * c_k)(y_k * y_k) - (y_k * c_k)(c_k * y_k))
|
|||
eta = ((cdot * ytdot) - (cydot * ctdot)) / denom; |
|||
} |
|||
|
|||
// u_k = sigma_k s_k + eta_k (t_(k-1) - r_k + beta_(k-1) u_(k-1))
|
|||
t0.Add(u.Multiply(beta), temp); |
|||
|
|||
temp.Subtract(residuals, temp); |
|||
temp.Multiply(eta, temp); |
|||
|
|||
temp.Add(s.Multiply(sigma), u); |
|||
|
|||
// z_k = sigma_k r_k +_ eta_k z_(k-1) - alpha_k u_k
|
|||
z.Multiply(eta, z); |
|||
z.Add(u.Multiply(-alpha), z); |
|||
|
|||
z.Add(residuals.Multiply(sigma), z); |
|||
|
|||
// x_(k+1) = x_k + alpha_k p_k + z_k
|
|||
xtemp.Add(p.Multiply(alpha), xtemp); |
|||
|
|||
xtemp.Add(z, xtemp); |
|||
|
|||
// r_(k+1) = t_k - eta_k y_k - sigma_k c_k
|
|||
// Copy the old residuals to a temp vector because we'll
|
|||
// need those in the next step
|
|||
residuals.CopyTo(t0); |
|||
|
|||
t.Add(y.Multiply(-eta), residuals); |
|||
|
|||
residuals.Add(c.Multiply(-sigma), residuals); |
|||
|
|||
// beta_k = alpha_k / sigma_k * (r*_0 * r_(k+1)) / (r*_0 * r_k)
|
|||
// But first we check if there is a possible NaN. If so just reset beta to zero.
|
|||
beta = (!sigma.AlmostEqual(0, 1)) ? alpha / sigma * rdash.DotProduct(residuals) / rdash.DotProduct(t0) : 0; |
|||
|
|||
// w_k = c_k + beta_k s_k
|
|||
c.Add(s.Multiply(beta), w); |
|||
|
|||
// Get the real value
|
|||
_preconditioner.Approximate(xtemp, result); |
|||
|
|||
// Now check for convergence
|
|||
if (!ShouldContinue(iterationNumber, result, input, residuals)) |
|||
{ |
|||
// Recalculate the residuals and go round again. This is done to ensure that
|
|||
// we have the proper residuals.
|
|||
CalculateTrueResidual(matrix, residuals, result, input); |
|||
} |
|||
|
|||
// Next iteration.
|
|||
iterationNumber++; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Calculates the true residual of the matrix equation Ax = b according to: residual = b - Ax
|
|||
/// </summary>
|
|||
/// <param name="matrix">Instance of the <see cref="Matrix{T}"/> A.</param>
|
|||
/// <param name="residual">Residual values in <see cref="Vector{T}"/>.</param>
|
|||
/// <param name="x">Instance of the <see cref="Vector{T}"/> x.</param>
|
|||
/// <param name="b">Instance of the <see cref="Vector{T}"/> b.</param>
|
|||
private static void CalculateTrueResidual(Matrix<float> matrix, Vector<float> residual, Vector<float> x, Vector<float> b) |
|||
{ |
|||
// -Ax = residual
|
|||
matrix.Multiply(x, residual); |
|||
residual.Multiply(-1, residual); |
|||
|
|||
// residual + b
|
|||
residual.Add(b, residual); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Determine if calculation should continue
|
|||
/// </summary>
|
|||
/// <param name="iterationNumber">Number of iterations passed</param>
|
|||
/// <param name="result">Result <see cref="Vector{T}"/>.</param>
|
|||
/// <param name="source">Source <see cref="Vector{T}"/>.</param>
|
|||
/// <param name="residuals">Residual <see cref="Vector{T}"/>.</param>
|
|||
/// <returns><c>true</c> if continue, otherwise <c>false</c></returns>
|
|||
private bool ShouldContinue(int iterationNumber, Vector<float> result, Vector<float> source, Vector<float> residuals) |
|||
{ |
|||
if (_hasBeenStopped) |
|||
{ |
|||
_iterator.IterationCancelled(); |
|||
return true; |
|||
} |
|||
|
|||
_iterator.DetermineStatus(iterationNumber, result, source, residuals); |
|||
var status = _iterator.Status; |
|||
|
|||
// We stop if either:
|
|||
// - the user has stopped the calculation
|
|||
// - the calculation needs to be stopped from a numerical point of view (divergence, convergence etc.)
|
|||
return (!status.TerminatesCalculation) && (!_hasBeenStopped); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Decide if to do steps with BiCgStab
|
|||
/// </summary>
|
|||
/// <param name="iterationNumber">Number of iteration</param>
|
|||
/// <returns><c>true</c> if yes, otherwise <c>false</c></returns>
|
|||
private bool ShouldRunBiCgStabSteps(int iterationNumber) |
|||
{ |
|||
// Run the first steps as BiCGStab
|
|||
// The number of steps past a whole iteration set
|
|||
var difference = iterationNumber % (_numberOfBiCgStabSteps + _numberOfGpbiCgSteps); |
|||
|
|||
// Do steps with BiCGStab if:
|
|||
// - The difference is zero or more (i.e. we have done zero or more complete cycles)
|
|||
// - The difference is less than the number of BiCGStab steps that should be taken
|
|||
return (difference >= 0) && (difference < _numberOfBiCgStabSteps); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Solves the matrix equation AX = B, where A is the coefficient matrix, B is the
|
|||
/// solution matrix and X is the unknown matrix.
|
|||
/// </summary>
|
|||
/// <param name="matrix">The coefficient matrix, <c>A</c>.</param>
|
|||
/// <param name="input">The solution matrix, <c>B</c>.</param>
|
|||
/// <returns>The result matrix, <c>X</c>.</returns>
|
|||
public Matrix<float> Solve(Matrix<float> matrix, Matrix<float> input) |
|||
{ |
|||
if (matrix == null) |
|||
{ |
|||
throw new ArgumentNullException("matrix"); |
|||
} |
|||
|
|||
if (input == null) |
|||
{ |
|||
throw new ArgumentNullException("input"); |
|||
} |
|||
|
|||
var result = matrix.CreateMatrix(input.RowCount, input.ColumnCount); |
|||
Solve(matrix, input, result); |
|||
return result; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Solves the matrix equation AX = B, where A is the coefficient matrix, B is the
|
|||
/// solution matrix and X is the unknown matrix.
|
|||
/// </summary>
|
|||
/// <param name="matrix">The coefficient matrix, <c>A</c>.</param>
|
|||
/// <param name="input">The solution matrix, <c>B</c>.</param>
|
|||
/// <param name="result">The result matrix, <c>X</c></param>
|
|||
public void Solve(Matrix<float> matrix, Matrix<float> input, Matrix<float> 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); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,779 @@ |
|||
// <copyright file="MlkBiCgStab.cs" company="Math.NET">
|
|||
// Math.NET Numerics, part of the Math.NET Project
|
|||
// http://numerics.mathdotnet.com
|
|||
// http://github.com/mathnet/mathnet-numerics
|
|||
// http://mathnetnumerics.codeplex.com
|
|||
//
|
|||
// Copyright (c) 2009-2010 Math.NET
|
|||
//
|
|||
// Permission is hereby granted, free of charge, to any person
|
|||
// obtaining a copy of this software and associated documentation
|
|||
// files (the "Software"), to deal in the Software without
|
|||
// restriction, including without limitation the rights to use,
|
|||
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|||
// copies of the Software, and to permit persons to whom the
|
|||
// Software is furnished to do so, subject to the following
|
|||
// conditions:
|
|||
//
|
|||
// The above copyright notice and this permission notice shall be
|
|||
// included in all copies or substantial portions of the Software.
|
|||
//
|
|||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|||
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|||
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|||
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|||
// OTHER DEALINGS IN THE SOFTWARE.
|
|||
// </copyright>
|
|||
|
|||
namespace MathNet.Numerics.LinearAlgebra.Single.Solvers.Iterative |
|||
{ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Diagnostics; |
|||
using Distributions; |
|||
using Factorization; |
|||
using Generic; |
|||
using Generic.Solvers; |
|||
using Generic.Solvers.Preconditioners; |
|||
using Generic.Solvers.Status; |
|||
using Preconditioners; |
|||
using Properties; |
|||
|
|||
/// <summary>
|
|||
/// A Multiple-Lanczos Bi-Conjugate Gradient stabilized iterative matrix solver.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// <para>
|
|||
/// The Multiple-Lanczos Bi-Conjugate Gradient stabilized (ML(k)-BiCGStab) solver is an 'improvement'
|
|||
/// of the standard BiCgStab solver.
|
|||
/// </para>
|
|||
/// <para>
|
|||
/// The algorithm was taken from: <br/>
|
|||
/// ML(k)BiCGSTAB: A BiCGSTAB variant based on multiple Lanczos starting vectors
|
|||
/// <br/>
|
|||
/// Man-chung Yeung and Tony F. Chan
|
|||
/// <br/>
|
|||
/// SIAM Journal of Scientific Computing
|
|||
/// <br/>
|
|||
/// Volume 21, Number 4, pp. 1263 - 1290
|
|||
/// </para>
|
|||
/// <para>
|
|||
/// The example code below provides an indication of the possible use of the
|
|||
/// solver.
|
|||
/// </para>
|
|||
/// </remarks>
|
|||
public sealed class MlkBiCgStab : IIterativeSolver<float> |
|||
{ |
|||
/// <summary>
|
|||
/// The default number of starting vectors.
|
|||
/// </summary>
|
|||
private const int DefaultNumberOfStartingVectors = 50; |
|||
|
|||
/// <summary>
|
|||
/// The status used if there is no status, i.e. the solver hasn't run yet and there is no
|
|||
/// iterator.
|
|||
/// </summary>
|
|||
private static readonly ICalculationStatus DefaultStatus = new CalculationIndetermined(); |
|||
|
|||
/// <summary>
|
|||
/// The preconditioner that will be used. Can be set to <see langword="null" />, in which case the default
|
|||
/// pre-conditioner will be used.
|
|||
/// </summary>
|
|||
private IPreConditioner<float> _preconditioner; |
|||
|
|||
/// <summary>
|
|||
/// The iterative process controller.
|
|||
/// </summary>
|
|||
private IIterator<float> _iterator; |
|||
|
|||
/// <summary>
|
|||
/// The collection of starting vectors which are used as the basis for the Krylov sub-space.
|
|||
/// </summary>
|
|||
private IList<Vector<float>> _startingVectors; |
|||
|
|||
/// <summary>
|
|||
/// The number of starting vectors used by the algorithm
|
|||
/// </summary>
|
|||
private int _numberOfStartingVectors = DefaultNumberOfStartingVectors; |
|||
|
|||
/// <summary>
|
|||
/// Indicates if the user has stopped the solver.
|
|||
/// </summary>
|
|||
private bool _hasBeenStopped; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="MlkBiCgStab"/> class.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// When using this constructor the solver will use the <see cref="IIterator{T}"/> with
|
|||
/// the standard settings and a default preconditioner.
|
|||
/// </remarks>
|
|||
public MlkBiCgStab() : this(null, null) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="MlkBiCgStab"/> class.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// <para>
|
|||
/// When using this constructor the solver will use a default preconditioner.
|
|||
/// </para>
|
|||
/// <para>
|
|||
/// The main advantages of using a user defined <see cref="IIterator{T}"/> are:
|
|||
/// <list type="number">
|
|||
/// <item>It is possible to set the desired convergence limits.</item>
|
|||
/// <item>
|
|||
/// It is possible to check the reason for which the solver finished
|
|||
/// the iterative procedure by calling the <see cref="IIterator{T}.Status"/> property.
|
|||
/// </item>
|
|||
/// </list>
|
|||
/// </para>
|
|||
/// </remarks>
|
|||
/// <param name="iterator">The <see cref="IIterator{T}"/> that will be used to monitor the iterative process.</param>
|
|||
public MlkBiCgStab(IIterator<float> iterator) : this(null, iterator) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="MlkBiCgStab"/> class.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// When using this constructor the solver will use the <see cref="IIterator{T}"/> with
|
|||
/// the standard settings.
|
|||
/// </remarks>
|
|||
/// <param name="preconditioner">The <see cref="IPreConditioner{T}"/> that will be used to precondition the matrix equation.</param>
|
|||
public MlkBiCgStab(IPreConditioner<float> preconditioner) : this(preconditioner, null) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="MlkBiCgStab"/> class.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// <para>
|
|||
/// The main advantages of using a user defined <see cref="IIterator{T}"/> are:
|
|||
/// <list type="number">
|
|||
/// <item>It is possible to set the desired convergence limits.</item>
|
|||
/// <item>
|
|||
/// It is possible to check the reason for which the solver finished
|
|||
/// the iterative procedure by calling the <see cref="IIterator{T}.Status"/> property.
|
|||
/// </item>
|
|||
/// </list>
|
|||
/// </para>
|
|||
/// </remarks>
|
|||
/// <param name="preconditioner">The <see cref="IPreConditioner{T}"/> that will be used to precondition the matrix equation.</param>
|
|||
/// <param name="iterator">The <see cref="IIterator{T}"/> that will be used to monitor the iterative process.</param>
|
|||
public MlkBiCgStab(IPreConditioner<float> preconditioner, IIterator<float> iterator) |
|||
{ |
|||
_iterator = iterator; |
|||
_preconditioner = preconditioner; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the number of starting vectors.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// Must be larger than 1 and smaller than the number of variables in the matrix that
|
|||
/// for which this solver will be used.
|
|||
/// </remarks>
|
|||
public int NumberOfStartingVectors |
|||
{ |
|||
[DebuggerStepThrough] |
|||
get |
|||
{ |
|||
return _numberOfStartingVectors; |
|||
} |
|||
|
|||
[DebuggerStepThrough] |
|||
set |
|||
{ |
|||
if (value <= 1) |
|||
{ |
|||
throw new ArgumentOutOfRangeException("value"); |
|||
} |
|||
|
|||
_numberOfStartingVectors = value; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Resets the number of starting vectors to the default value.
|
|||
/// </summary>
|
|||
public void ResetNumberOfStartingVectors() |
|||
{ |
|||
_numberOfStartingVectors = DefaultNumberOfStartingVectors; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sets the <see cref="IPreConditioner{T}"/> that will be used to precondition the iterative process.
|
|||
/// </summary>
|
|||
/// <param name="preconditioner">The preconditioner.</param>
|
|||
public void SetPreconditioner(IPreConditioner<float> preconditioner) |
|||
{ |
|||
_preconditioner = preconditioner; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sets the <see cref="IIterator{T}"/> that will be used to track the iterative process.
|
|||
/// </summary>
|
|||
/// <param name="iterator">The iterator.</param>
|
|||
public void SetIterator(IIterator<float> iterator) |
|||
{ |
|||
_iterator = iterator; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets a series of orthonormal vectors which will be used as basis for the
|
|||
/// Krylov sub-space.
|
|||
/// </summary>
|
|||
public IList<Vector<float>> StartingVectors |
|||
{ |
|||
[DebuggerStepThrough] |
|||
get |
|||
{ |
|||
return _startingVectors; |
|||
} |
|||
|
|||
[DebuggerStepThrough] |
|||
set |
|||
{ |
|||
if ((value == null) || (value.Count == 0)) |
|||
{ |
|||
_startingVectors = null; |
|||
} |
|||
else |
|||
{ |
|||
_startingVectors = value; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the status of the iteration once the calculation is finished.
|
|||
/// </summary>
|
|||
public ICalculationStatus IterationResult |
|||
{ |
|||
[DebuggerStepThrough] |
|||
get |
|||
{ |
|||
return (_iterator != null) ? _iterator.Status : DefaultStatus; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Stops the solve process.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// Note that it may take an indetermined amount of time for the solver to actually stop the process.
|
|||
/// </remarks>
|
|||
public void StopSolve() |
|||
{ |
|||
_hasBeenStopped = true; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Solves the matrix equation Ax = b, where A is the coefficient matrix, b is the
|
|||
/// solution vector and x is the unknown vector.
|
|||
/// </summary>
|
|||
/// <param name="matrix">The coefficient matrix, <c>A</c>.</param>
|
|||
/// <param name="vector">The solution vector, <c>b</c>.</param>
|
|||
/// <returns>The result vector, <c>x</c>.</returns>
|
|||
public Vector<float> Solve(Matrix<float> matrix, Vector<float> vector) |
|||
{ |
|||
if (vector == null) |
|||
{ |
|||
throw new ArgumentNullException(); |
|||
} |
|||
|
|||
Vector<float> result = new DenseVector(matrix.RowCount); |
|||
Solve(matrix, vector, result); |
|||
return result; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Solves the matrix equation Ax = b, where A is the coefficient matrix, b is the
|
|||
/// solution vector and x is the unknown vector.
|
|||
/// </summary>
|
|||
/// <param name="matrix">The coefficient matrix, <c>A</c>.</param>
|
|||
/// <param name="input">The solution vector, <c>b</c></param>
|
|||
/// <param name="result">The result vector, <c>x</c></param>
|
|||
public void Solve(Matrix<float> matrix, Vector<float> input, Vector<float> 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<float> 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<float> residuals = new DenseVector(matrix.RowCount); |
|||
CalculateTrueResidual(matrix, residuals, xtemp, input); |
|||
|
|||
// Define the temporary scalars
|
|||
var c = new float[k]; |
|||
|
|||
// Define the temporary vectors
|
|||
Vector<float> gtemp = new DenseVector(residuals.Count); |
|||
|
|||
Vector<float> u = new DenseVector(residuals.Count); |
|||
Vector<float> utemp = new DenseVector(residuals.Count); |
|||
Vector<float> temp = new DenseVector(residuals.Count); |
|||
Vector<float> temp1 = new DenseVector(residuals.Count); |
|||
|
|||
Vector<float> zd = new DenseVector(residuals.Count); |
|||
Vector<float> zg = new DenseVector(residuals.Count); |
|||
Vector<float> zw = new DenseVector(residuals.Count); |
|||
|
|||
var d = CreateVectorArray(_startingVectors.Count, residuals.Count); |
|||
|
|||
// g_0 = r_0
|
|||
var g = CreateVectorArray(_startingVectors.Count, residuals.Count); |
|||
residuals.CopyTo(g[k - 1]); |
|||
|
|||
var w = CreateVectorArray(_startingVectors.Count, residuals.Count); |
|||
|
|||
// FOR (j = 0, 1, 2 ....)
|
|||
var iterationNumber = 0; |
|||
while (ShouldContinue(iterationNumber, xtemp, input, residuals)) |
|||
{ |
|||
// SOLVE M g~_((j-1)k+k) = g_((j-1)k+k)
|
|||
_preconditioner.Approximate(g[k - 1], gtemp); |
|||
|
|||
// w_((j-1)k+k) = A g~_((j-1)k+k)
|
|||
matrix.Multiply(gtemp, w[k - 1]); |
|||
|
|||
// c_((j-1)k+k) = q^T_1 w_((j-1)k+k)
|
|||
c[k - 1] = _startingVectors[0].DotProduct(w[k - 1]); |
|||
if (c[k - 1].AlmostEqual(0, 1)) |
|||
{ |
|||
throw new Exception("Iterative solver experience a numerical break down"); |
|||
} |
|||
|
|||
// alpha_(jk+1) = q^T_1 r_((j-1)k+k) / c_((j-1)k+k)
|
|||
var alpha = _startingVectors[0].DotProduct(residuals) / c[k - 1]; |
|||
|
|||
// u_(jk+1) = r_((j-1)k+k) - alpha_(jk+1) w_((j-1)k+k)
|
|||
residuals.Add(w[k - 1].Multiply(-alpha), u); |
|||
|
|||
// SOLVE M u~_(jk+1) = u_(jk+1)
|
|||
_preconditioner.Approximate(u, temp1); |
|||
temp1.CopyTo(utemp); |
|||
|
|||
// rho_(j+1) = -u^t_(jk+1) A u~_(jk+1) / ||A u~_(jk+1)||^2
|
|||
matrix.Multiply(temp1, temp); |
|||
var rho = temp.DotProduct(temp); |
|||
|
|||
// If rho is zero then temp is a zero vector and we're probably
|
|||
// about to have zero residuals (i.e. an exact solution).
|
|||
// So set rho to 1.0 because in the next step it will turn to zero.
|
|||
if (rho.AlmostEqual(0, 1)) |
|||
{ |
|||
rho = 1.0f; |
|||
} |
|||
|
|||
rho = -u.DotProduct(temp) / rho; |
|||
|
|||
// x_(jk+1) = x_((j-1)k_k) - rho_(j+1) u~_(jk+1) + alpha_(jk+1) g~_((j-1)k+k)
|
|||
xtemp.Add(utemp.Multiply(-rho), xtemp); |
|||
|
|||
gtemp.Multiply(alpha, gtemp); |
|||
xtemp.Add(gtemp, xtemp); |
|||
|
|||
// r_(jk+1) = rho_(j+1) A u~_(jk+1) + u_(jk+1)
|
|||
u.CopyTo(residuals); |
|||
|
|||
// Reuse temp
|
|||
temp.Multiply(rho, temp); |
|||
residuals.Add(temp, residuals); |
|||
|
|||
// Check convergence and stop if we are converged.
|
|||
if (!ShouldContinue(iterationNumber, xtemp, input, residuals)) |
|||
{ |
|||
// Calculate the true residual
|
|||
CalculateTrueResidual(matrix, residuals, xtemp, input); |
|||
|
|||
// Now recheck the convergence
|
|||
if (!ShouldContinue(iterationNumber, xtemp, input, residuals)) |
|||
{ |
|||
// We're all good now.
|
|||
// Exit from the while loop.
|
|||
break; |
|||
} |
|||
} |
|||
|
|||
// FOR (i = 1,2, ...., k)
|
|||
for (var i = 0; i < k; i++) |
|||
{ |
|||
// z_d = u_(jk+1)
|
|||
u.CopyTo(zd); |
|||
|
|||
// z_g = r_(jk+i)
|
|||
residuals.CopyTo(zg); |
|||
|
|||
// z_w = 0
|
|||
zw.Clear(); |
|||
|
|||
// FOR (s = i, ...., k-1) AND j >= 1
|
|||
float beta; |
|||
if (iterationNumber >= 1) |
|||
{ |
|||
for (var s = i; s < k - 1; s++) |
|||
{ |
|||
// beta^(jk+i)_((j-1)k+s) = -q^t_(s+1) z_d / c_((j-1)k+s)
|
|||
beta = -_startingVectors[s + 1].DotProduct(zd) / c[s]; |
|||
|
|||
// z_d = z_d + beta^(jk+i)_((j-1)k+s) d_((j-1)k+s)
|
|||
zd.Add(d[s].Multiply(beta), zd); |
|||
|
|||
// z_g = z_g + beta^(jk+i)_((j-1)k+s) g_((j-1)k+s)
|
|||
zg.Add(g[s].Multiply(beta), zg); |
|||
|
|||
// z_w = z_w + beta^(jk+i)_((j-1)k+s) w_((j-1)k+s)
|
|||
zw.Add(w[s].Multiply(beta), zw); |
|||
} |
|||
} |
|||
|
|||
beta = rho * c[k - 1]; |
|||
if (beta.AlmostEqual(0, 1)) |
|||
{ |
|||
throw new Exception("Iterative solver experience a numerical break down"); |
|||
} |
|||
|
|||
// beta^(jk+i)_((j-1)k+k) = -(q^T_1 (r_(jk+1) + rho_(j+1) z_w)) / (rho_(j+1) c_((j-1)k+k))
|
|||
residuals.Add(zw.Multiply(rho), temp); |
|||
beta = -_startingVectors[0].DotProduct(temp) / beta; |
|||
|
|||
// z_g = z_g + beta^(jk+i)_((j-1)k+k) g_((j-1)k+k)
|
|||
zg.Add(g[k - 1].Multiply(beta), zg); |
|||
|
|||
// z_w = rho_(j+1) (z_w + beta^(jk+i)_((j-1)k+k) w_((j-1)k+k))
|
|||
zw.Add(w[k - 1].Multiply(beta), zw); |
|||
zw.Multiply(rho, zw); |
|||
|
|||
// z_d = r_(jk+i) + z_w
|
|||
residuals.Add(zw, zd); |
|||
|
|||
// FOR (s = 1, ... i - 1)
|
|||
for (var s = 0; s < i - 1; s++) |
|||
{ |
|||
// beta^(jk+i)_(jk+s) = -q^T_s+1 z_d / c_(jk+s)
|
|||
beta = -_startingVectors[s + 1].DotProduct(zd) / c[s]; |
|||
|
|||
// z_d = z_d + beta^(jk+i)_(jk+s) * d_(jk+s)
|
|||
zd.Add(d[s].Multiply(beta), zd); |
|||
|
|||
// z_g = z_g + beta^(jk+i)_(jk+s) * g_(jk+s)
|
|||
zg.Add(g[s].Multiply(beta), zg); |
|||
} |
|||
|
|||
// d_(jk+i) = z_d - u_(jk+i)
|
|||
zd.Subtract(u, d[i]); |
|||
|
|||
// g_(jk+i) = z_g + z_w
|
|||
zg.Add(zw, g[i]); |
|||
|
|||
// IF (i < k - 1)
|
|||
if (i < k - 1) |
|||
{ |
|||
// c_(jk+1) = q^T_i+1 d_(jk+i)
|
|||
c[i] = _startingVectors[i + 1].DotProduct(d[i]); |
|||
if (c[i].AlmostEqual(0, 1)) |
|||
{ |
|||
throw new Exception("Iterative solver experience a numerical break down"); |
|||
} |
|||
|
|||
// alpha_(jk+i+1) = q^T_(i+1) u_(jk+i) / c_(jk+i)
|
|||
alpha = _startingVectors[i + 1].DotProduct(u) / c[i]; |
|||
|
|||
// u_(jk+i+1) = u_(jk+i) - alpha_(jk+i+1) d_(jk+i)
|
|||
u.Add(d[i].Multiply(-alpha), u); |
|||
|
|||
// SOLVE M g~_(jk+i) = g_(jk+i)
|
|||
_preconditioner.Approximate(g[i], gtemp); |
|||
|
|||
// x_(jk+i+1) = x_(jk+i) + rho_(j+1) alpha_(jk+i+1) g~_(jk+i)
|
|||
xtemp.Add(gtemp.Multiply(rho * alpha), xtemp); |
|||
|
|||
// w_(jk+i) = A g~_(jk+i)
|
|||
matrix.Multiply(gtemp, w[i]); |
|||
|
|||
// r_(jk+i+1) = r_(jk+i) - rho_(j+1) alpha_(jk+i+1) w_(jk+i)
|
|||
residuals.Add(w[i].Multiply(-rho * alpha), residuals); |
|||
|
|||
// We can check the residuals here if they're close
|
|||
if (!ShouldContinue(iterationNumber, xtemp, input, residuals)) |
|||
{ |
|||
// Recalculate the residuals and go round again. This is done to ensure that
|
|||
// we have the proper residuals.
|
|||
CalculateTrueResidual(matrix, residuals, xtemp, input); |
|||
} |
|||
} |
|||
} // END ITERATION OVER i
|
|||
|
|||
iterationNumber++; |
|||
} |
|||
|
|||
// copy the temporary result to the real result vector
|
|||
xtemp.CopyTo(result); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the number of starting vectors to create
|
|||
/// </summary>
|
|||
/// <param name="maximumNumberOfStartingVectors">Maximum number</param>
|
|||
/// <param name="numberOfVariables">Number of variables</param>
|
|||
/// <returns>Number of starting vectors to create</returns>
|
|||
private static int NumberOfStartingVectorsToCreate(int maximumNumberOfStartingVectors, int numberOfVariables) |
|||
{ |
|||
// Create no more starting vectors than the size of the problem - 1
|
|||
return Math.Min(maximumNumberOfStartingVectors, (numberOfVariables - 1)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns an array of starting vectors.
|
|||
/// </summary>
|
|||
/// <param name="maximumNumberOfStartingVectors">The maximum number of starting vectors that should be created.</param>
|
|||
/// <param name="numberOfVariables">The number of variables.</param>
|
|||
/// <returns>
|
|||
/// An array with starting vectors. The array will never be larger than the
|
|||
/// <paramref name="maximumNumberOfStartingVectors"/> but it may be smaller if
|
|||
/// the <paramref name="numberOfVariables"/> is smaller than
|
|||
/// the <paramref name="maximumNumberOfStartingVectors"/>.
|
|||
/// </returns>
|
|||
private static IList<Vector<float>> 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<float> matrix = new DenseMatrix(numberOfVariables, count); |
|||
for (var i = 0; i < matrix.ColumnCount; i++) |
|||
{ |
|||
var samples = new float[matrix.RowCount]; |
|||
for (var j = 0; j < matrix.RowCount; j++) |
|||
{ |
|||
samples[j] = (float)distribution.Sample(); |
|||
} |
|||
|
|||
// Set the column
|
|||
matrix.SetColumn(i, samples); |
|||
} |
|||
|
|||
// Compute the orthogonalization.
|
|||
var gs = new GramSchmidt(matrix); |
|||
var orthogonalMatrix = gs.Q; |
|||
|
|||
// Now transfer this to vectors
|
|||
var result = new List<Vector<float>>(); |
|||
for (var i = 0; i < orthogonalMatrix.ColumnCount; i++) |
|||
{ |
|||
result.Add(orthogonalMatrix.Column(i)); |
|||
|
|||
// Normalize the result vector
|
|||
result[i].Multiply(1 / (float)result[i].Norm(2), result[i]); |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Create random vecrors array
|
|||
/// </summary>
|
|||
/// <param name="arraySize">Number of vectors</param>
|
|||
/// <param name="vectorSize">Size of each vector</param>
|
|||
/// <returns>Array of random vectors</returns>
|
|||
private static Vector<float>[] CreateVectorArray(int arraySize, int vectorSize) |
|||
{ |
|||
var result = new Vector<float>[arraySize]; |
|||
for (var i = 0; i < result.Length; i++) |
|||
{ |
|||
result[i] = new DenseVector(vectorSize); |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Calculates the true residual of the matrix equation Ax = b according to: residual = b - Ax
|
|||
/// </summary>
|
|||
/// <param name="matrix">Source <see cref="Matrix{T}"/>A.</param>
|
|||
/// <param name="residual">Residual <see cref="Vector{T}"/> data.</param>
|
|||
/// <param name="x">x <see cref="Vector{T}"/> data.</param>
|
|||
/// <param name="b">b <see cref="Vector{T}"/> data.</param>
|
|||
private static void CalculateTrueResidual(Matrix<float> matrix, Vector<float> residual, Vector<float> x, Vector<float> b) |
|||
{ |
|||
// -Ax = residual
|
|||
matrix.Multiply(x, residual); |
|||
residual.Multiply(-1, residual); |
|||
|
|||
// residual + b
|
|||
residual.Add(b, residual); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Determine if calculation should continue
|
|||
/// </summary>
|
|||
/// <param name="iterationNumber">Number of iterations passed</param>
|
|||
/// <param name="result">Result <see cref="Vector{T}"/>.</param>
|
|||
/// <param name="source">Source <see cref="Vector{T}"/>.</param>
|
|||
/// <param name="residuals">Residual <see cref="Vector{T}"/>.</param>
|
|||
/// <returns><c>true</c> if continue, otherwise <c>false</c></returns>
|
|||
private bool ShouldContinue(int iterationNumber, Vector<float> result, Vector<float> source, Vector<float> residuals) |
|||
{ |
|||
if (_hasBeenStopped) |
|||
{ |
|||
_iterator.IterationCancelled(); |
|||
return true; |
|||
} |
|||
|
|||
_iterator.DetermineStatus(iterationNumber, result, source, residuals); |
|||
var status = _iterator.Status; |
|||
|
|||
// We stop if either:
|
|||
// - the user has stopped the calculation
|
|||
// - the calculation needs to be stopped from a numerical point of view (divergence, convergence etc.)
|
|||
return (!status.TerminatesCalculation) && (!_hasBeenStopped); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Solves the matrix equation AX = B, where A is the coefficient matrix, B is the
|
|||
/// solution matrix and X is the unknown matrix.
|
|||
/// </summary>
|
|||
/// <param name="matrix">The coefficient matrix, <c>A</c>.</param>
|
|||
/// <param name="input">The solution matrix, <c>B</c>.</param>
|
|||
/// <returns>The result matrix, <c>X</c>.</returns>
|
|||
public Matrix<float> Solve(Matrix<float> matrix, Matrix<float> input) |
|||
{ |
|||
if (matrix == null) |
|||
{ |
|||
throw new ArgumentNullException("matrix"); |
|||
} |
|||
|
|||
if (input == null) |
|||
{ |
|||
throw new ArgumentNullException("input"); |
|||
} |
|||
|
|||
var result = matrix.CreateMatrix(input.RowCount, input.ColumnCount); |
|||
Solve(matrix, input, result); |
|||
return result; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Solves the matrix equation AX = B, where A is the coefficient matrix, B is the
|
|||
/// solution matrix and X is the unknown matrix.
|
|||
/// </summary>
|
|||
/// <param name="matrix">The coefficient matrix, <c>A</c>.</param>
|
|||
/// <param name="input">The solution matrix, <c>B</c>.</param>
|
|||
/// <param name="result">The result matrix, <c>X</c></param>
|
|||
public void Solve(Matrix<float> matrix, Matrix<float> input, Matrix<float> 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); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,528 @@ |
|||
// <copyright file="TFQMR.cs" company="Math.NET">
|
|||
// Math.NET Numerics, part of the Math.NET Project
|
|||
// http://numerics.mathdotnet.com
|
|||
// http://github.com/mathnet/mathnet-numerics
|
|||
// http://mathnetnumerics.codeplex.com
|
|||
//
|
|||
// Copyright (c) 2009-2010 Math.NET
|
|||
//
|
|||
// Permission is hereby granted, free of charge, to any person
|
|||
// obtaining a copy of this software and associated documentation
|
|||
// files (the "Software"), to deal in the Software without
|
|||
// restriction, including without limitation the rights to use,
|
|||
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|||
// copies of the Software, and to permit persons to whom the
|
|||
// Software is furnished to do so, subject to the following
|
|||
// conditions:
|
|||
//
|
|||
// The above copyright notice and this permission notice shall be
|
|||
// included in all copies or substantial portions of the Software.
|
|||
//
|
|||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|||
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|||
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|||
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|||
// OTHER DEALINGS IN THE SOFTWARE.
|
|||
// </copyright>
|
|||
|
|||
namespace MathNet.Numerics.LinearAlgebra.Single.Solvers.Iterative |
|||
{ |
|||
using System; |
|||
using Generic; |
|||
using Generic.Solvers; |
|||
using Generic.Solvers.Preconditioners; |
|||
using Generic.Solvers.Status; |
|||
using Preconditioners; |
|||
using Properties; |
|||
|
|||
/// <summary>
|
|||
/// A Transpose Free Quasi-Minimal Residual (TFQMR) iterative matrix solver.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// <para>
|
|||
/// The TFQMR algorithm was taken from: <br/>
|
|||
/// Iterative methods for sparse linear systems.
|
|||
/// <br/>
|
|||
/// Yousef Saad
|
|||
/// <br/>
|
|||
/// Algorithm is described in Chapter 7, section 7.4.3, page 219
|
|||
/// </para>
|
|||
/// <para>
|
|||
/// The example code below provides an indication of the possible use of the
|
|||
/// solver.
|
|||
/// </para>
|
|||
/// </remarks>
|
|||
public sealed class TFQMR : IIterativeSolver<float> |
|||
{ |
|||
/// <summary>
|
|||
/// The status used if there is no status, i.e. the solver hasn't run yet and there is no
|
|||
/// iterator.
|
|||
/// </summary>
|
|||
private static readonly ICalculationStatus DefaultStatus = new CalculationIndetermined(); |
|||
|
|||
/// <summary>
|
|||
/// The preconditioner that will be used. Can be set to <see langword="null" />, in which case the default
|
|||
/// pre-conditioner will be used.
|
|||
/// </summary>
|
|||
private IPreConditioner<float> _preconditioner; |
|||
|
|||
/// <summary>
|
|||
/// The iterative process controller.
|
|||
/// </summary>
|
|||
private IIterator<float> _iterator; |
|||
|
|||
/// <summary>
|
|||
/// Indicates if the user has stopped the solver.
|
|||
/// </summary>
|
|||
private bool _hasBeenStopped; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="TFQMR"/> class.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// When using this constructor the solver will use the <see cref="IIterator{T}"/> with
|
|||
/// the standard settings and a default preconditioner.
|
|||
/// </remarks>
|
|||
public TFQMR() : this(null, null) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="TFQMR"/> class.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// <para>
|
|||
/// When using this constructor the solver will use a default preconditioner.
|
|||
/// </para>
|
|||
/// <para>
|
|||
/// The main advantages of using a user defined <see cref="IIterator{T}"/> are:
|
|||
/// <list type="number">
|
|||
/// <item>It is possible to set the desired convergence limits.</item>
|
|||
/// <item>
|
|||
/// It is possible to check the reason for which the solver finished
|
|||
/// the iterative procedure by calling the <see cref="IIterator{T}.Status"/> property.
|
|||
/// </item>
|
|||
/// </list>
|
|||
/// </para>
|
|||
/// </remarks>
|
|||
/// <param name="iterator">The <see cref="IIterator{T}"/> that will be used to monitor the iterative process.</param>
|
|||
public TFQMR(IIterator<float> iterator) : this(null, iterator) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="TFQMR"/> class.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// When using this constructor the solver will use the <see cref="IIterator{T}"/> with
|
|||
/// the standard settings.
|
|||
/// </remarks>
|
|||
/// <param name="preconditioner">The <see cref="IPreConditioner{T}"/> that will be used to precondition the matrix equation.</param>
|
|||
public TFQMR(IPreConditioner<float> preconditioner) : this(preconditioner, null) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="TFQMR"/> class.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// <para>
|
|||
/// The main advantages of using a user defined <see cref="IIterator{T}"/> are:
|
|||
/// <list type="number">
|
|||
/// <item>It is possible to set the desired convergence limits.</item>
|
|||
/// <item>
|
|||
/// It is possible to check the reason for which the solver finished
|
|||
/// the iterative procedure by calling the <see cref="IIterator{T}.Status"/> property.
|
|||
/// </item>
|
|||
/// </list>
|
|||
/// </para>
|
|||
/// </remarks>
|
|||
/// <param name="preconditioner">The <see cref="IPreConditioner{T}"/> that will be used to precondition the matrix equation.</param>
|
|||
/// <param name="iterator">The <see cref="IIterator{T}"/> that will be used to monitor the iterative process.</param>
|
|||
public TFQMR(IPreConditioner<float> preconditioner, IIterator<float> iterator) |
|||
{ |
|||
_iterator = iterator; |
|||
_preconditioner = preconditioner; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sets the <see cref="IPreConditioner{T}"/> that will be used to precondition the iterative process.
|
|||
/// </summary>
|
|||
/// <param name="preconditioner">The preconditioner.</param>
|
|||
public void SetPreconditioner(IPreConditioner<float> preconditioner) |
|||
{ |
|||
_preconditioner = preconditioner; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sets the <see cref="IIterator{T}"/> that will be used to track the iterative process.
|
|||
/// </summary>
|
|||
/// <param name="iterator">The iterator.</param>
|
|||
public void SetIterator(IIterator<float> iterator) |
|||
{ |
|||
_iterator = iterator; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the status of the iteration once the calculation is finished.
|
|||
/// </summary>
|
|||
public ICalculationStatus IterationResult |
|||
{ |
|||
get |
|||
{ |
|||
return (_iterator != null) ? _iterator.Status : DefaultStatus; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Stops the solve process.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// Note that it may take an indetermined amount of time for the solver to actually stop the process.
|
|||
/// </remarks>
|
|||
public void StopSolve() |
|||
{ |
|||
_hasBeenStopped = true; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Solves the matrix equation Ax = b, where A is the coefficient matrix, b is the
|
|||
/// solution vector and x is the unknown vector.
|
|||
/// </summary>
|
|||
/// <param name="matrix">The coefficient matrix, <c>A</c>.</param>
|
|||
/// <param name="vector">The solution vector, <c>b</c>.</param>
|
|||
/// <returns>The result vector, <c>x</c>.</returns>
|
|||
public Vector<float> Solve(Matrix<float> matrix, Vector<float> vector) |
|||
{ |
|||
if (vector == null) |
|||
{ |
|||
throw new ArgumentNullException(); |
|||
} |
|||
|
|||
Vector<float> result = new DenseVector(matrix.RowCount); |
|||
Solve(matrix, vector, result); |
|||
return result; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Solves the matrix equation Ax = b, where A is the coefficient matrix, b is the
|
|||
/// solution vector and x is the unknown vector.
|
|||
/// </summary>
|
|||
/// <param name="matrix">The coefficient matrix, <c>A</c>.</param>
|
|||
/// <param name="input">The solution vector, <c>b</c></param>
|
|||
/// <param name="result">The result vector, <c>x</c></param>
|
|||
public void Solve(Matrix<float> matrix, Vector<float> input, Vector<float> result) |
|||
{ |
|||
// If we were stopped before, we are no longer
|
|||
// We're doing this at the start of the method to ensure
|
|||
// that we can use these fields immediately.
|
|||
_hasBeenStopped = false; |
|||
|
|||
// Error checks
|
|||
if (matrix == null) |
|||
{ |
|||
throw new ArgumentNullException("matrix"); |
|||
} |
|||
|
|||
if (matrix.RowCount != matrix.ColumnCount) |
|||
{ |
|||
throw new ArgumentException(Resources.ArgumentMatrixSquare, "matrix"); |
|||
} |
|||
|
|||
if (input == null) |
|||
{ |
|||
throw new ArgumentNullException("input"); |
|||
} |
|||
|
|||
if (result == null) |
|||
{ |
|||
throw new ArgumentNullException("result"); |
|||
} |
|||
|
|||
if (result.Count != input.Count) |
|||
{ |
|||
throw new ArgumentException(Resources.ArgumentVectorsSameLength); |
|||
} |
|||
|
|||
// Initialize the solver fields
|
|||
// Set the convergence monitor
|
|||
if (_iterator == null) |
|||
{ |
|||
_iterator = Iterator.CreateDefault(); |
|||
} |
|||
|
|||
if (_preconditioner == null) |
|||
{ |
|||
_preconditioner = new UnitPreconditioner(); |
|||
} |
|||
|
|||
_preconditioner.Initialize(matrix); |
|||
|
|||
var d = new DenseVector(input.Count); |
|||
var r = new DenseVector(input); |
|||
|
|||
var uodd = new DenseVector(input.Count); |
|||
var ueven = new DenseVector(input.Count); |
|||
|
|||
var v = new DenseVector(input.Count); |
|||
var pseudoResiduals = new DenseVector(input); |
|||
|
|||
var x = new DenseVector(input.Count); |
|||
var yodd = new DenseVector(input.Count); |
|||
var yeven = new DenseVector(input); |
|||
|
|||
// Temp vectors
|
|||
var temp = new DenseVector(input.Count); |
|||
|
|||
// Initialize
|
|||
var startNorm = (float)input.Norm(2); |
|||
|
|||
// Define the scalars
|
|||
float alpha = 0; |
|||
float eta = 0; |
|||
float 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
|
|||
yeven.Add(v.Multiply(-alpha), yodd); |
|||
|
|||
// Solve M temp = yOdd
|
|||
_preconditioner.Approximate(yodd, temp); |
|||
|
|||
// uOdd = A temp
|
|||
matrix.Multiply(temp, uodd); |
|||
} |
|||
|
|||
// The intermediate step which is equal for both even and
|
|||
// odd iteration steps.
|
|||
// Select the correct vector
|
|||
var uinternal = IsEven(iterationNumber) ? ueven : uodd; |
|||
var yinternal = IsEven(iterationNumber) ? yeven : yodd; |
|||
|
|||
// pseudoResiduals = pseudoResiduals - alpha * uOdd
|
|||
pseudoResiduals.Add(uinternal.Multiply(-alpha), pseudoResiduals); |
|||
|
|||
// d = yOdd + theta * theta * eta / alpha * d
|
|||
d.Multiply(theta * theta * eta / alpha, temp); |
|||
yinternal.Add(temp, d); |
|||
|
|||
// theta = ||pseudoResiduals||_2 / tau
|
|||
theta = (float)pseudoResiduals.Norm(2) / tau; |
|||
var c = 1 / (float)Math.Sqrt(1 + (theta * theta)); |
|||
|
|||
// tau = tau * theta * c
|
|||
tau *= theta * c; |
|||
|
|||
// eta = c^2 * alpha
|
|||
eta = c * c * alpha; |
|||
|
|||
// x = x + eta * d
|
|||
x.Add(d.Multiply(eta), x); |
|||
|
|||
// Check convergence and see if we can bail
|
|||
if (!ShouldContinue(iterationNumber, result, input, pseudoResiduals)) |
|||
{ |
|||
// Calculate the real values
|
|||
_preconditioner.Approximate(x, result); |
|||
|
|||
// Calculate the true residual. Use the temp vector for that
|
|||
// so that we don't pollute the pseudoResidual vector for no
|
|||
// good reason.
|
|||
CalculateTrueResidual(matrix, temp, result, input); |
|||
|
|||
// Now recheck the convergence
|
|||
if (!ShouldContinue(iterationNumber, result, input, temp)) |
|||
{ |
|||
// We're all good now.
|
|||
return; |
|||
} |
|||
} |
|||
|
|||
// The odd step
|
|||
if (!IsEven(iterationNumber)) |
|||
{ |
|||
if (rho.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
|
|||
pseudoResiduals.Add(yodd.Multiply(beta), yeven); |
|||
|
|||
// Solve M temp = yOdd
|
|||
_preconditioner.Approximate(yeven, temp); |
|||
|
|||
// uOdd = A temp
|
|||
matrix.Multiply(temp, ueven); |
|||
|
|||
// v = uEven + beta * (uOdd + beta * v)
|
|||
uodd.Add(v.Multiply(beta), temp); |
|||
|
|||
ueven.Add(temp.Multiply(beta), v); |
|||
} |
|||
|
|||
// Calculate the real values
|
|||
_preconditioner.Approximate(x, result); |
|||
|
|||
iterationNumber++; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Calculates the true residual of the matrix equation Ax = b according to: residual = b - Ax
|
|||
/// </summary>
|
|||
/// <param name="matrix">Instance of the <see cref="Matrix{T}"/> A.</param>
|
|||
/// <param name="residual">Residual values in <see cref="Vector{T}"/>.</param>
|
|||
/// <param name="x">Instance of the <see cref="Vector{T}"/> x.</param>
|
|||
/// <param name="b">Instance of the <see cref="Vector{T}"/> b.</param>
|
|||
private static void CalculateTrueResidual(Matrix<float> matrix, Vector<float> residual, Vector<float> x, Vector<float> b) |
|||
{ |
|||
// -Ax = residual
|
|||
matrix.Multiply(x, residual); |
|||
residual.Multiply(-1, residual); |
|||
|
|||
// residual + b
|
|||
residual.Add(b, residual); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Determine if calculation should continue
|
|||
/// </summary>
|
|||
/// <param name="iterationNumber">Number of iterations passed</param>
|
|||
/// <param name="result">Result <see cref="Vector{T}"/>.</param>
|
|||
/// <param name="source">Source <see cref="Vector{T}"/>.</param>
|
|||
/// <param name="residuals">Residual <see cref="Vector{T}"/>.</param>
|
|||
/// <returns><c>true</c> if continue, otherwise <c>false</c></returns>
|
|||
private bool ShouldContinue(int iterationNumber, Vector<float> result, Vector<float> source, Vector<float> residuals) |
|||
{ |
|||
if (_hasBeenStopped) |
|||
{ |
|||
_iterator.IterationCancelled(); |
|||
return true; |
|||
} |
|||
|
|||
_iterator.DetermineStatus(iterationNumber, result, source, residuals); |
|||
var status = _iterator.Status; |
|||
|
|||
// We stop if either:
|
|||
// - the user has stopped the calculation
|
|||
// - the calculation needs to be stopped from a numerical point of view (divergence, convergence etc.)
|
|||
return (!status.TerminatesCalculation) && (!_hasBeenStopped); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Is <paramref name="number"/> even?
|
|||
/// </summary>
|
|||
/// <param name="number">Number to check</param>
|
|||
/// <returns><c>true</c> if <paramref name="number"/> even, otherwise <c>false</c></returns>
|
|||
private static bool IsEven(int number) |
|||
{ |
|||
return number % 2 == 0; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Solves the matrix equation AX = B, where A is the coefficient matrix, B is the
|
|||
/// solution matrix and X is the unknown matrix.
|
|||
/// </summary>
|
|||
/// <param name="matrix">The coefficient matrix, <c>A</c>.</param>
|
|||
/// <param name="input">The solution matrix, <c>B</c>.</param>
|
|||
/// <returns>The result matrix, <c>X</c>.</returns>
|
|||
public Matrix<float> Solve(Matrix<float> matrix, Matrix<float> input) |
|||
{ |
|||
if (matrix == null) |
|||
{ |
|||
throw new ArgumentNullException("matrix"); |
|||
} |
|||
|
|||
if (input == null) |
|||
{ |
|||
throw new ArgumentNullException("input"); |
|||
} |
|||
|
|||
var result = matrix.CreateMatrix(input.RowCount, input.ColumnCount); |
|||
Solve(matrix, input, result); |
|||
return result; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Solves the matrix equation AX = B, where A is the coefficient matrix, B is the
|
|||
/// solution matrix and X is the unknown matrix.
|
|||
/// </summary>
|
|||
/// <param name="matrix">The coefficient matrix, <c>A</c>.</param>
|
|||
/// <param name="input">The solution matrix, <c>B</c>.</param>
|
|||
/// <param name="result">The result matrix, <c>X</c></param>
|
|||
public void Solve(Matrix<float> matrix, Matrix<float> input, Matrix<float> 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); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,328 @@ |
|||
// <copyright file="Iterator.cs" company="Math.NET">
|
|||
// Math.NET Numerics, part of the Math.NET Project
|
|||
// http://numerics.mathdotnet.com
|
|||
// http://github.com/mathnet/mathnet-numerics
|
|||
// http://mathnetnumerics.codeplex.com
|
|||
//
|
|||
// Copyright (c) 2009-2010 Math.NET
|
|||
//
|
|||
// Permission is hereby granted, free of charge, to any person
|
|||
// obtaining a copy of this software and associated documentation
|
|||
// files (the "Software"), to deal in the Software without
|
|||
// restriction, including without limitation the rights to use,
|
|||
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|||
// copies of the Software, and to permit persons to whom the
|
|||
// Software is furnished to do so, subject to the following
|
|||
// conditions:
|
|||
//
|
|||
// The above copyright notice and this permission notice shall be
|
|||
// included in all copies or substantial portions of the Software.
|
|||
//
|
|||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|||
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|||
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|||
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|||
// OTHER DEALINGS IN THE SOFTWARE.
|
|||
// </copyright>
|
|||
|
|||
namespace MathNet.Numerics.LinearAlgebra.Single.Solvers |
|||
{ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using Generic; |
|||
using Generic.Solvers; |
|||
using Generic.Solvers.Status; |
|||
using Generic.Solvers.StopCriterium; |
|||
using Properties; |
|||
using StopCriterium; |
|||
|
|||
/// <summary>
|
|||
/// An iterator that is used to check if an iterative calculation should continue or stop.
|
|||
/// </summary>
|
|||
public sealed class Iterator : IIterator<float> |
|||
{ |
|||
/// <summary>
|
|||
/// The default status for the iterator.
|
|||
/// </summary>
|
|||
private static readonly ICalculationStatus DefaultStatus = new CalculationIndetermined(); |
|||
|
|||
/// <summary>
|
|||
/// Creates a default iterator with all the <see cref="IIterationStopCriterium{T}"/> objects.
|
|||
/// </summary>
|
|||
/// <returns>A new <see cref="IIterator{T}"/> object.</returns>
|
|||
public static IIterator<float> CreateDefault() |
|||
{ |
|||
var iterator = new Iterator(); |
|||
iterator.Add(new FailureStopCriterium()); |
|||
iterator.Add(new DivergenceStopCriterium()); |
|||
iterator.Add(new IterationCountStopCriterium()); |
|||
iterator.Add(new ResidualStopCriterium()); |
|||
|
|||
return iterator; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// The collection that holds all the stop criteria and the flag indicating if they should be added
|
|||
/// to the child iterators.
|
|||
/// </summary>
|
|||
private readonly Dictionary<Type, IIterationStopCriterium<float>> _stopCriterias = new Dictionary<Type, IIterationStopCriterium<float>>(); |
|||
|
|||
/// <summary>
|
|||
/// The status of the iterator.
|
|||
/// </summary>
|
|||
private ICalculationStatus _status = DefaultStatus; |
|||
|
|||
/// <summary>
|
|||
/// Indicates if the iteration was cancelled.
|
|||
/// </summary>
|
|||
private bool _wasIterationCancelled; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="Iterator"/> class.
|
|||
/// </summary>
|
|||
public Iterator() : this(null) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="Iterator"/> class with the specified stop criteria.
|
|||
/// </summary>
|
|||
/// <param name="stopCriteria">
|
|||
/// The specified stop criteria. Only one stop criterium of each type can be passed in. None
|
|||
/// of the stop criteria will be passed on to child iterators.
|
|||
/// </param>
|
|||
/// <exception cref="ArgumentException">Thrown if <paramref name="stopCriteria"/> contains multiple stop criteria of the same type.</exception>
|
|||
public Iterator(IEnumerable<IIterationStopCriterium<float>> stopCriteria) |
|||
{ |
|||
// Add the stop criteria
|
|||
if (stopCriteria == null) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
foreach (var stopCriterium in stopCriteria.Where(stopCriterium => stopCriterium != null)) |
|||
{ |
|||
Add(stopCriterium); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Adds an <see cref="IIterationStopCriterium{T}"/> to the internal collection of stop-criteria. Only a
|
|||
/// single stop criterium of each type can be stored.
|
|||
/// </summary>
|
|||
/// <param name="stopCriterium">The stop criterium to add.</param>
|
|||
/// <exception cref="ArgumentNullException">Thrown if <paramref name="stopCriterium"/> is <see langword="null" />.</exception>
|
|||
/// <exception cref="ArgumentException">
|
|||
/// Thrown if <paramref name="stopCriterium"/> is of the same type as an already
|
|||
/// stored criterium.
|
|||
/// </exception>
|
|||
public void Add(IIterationStopCriterium<float> stopCriterium) |
|||
{ |
|||
if (stopCriterium == null) |
|||
{ |
|||
throw new ArgumentNullException("stopCriterium"); |
|||
} |
|||
|
|||
if (_stopCriterias.ContainsKey(stopCriterium.GetType())) |
|||
{ |
|||
throw new ArgumentException(Resources.StopCriteriumDuplicate); |
|||
} |
|||
|
|||
// Store the stop criterium.
|
|||
_stopCriterias.Add(stopCriterium.GetType(), stopCriterium); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Removes the <see cref="IIterationStopCriterium{T}"/> from the internal collection.
|
|||
/// </summary>
|
|||
/// <param name="stopCriterium">The stop criterium that must be removed.</param>
|
|||
public void Remove(IIterationStopCriterium<float> stopCriterium) |
|||
{ |
|||
if (stopCriterium == null) |
|||
{ |
|||
throw new ArgumentNullException("stopCriterium"); |
|||
} |
|||
|
|||
if (!_stopCriterias.ContainsKey(stopCriterium.GetType())) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
// Remove from the collection
|
|||
_stopCriterias.Remove(stopCriterium.GetType()); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Indicates if the specific stop criterium is stored by the <see cref="IIterator{T}"/>.
|
|||
/// </summary>
|
|||
/// <param name="stopCriterium">The stop criterium.</param>
|
|||
/// <returns><c>true</c> if the <see cref="IIterator{T}"/> contains the stop criterium; otherwise <c>false</c>.</returns>
|
|||
public bool Contains(IIterationStopCriterium<float> stopCriterium) |
|||
{ |
|||
return stopCriterium != null && _stopCriterias.ContainsKey(stopCriterium.GetType()); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the number of stored stop criteria.
|
|||
/// </summary>
|
|||
/// <remarks>Used for testing only.</remarks>
|
|||
internal int NumberOfCriteria |
|||
{ |
|||
get |
|||
{ |
|||
return _stopCriterias.Count; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets an <c>IEnumerator</c> that enumerates over all the stored stop criteria.
|
|||
/// </summary>
|
|||
/// <remarks>Used for testing only.</remarks>
|
|||
internal IEnumerator<IIterationStopCriterium<float>> StoredStopCriteria |
|||
{ |
|||
get |
|||
{ |
|||
return _stopCriterias.Select(criterium => criterium.Value).GetEnumerator(); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Indicates to the iterator that the iterative process has been cancelled.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// Does not reset the stop-criteria.
|
|||
/// </remarks>
|
|||
public void IterationCancelled() |
|||
{ |
|||
_wasIterationCancelled = true; |
|||
_status = new CalculationCancelled(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Determines the status of the iterative calculation based on the stop criteria stored
|
|||
/// by the current <see cref="IIterator{T}"/>. Result is set into <c>Status</c> field.
|
|||
/// </summary>
|
|||
/// <param name="iterationNumber">The number of iterations that have passed so far.</param>
|
|||
/// <param name="solutionVector">The vector containing the current solution values.</param>
|
|||
/// <param name="sourceVector">The right hand side vector.</param>
|
|||
/// <param name="residualVector">The vector containing the current residual vectors.</param>
|
|||
/// <remarks>
|
|||
/// The individual iterators may internally track the progress of the calculation based
|
|||
/// on the invocation of this method. Therefore this method should only be called if the
|
|||
/// calculation has moved forwards at least one step.
|
|||
/// </remarks>
|
|||
public void DetermineStatus(int iterationNumber, Vector<float> solutionVector, Vector<float> sourceVector, Vector<float> residualVector) |
|||
{ |
|||
if (_stopCriterias.Count == 0) |
|||
{ |
|||
throw new ArgumentException(Resources.StopCriteriumMissing); |
|||
} |
|||
|
|||
if (iterationNumber < 0) |
|||
{ |
|||
throw new ArgumentOutOfRangeException("iterationNumber"); |
|||
} |
|||
|
|||
if (solutionVector == null) |
|||
{ |
|||
throw new ArgumentNullException("solutionVector"); |
|||
} |
|||
|
|||
if (sourceVector == null) |
|||
{ |
|||
throw new ArgumentNullException("sourceVector"); |
|||
} |
|||
|
|||
if (residualVector == null) |
|||
{ |
|||
throw new ArgumentNullException("residualVector"); |
|||
} |
|||
|
|||
// While we're cancelled we don't call on the stop-criteria.
|
|||
if (_wasIterationCancelled) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
foreach (var stopCriterium in _stopCriterias.Select(pair => pair.Value)) |
|||
{ |
|||
stopCriterium.DetermineStatus(iterationNumber, solutionVector, sourceVector, residualVector); |
|||
var status = stopCriterium.Status; |
|||
|
|||
// Check if the status is:
|
|||
// - Running --> keep going
|
|||
// - Indetermined --> keep going
|
|||
// Anything else:
|
|||
// Stop looping and set that status
|
|||
if ((status is CalculationRunning) || (status is CalculationIndetermined)) |
|||
{ |
|||
continue; |
|||
} |
|||
|
|||
_status = status; |
|||
return; |
|||
} |
|||
|
|||
// Got all the way through
|
|||
// So we're running because we had vectors passed to us.
|
|||
if (!(_status is CalculationRunning)) |
|||
{ |
|||
_status = new CalculationRunning(); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the current calculation status.
|
|||
/// </summary>
|
|||
public ICalculationStatus Status |
|||
{ |
|||
get |
|||
{ |
|||
return _status; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Resets the <see cref="IIterator{T}"/> to the pre-calculation state.
|
|||
/// </summary>
|
|||
public void ResetToPrecalculationState() |
|||
{ |
|||
// Indicate that we're no longer cancelled.
|
|||
_wasIterationCancelled = false; |
|||
|
|||
// Reset the status.
|
|||
_status = DefaultStatus; |
|||
|
|||
// Reset the stop-criteria
|
|||
foreach (var stopCriterium in _stopCriterias.Select(pair => pair.Value)) |
|||
{ |
|||
stopCriterium.ResetToPrecalculationState(); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Creates a deep clone of the current iterator.
|
|||
/// </summary>
|
|||
/// <returns>The deep clone of the current iterator.</returns>
|
|||
public IIterator<float> Clone() |
|||
{ |
|||
var stopCriteria = _stopCriterias.Select(pair => pair.Value).Select(stopCriterium => (IIterationStopCriterium<float>)stopCriterium.Clone()).ToList(); |
|||
return new Iterator(stopCriteria); |
|||
} |
|||
|
|||
#if !SILVERLIGHT
|
|||
/// <summary>
|
|||
/// Creates a deep clone of the current iterator.
|
|||
/// </summary>
|
|||
/// <returns>The deep clone of the current iterator.</returns>
|
|||
object ICloneable.Clone() |
|||
{ |
|||
return Clone(); |
|||
} |
|||
#endif
|
|||
} |
|||
} |
|||
@ -0,0 +1,150 @@ |
|||
// <copyright file="Diagonal.cs" company="Math.NET">
|
|||
// Math.NET Numerics, part of the Math.NET Project
|
|||
// http://numerics.mathdotnet.com
|
|||
// http://github.com/mathnet/mathnet-numerics
|
|||
// http://mathnetnumerics.codeplex.com
|
|||
//
|
|||
// Copyright (c) 2009-2010 Math.NET
|
|||
//
|
|||
// Permission is hereby granted, free of charge, to any person
|
|||
// obtaining a copy of this software and associated documentation
|
|||
// files (the "Software"), to deal in the Software without
|
|||
// restriction, including without limitation the rights to use,
|
|||
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|||
// copies of the Software, and to permit persons to whom the
|
|||
// Software is furnished to do so, subject to the following
|
|||
// conditions:
|
|||
//
|
|||
// The above copyright notice and this permission notice shall be
|
|||
// included in all copies or substantial portions of the Software.
|
|||
//
|
|||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|||
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|||
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|||
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|||
// OTHER DEALINGS IN THE SOFTWARE.
|
|||
// </copyright>
|
|||
|
|||
namespace MathNet.Numerics.LinearAlgebra.Single.Solvers.Preconditioners |
|||
{ |
|||
using System; |
|||
using Generic; |
|||
using Generic.Solvers.Preconditioners; |
|||
using Properties; |
|||
|
|||
/// <summary>
|
|||
/// A diagonal preconditioner. The preconditioner uses the inverse
|
|||
/// of the matrix diagonal as preconditioning values.
|
|||
/// </summary>
|
|||
public sealed class Diagonal : IPreConditioner<float> |
|||
{ |
|||
/// <summary>
|
|||
/// The inverse of the matrix diagonal.
|
|||
/// </summary>
|
|||
private float[] _inverseDiagonals; |
|||
|
|||
/// <summary>
|
|||
/// Returns the decomposed matrix diagonal.
|
|||
/// </summary>
|
|||
/// <returns>The matrix diagonal.</returns>
|
|||
internal DiagonalMatrix DiagonalEntries() |
|||
{ |
|||
var result = new DiagonalMatrix(_inverseDiagonals.Length); |
|||
for (var i = 0; i < _inverseDiagonals.Length; i++) |
|||
{ |
|||
result[i, i] = 1 / _inverseDiagonals[i]; |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes the preconditioner and loads the internal data structures.
|
|||
/// </summary>
|
|||
/// <param name="matrix">
|
|||
/// The <see cref="Matrix{T}"/> upon which this preconditioner is based.</param>
|
|||
/// <exception cref="ArgumentNullException">If <paramref name="matrix"/> is <see langword="null" />. </exception>
|
|||
/// <exception cref="ArgumentException">If <paramref name="matrix"/> is not a square matrix.</exception>
|
|||
public void Initialize(Matrix<float> matrix) |
|||
{ |
|||
if (matrix == null) |
|||
{ |
|||
throw new ArgumentNullException("matrix"); |
|||
} |
|||
|
|||
if (matrix.RowCount != matrix.ColumnCount) |
|||
{ |
|||
throw new ArgumentException(Resources.ArgumentMatrixSquare, "matrix"); |
|||
} |
|||
|
|||
_inverseDiagonals = new float[matrix.RowCount]; |
|||
for (var i = 0; i < matrix.RowCount; i++) |
|||
{ |
|||
_inverseDiagonals[i] = 1 / matrix[i, i]; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Approximates the solution to the matrix equation <b>Ax = b</b>.
|
|||
/// </summary>
|
|||
/// <param name="rhs">The right hand side vector.</param>
|
|||
/// <returns>The left hand side vector.</returns>
|
|||
public Vector<float> Approximate(Vector<float> 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<float> result = new DenseVector(rhs.Count); |
|||
Approximate(rhs, result); |
|||
return result; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Approximates the solution to the matrix equation <b>Ax = b</b>.
|
|||
/// </summary>
|
|||
/// <param name="rhs">The right hand side vector.</param>
|
|||
/// <param name="lhs">The left hand side vector. Also known as the result vector.</param>
|
|||
public void Approximate(Vector<float> rhs, Vector<float> 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]; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,729 @@ |
|||
// <copyright file="Ilutp.cs" company="Math.NET">
|
|||
// Math.NET Numerics, part of the Math.NET Project
|
|||
// http://numerics.mathdotnet.com
|
|||
// http://github.com/mathnet/mathnet-numerics
|
|||
// http://mathnetnumerics.codeplex.com
|
|||
//
|
|||
// Copyright (c) 2009-2010 Math.NET
|
|||
//
|
|||
// Permission is hereby granted, free of charge, to any person
|
|||
// obtaining a copy of this software and associated documentation
|
|||
// files (the "Software"), to deal in the Software without
|
|||
// restriction, including without limitation the rights to use,
|
|||
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|||
// copies of the Software, and to permit persons to whom the
|
|||
// Software is furnished to do so, subject to the following
|
|||
// conditions:
|
|||
//
|
|||
// The above copyright notice and this permission notice shall be
|
|||
// included in all copies or substantial portions of the Software.
|
|||
//
|
|||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|||
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|||
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|||
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|||
// OTHER DEALINGS IN THE SOFTWARE.
|
|||
// </copyright>
|
|||
|
|||
namespace MathNet.Numerics.LinearAlgebra.Single.Solvers.Preconditioners |
|||
{ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using Generic; |
|||
using Generic.Solvers.Preconditioners; |
|||
using Properties; |
|||
|
|||
/// <summary>
|
|||
/// This class performs an Incomplete LU factorization with drop tolerance
|
|||
/// and partial pivoting. The drop tolerance indicates which additional entries
|
|||
/// will be dropped from the factorized LU matrices.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// The ILUTP-Mem algorithm was taken from: <br/>
|
|||
/// ILUTP_Mem: a Space-Efficient Incomplete LU Preconditioner
|
|||
/// <br/>
|
|||
/// Tzu-Yi Chen, Department of Mathematics and Computer Science, <br/>
|
|||
/// Pomona College, Claremont CA 91711, USA <br/>
|
|||
/// Published in: <br/>
|
|||
/// Lecture Notes in Computer Science <br/>
|
|||
/// Volume 3046 / 2004 <br/>
|
|||
/// pp. 20 - 28 <br/>
|
|||
/// Algorithm is described in Section 2, page 22
|
|||
/// </remarks>
|
|||
public sealed class Ilutp : IPreConditioner<float> |
|||
{ |
|||
/// <summary>
|
|||
/// The default fill level.
|
|||
/// </summary>
|
|||
public const double DefaultFillLevel = 200.0; |
|||
|
|||
/// <summary>
|
|||
/// The default drop tolerance.
|
|||
/// </summary>
|
|||
public const double DefaultDropTolerance = 0.0001; |
|||
|
|||
/// <summary>
|
|||
/// The decomposed upper triangular matrix.
|
|||
/// </summary>
|
|||
private SparseMatrix _upper; |
|||
|
|||
/// <summary>
|
|||
/// The decomposed lower triangular matrix.
|
|||
/// </summary>
|
|||
private SparseMatrix _lower; |
|||
|
|||
/// <summary>
|
|||
/// The array containing the pivot values.
|
|||
/// </summary>
|
|||
private int[] _pivots; |
|||
|
|||
/// <summary>
|
|||
/// The fill level.
|
|||
/// </summary>
|
|||
private double _fillLevel = DefaultFillLevel; |
|||
|
|||
/// <summary>
|
|||
/// The drop tolerance.
|
|||
/// </summary>
|
|||
private double _dropTolerance = DefaultDropTolerance; |
|||
|
|||
/// <summary>
|
|||
/// The pivot tolerance.
|
|||
/// </summary>
|
|||
private double _pivotTolerance; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="Ilutp"/> class with the default settings.
|
|||
/// </summary>
|
|||
public Ilutp() |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="Ilutp"/> class with the specified settings.
|
|||
/// </summary>
|
|||
/// <param name="fillLevel">
|
|||
/// The amount of fill that is allowed in the matrix. The value is a fraction of
|
|||
/// the number of non-zero entries in the original matrix. Values should be positive.
|
|||
/// </param>
|
|||
/// <param name="dropTolerance">
|
|||
/// The absolute drop tolerance which indicates below what absolute value an entry
|
|||
/// will be dropped from the matrix. A drop tolerance of 0.0 means that no values
|
|||
/// will be dropped. Values should always be positive.
|
|||
/// </param>
|
|||
/// <param name="pivotTolerance">
|
|||
/// The pivot tolerance which indicates at what level pivoting will take place. A
|
|||
/// value of 0.0 means that no pivoting will take place.
|
|||
/// </param>
|
|||
public Ilutp(double fillLevel, double dropTolerance, double pivotTolerance) |
|||
{ |
|||
if (fillLevel < 0) |
|||
{ |
|||
throw new ArgumentOutOfRangeException("fillLevel"); |
|||
} |
|||
|
|||
if (dropTolerance < 0) |
|||
{ |
|||
throw new ArgumentOutOfRangeException("dropTolerance"); |
|||
} |
|||
|
|||
if (pivotTolerance < 0) |
|||
{ |
|||
throw new ArgumentOutOfRangeException("pivotTolerance"); |
|||
} |
|||
|
|||
_fillLevel = fillLevel; |
|||
_dropTolerance = dropTolerance; |
|||
_pivotTolerance = pivotTolerance; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the amount of fill that is allowed in the matrix. The
|
|||
/// value is a fraction of the number of non-zero entries in the original
|
|||
/// matrix. The standard value is 200.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// <para>
|
|||
/// Values should always be positive and can be higher than 1.0. A value lower
|
|||
/// than 1.0 means that the eventual preconditioner matrix will have fewer
|
|||
/// non-zero entries as the original matrix. A value higher than 1.0 means that
|
|||
/// the eventual preconditioner can have more non-zero values than the original
|
|||
/// matrix.
|
|||
/// </para>
|
|||
/// <para>
|
|||
/// Note that any changes to the <b>FillLevel</b> after creating the preconditioner
|
|||
/// will invalidate the created preconditioner and will require a re-initialization of
|
|||
/// the preconditioner.
|
|||
/// </para>
|
|||
/// </remarks>
|
|||
/// <exception cref="ArgumentOutOfRangeException">Thrown if a negative value is provided.</exception>
|
|||
public double FillLevel |
|||
{ |
|||
get |
|||
{ |
|||
return _fillLevel; |
|||
} |
|||
|
|||
set |
|||
{ |
|||
if (value < 0) |
|||
{ |
|||
throw new ArgumentOutOfRangeException("Value"); |
|||
} |
|||
|
|||
_fillLevel = value; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the absolute drop tolerance which indicates below what absolute value
|
|||
/// an entry will be dropped from the matrix. The standard value is 0.0001.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// <para>
|
|||
/// The values should always be positive and can be larger than 1.0. A low value will
|
|||
/// keep more small numbers in the preconditioner matrix. A high value will remove
|
|||
/// more small numbers from the preconditioner matrix.
|
|||
/// </para>
|
|||
/// <para>
|
|||
/// Note that any changes to the <b>DropTolerance</b> after creating the preconditioner
|
|||
/// will invalidate the created preconditioner and will require a re-initialization of
|
|||
/// the preconditioner.
|
|||
/// </para>
|
|||
/// </remarks>
|
|||
/// <exception cref="ArgumentOutOfRangeException">Thrown if a negative value is provided.</exception>
|
|||
public double DropTolerance |
|||
{ |
|||
get |
|||
{ |
|||
return _dropTolerance; |
|||
} |
|||
|
|||
set |
|||
{ |
|||
if (value < 0) |
|||
{ |
|||
throw new ArgumentOutOfRangeException("Value"); |
|||
} |
|||
|
|||
_dropTolerance = value; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the pivot tolerance which indicates at what level pivoting will
|
|||
/// take place. The standard value is 0.0 which means pivoting will never take place.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// <para>
|
|||
/// The pivot tolerance is used to calculate if pivoting is necessary. Pivoting
|
|||
/// will take place if any of the values in a row is bigger than the
|
|||
/// diagonal value of that row divided by the pivot tolerance, i.e. pivoting
|
|||
/// will take place if <b>row(i,j) > row(i,i) / PivotTolerance</b> for
|
|||
/// any <b>j</b> that is not equal to <b>i</b>.
|
|||
/// </para>
|
|||
/// <para>
|
|||
/// Note that any changes to the <b>PivotTolerance</b> after creating the preconditioner
|
|||
/// will invalidate the created preconditioner and will require a re-initialization of
|
|||
/// the preconditioner.
|
|||
/// </para>
|
|||
/// </remarks>
|
|||
/// <exception cref="ArgumentOutOfRangeException">Thrown if a negative value is provided.</exception>
|
|||
public double PivotTolerance |
|||
{ |
|||
get |
|||
{ |
|||
return _pivotTolerance; |
|||
} |
|||
|
|||
set |
|||
{ |
|||
if (value < 0) |
|||
{ |
|||
throw new ArgumentOutOfRangeException("Value"); |
|||
} |
|||
|
|||
_pivotTolerance = value; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns the upper triagonal matrix that was created during the LU decomposition.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// This method is used for debugging purposes only and should normally not be used.
|
|||
/// </remarks>
|
|||
/// <returns>A new matrix containing the upper triagonal elements.</returns>
|
|||
internal Matrix<float> UpperTriangle() |
|||
{ |
|||
return _upper.Clone(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns the lower triagonal matrix that was created during the LU decomposition.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// This method is used for debugging purposes only and should normally not be used.
|
|||
/// </remarks>
|
|||
/// <returns>A new matrix containing the lower triagonal elements.</returns>
|
|||
internal Matrix<float> LowerTriangle() |
|||
{ |
|||
return _lower.Clone(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns the pivot array. This array is not needed for normal use because
|
|||
/// the preconditioner will return the solution vector values in the proper order.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// This method is used for debugging purposes only and should normally not be used.
|
|||
/// </remarks>
|
|||
/// <returns>The pivot array.</returns>
|
|||
internal int[] Pivots() |
|||
{ |
|||
var result = new int[_pivots.Length]; |
|||
for (var i = 0; i < _pivots.Length; i++) |
|||
{ |
|||
result[i] = _pivots[i]; |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes the preconditioner and loads the internal data structures.
|
|||
/// </summary>
|
|||
/// <param name="matrix">
|
|||
/// The <see cref="Matrix{T}"/> upon which this preconditioner is based. Note that the
|
|||
/// method takes a general matrix type. However internally the data is stored
|
|||
/// as a sparse matrix. Therefore it is not recommended to pass a dense matrix.
|
|||
/// </param>
|
|||
/// <exception cref="ArgumentNullException"> If <paramref name="matrix"/> is <see langword="null" />.</exception>
|
|||
/// <exception cref="ArgumentException">If <paramref name="matrix"/> is not a square matrix.</exception>
|
|||
public void Initialize(Matrix<float> 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<float> workVector = new DenseVector(sparseMatrix.RowCount); |
|||
Vector<float> 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.0f; |
|||
} |
|||
|
|||
// 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.0f; |
|||
} |
|||
|
|||
rowVector.Multiply(workVector[j], rowVector); |
|||
workVector.Subtract(rowVector, workVector); |
|||
} |
|||
} |
|||
} |
|||
|
|||
// for j = i, .. ,n
|
|||
for (var j = i; j < sparseMatrix.RowCount; j++) |
|||
{ |
|||
// if w(j) <= dropTol * ||A(i,*)||
|
|||
// {
|
|||
// w(j) = 0
|
|||
// }
|
|||
if (Math.Abs(workVector[j]) <= _dropTolerance * vectorNorm) |
|||
{ |
|||
workVector[j] = 0.0f; |
|||
} |
|||
} |
|||
|
|||
// 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.0f; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Pivot elements in the <paramref name="row"/> according to internal pivot array
|
|||
/// </summary>
|
|||
/// <param name="row">Row <see cref="Vector{T}"/> to pivot in</param>
|
|||
private void PivotRow(Vector<float> row) |
|||
{ |
|||
var knownPivots = new Dictionary<int, int>(); |
|||
|
|||
// pivot the row
|
|||
for (var i = 0; i < row.Count; i++) |
|||
{ |
|||
if ((_pivots[i] != i) && (!PivotMapFound(knownPivots, i))) |
|||
{ |
|||
// store the pivots in the hashtable
|
|||
knownPivots.Add(_pivots[i], i); |
|||
|
|||
var t = row[i]; |
|||
row[i] = row[_pivots[i]]; |
|||
row[_pivots[i]] = t; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Was pivoting already performed
|
|||
/// </summary>
|
|||
/// <param name="knownPivots">Pivots already done</param>
|
|||
/// <param name="currentItem">Current item to pivot</param>
|
|||
/// <returns><c>true</c> if performed, otherwise <c>false</c></returns>
|
|||
private bool PivotMapFound(Dictionary<int, int> knownPivots, int currentItem) |
|||
{ |
|||
if (knownPivots.ContainsKey(_pivots[currentItem])) |
|||
{ |
|||
if (knownPivots[_pivots[currentItem]].Equals(currentItem)) |
|||
{ |
|||
return true; |
|||
} |
|||
} |
|||
|
|||
if (knownPivots.ContainsKey(currentItem)) |
|||
{ |
|||
if (knownPivots[currentItem].Equals(_pivots[currentItem])) |
|||
{ |
|||
return true; |
|||
} |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Swap columns in the <see cref="Matrix{T}"/>
|
|||
/// </summary>
|
|||
/// <param name="matrix">Source <see cref="Matrix{T}"/>.</param>
|
|||
/// <param name="firstColumn">First column index to swap</param>
|
|||
/// <param name="secondColumn">Second column index to swap</param>
|
|||
private static void SwapColumns(Matrix<float> matrix, int firstColumn, int secondColumn) |
|||
{ |
|||
for (var i = 0; i < matrix.RowCount; i++) |
|||
{ |
|||
var temp = matrix[i, firstColumn]; |
|||
matrix[i, firstColumn] = matrix[i, secondColumn]; |
|||
matrix[i, secondColumn] = temp; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sort vector descending, not changing vector but placing sorted indicies to <paramref name="sortedIndices"/>
|
|||
/// </summary>
|
|||
/// <param name="lowerBound">Start sort form</param>
|
|||
/// <param name="upperBound">Sort till upper bound</param>
|
|||
/// <param name="sortedIndices">Array with sorted vector indicies</param>
|
|||
/// <param name="values">Source <see cref="Vector{T}"/></param>
|
|||
private static void FindLargestItems(int lowerBound, int upperBound, int[] sortedIndices, Vector<float> values) |
|||
{ |
|||
// Copy the indices for the values into the array
|
|||
for (var i = 0; i < upperBound + 1 - lowerBound; i++) |
|||
{ |
|||
sortedIndices[i] = lowerBound + i; |
|||
} |
|||
|
|||
for (var i = upperBound + 1 - lowerBound; i < sortedIndices.Length; i++) |
|||
{ |
|||
sortedIndices[i] = -1; |
|||
} |
|||
|
|||
// Sort the first set of items.
|
|||
// Sorting starts at index 0 because the index array
|
|||
// starts at zero
|
|||
// and ends at index upperBound - lowerBound
|
|||
IlutpElementSorter.SortDoubleIndicesDecreasing(0, upperBound - lowerBound, sortedIndices, values); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Approximates the solution to the matrix equation <b>Ax = b</b>.
|
|||
/// </summary>
|
|||
/// <param name="rhs">The right hand side vector.</param>
|
|||
/// <returns>The left hand side vector.</returns>
|
|||
public Vector<float> Approximate(Vector<float> 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<float> result = new DenseVector(rhs.Count); |
|||
Approximate(rhs, result); |
|||
return result; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Approximates the solution to the matrix equation <b>Ax = b</b>.
|
|||
/// </summary>
|
|||
/// <param name="rhs">The right hand side vector.</param>
|
|||
/// <param name="lhs">The left hand side vector. Also known as the result vector.</param>
|
|||
public void Approximate(Vector<float> rhs, Vector<float> 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<float> rowValues = new DenseVector(_lower.RowCount); |
|||
for (var i = 0; i < _lower.RowCount; i++) |
|||
{ |
|||
_lower.Row(i, rowValues); |
|||
|
|||
var sum = 0.0f; |
|||
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.0f; |
|||
for (var j = _upper.RowCount - 1; j > i; j--) |
|||
{ |
|||
sum += rowValues[j] * lhs[j]; |
|||
} |
|||
|
|||
lhs[i] = 1 / rowValues[i] * (lhs[i] - sum); |
|||
} |
|||
|
|||
// We have a column pivot so we only need to pivot the
|
|||
// end result not the incoming right hand side vector
|
|||
var temp = lhs.Clone(); |
|||
|
|||
Pivot(temp, lhs); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Pivot elements in <see cref="Vector{T}"/> accoring to internal pivot array
|
|||
/// </summary>
|
|||
/// <param name="vector">Source <see cref="Vector{T}"/>.</param>
|
|||
/// <param name="result">Result <see cref="Vector{T}"/> after pivoting.</param>
|
|||
private void Pivot(Vector<float> vector, Vector<float> result) |
|||
{ |
|||
for (var i = 0; i < _pivots.Length; i++) |
|||
{ |
|||
result[i] = vector[_pivots[i]]; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,227 @@ |
|||
// <copyright file="IlutpElementSorter.cs" company="Math.NET">
|
|||
// Math.NET Numerics, part of the Math.NET Project
|
|||
// http://numerics.mathdotnet.com
|
|||
// http://github.com/mathnet/mathnet-numerics
|
|||
// http://mathnetnumerics.codeplex.com
|
|||
//
|
|||
// Copyright (c) 2009-2010 Math.NET
|
|||
//
|
|||
// Permission is hereby granted, free of charge, to any person
|
|||
// obtaining a copy of this software and associated documentation
|
|||
// files (the "Software"), to deal in the Software without
|
|||
// restriction, including without limitation the rights to use,
|
|||
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|||
// copies of the Software, and to permit persons to whom the
|
|||
// Software is furnished to do so, subject to the following
|
|||
// conditions:
|
|||
//
|
|||
// The above copyright notice and this permission notice shall be
|
|||
// included in all copies or substantial portions of the Software.
|
|||
//
|
|||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|||
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|||
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|||
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|||
// OTHER DEALINGS IN THE SOFTWARE.
|
|||
// </copyright>
|
|||
|
|||
namespace MathNet.Numerics.LinearAlgebra.Single.Solvers.Preconditioners |
|||
{ |
|||
using Generic; |
|||
|
|||
/// <summary>
|
|||
/// An element sort algorithm for the <see cref="Ilutp"/> class.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// This sort algorithm is used to sort the columns in a sparse matrix based on
|
|||
/// the value of the element on the diagonal of the matrix.
|
|||
/// </remarks>
|
|||
internal class IlutpElementSorter |
|||
{ |
|||
/// <summary>
|
|||
/// Sorts the elements of the <paramref name="values"/> vector in decreasing
|
|||
/// fashion. The vector itself is not affected.
|
|||
/// </summary>
|
|||
/// <param name="lowerBound">The starting index.</param>
|
|||
/// <param name="upperBound">The stopping index.</param>
|
|||
/// <param name="sortedIndices">An array that will contain the sorted indices once the algorithm finishes.</param>
|
|||
/// <param name="values">The <see cref="Vector{T}"/> that contains the values that need to be sorted.</param>
|
|||
public static void SortDoubleIndicesDecreasing(int lowerBound, int upperBound, int[] sortedIndices, Vector<float> values) |
|||
{ |
|||
// Move all the indices that we're interested in to the beginning of the
|
|||
// array. Ignore the rest of the indices.
|
|||
if (lowerBound > 0) |
|||
{ |
|||
for (var i = 0; i < (upperBound - lowerBound + 1); i++) |
|||
{ |
|||
Exchange(sortedIndices, i, i + lowerBound); |
|||
} |
|||
|
|||
upperBound -= lowerBound; |
|||
lowerBound = 0; |
|||
} |
|||
|
|||
HeapSortDoublesIndices(lowerBound, upperBound, sortedIndices, values); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sorts the elements of the <paramref name="values"/> vector in decreasing
|
|||
/// fashion using heap sort algorithm. The vector itself is not affected.
|
|||
/// </summary>
|
|||
/// <param name="lowerBound">The starting index.</param>
|
|||
/// <param name="upperBound">The stopping index.</param>
|
|||
/// <param name="sortedIndices">An array that will contain the sorted indices once the algorithm finishes.</param>
|
|||
/// <param name="values">The <see cref="Vector{T}"/> that contains the values that need to be sorted.</param>
|
|||
private static void HeapSortDoublesIndices(int lowerBound, int upperBound, int[] sortedIndices, Vector<float> values) |
|||
{ |
|||
var start = ((upperBound - lowerBound + 1) / 2) - 1 + lowerBound; |
|||
var end = (upperBound - lowerBound + 1) - 1 + lowerBound; |
|||
|
|||
BuildDoubleIndexHeap(start, upperBound - lowerBound + 1, sortedIndices, values); |
|||
|
|||
while (end >= lowerBound) |
|||
{ |
|||
Exchange(sortedIndices, end, lowerBound); |
|||
SiftDoubleIndices(sortedIndices, values, lowerBound, end); |
|||
end -= 1; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Build heap for double indicies
|
|||
/// </summary>
|
|||
/// <param name="start">Root position</param>
|
|||
/// <param name="count">Lenght of <paramref name="values"/></param>
|
|||
/// <param name="sortedIndices">Indicies of <paramref name="values"/></param>
|
|||
/// <param name="values">Target <see cref="Vector{T}"/></param>
|
|||
private static void BuildDoubleIndexHeap(int start, int count, int[] sortedIndices, Vector<float> values) |
|||
{ |
|||
while (start >= 0) |
|||
{ |
|||
SiftDoubleIndices(sortedIndices, values, start, count); |
|||
start -= 1; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sift double indicies
|
|||
/// </summary>
|
|||
/// <param name="sortedIndices">Indicies of <paramref name="values"/></param>
|
|||
/// <param name="values">Target <see cref="Vector{T}"/></param>
|
|||
/// <param name="begin">Root position</param>
|
|||
/// <param name="count">Lenght of <paramref name="values"/></param>
|
|||
private static void SiftDoubleIndices(int[] sortedIndices, Vector<float> 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; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sorts the given integers in a decreasing fashion.
|
|||
/// </summary>
|
|||
/// <param name="values">The values.</param>
|
|||
public static void SortIntegersDecreasing(int[] values) |
|||
{ |
|||
HeapSortIntegers(values, values.Length); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sort the given integers in a decreasing fashion using heapsort algorithm
|
|||
/// </summary>
|
|||
/// <param name="values">Array of values to sort</param>
|
|||
/// <param name="count">Length of <paramref name="values"/></param>
|
|||
private static void HeapSortIntegers(int[] values, int count) |
|||
{ |
|||
var start = (count / 2) - 1; |
|||
var end = count - 1; |
|||
|
|||
BuildHeap(values, start, count); |
|||
|
|||
while (end >= 0) |
|||
{ |
|||
Exchange(values, end, 0); |
|||
Sift(values, 0, end); |
|||
end -= 1; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Build heap
|
|||
/// </summary>
|
|||
/// <param name="values">Target values array</param>
|
|||
/// <param name="start">Root position</param>
|
|||
/// <param name="count">Lenght of <paramref name="values"/></param>
|
|||
private static void BuildHeap(int[] values, int start, int count) |
|||
{ |
|||
while (start >= 0) |
|||
{ |
|||
Sift(values, start, count); |
|||
start -= 1; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sift values
|
|||
/// </summary>
|
|||
/// <param name="values">Target value array</param>
|
|||
/// <param name="start">Root position</param>
|
|||
/// <param name="count">Lenght of <paramref name="values"/></param>
|
|||
private static void Sift(int[] values, int start, int count) |
|||
{ |
|||
var root = start; |
|||
int child; |
|||
|
|||
while (root * 2 < count) |
|||
{ |
|||
child = root * 2; |
|||
if ((child < count - 1) && (values[child] > values[child + 1])) |
|||
{ |
|||
child += 1; |
|||
} |
|||
|
|||
if (values[root] > values[child]) |
|||
{ |
|||
Exchange(values, root, child); |
|||
root = child; |
|||
} |
|||
else |
|||
{ |
|||
return; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Exchange values in array
|
|||
/// </summary>
|
|||
/// <param name="values">Target values array</param>
|
|||
/// <param name="first">First value to exchanghe</param>
|
|||
/// <param name="second">Second value to exchanghe</param>
|
|||
private static void Exchange(int[] values, int first, int second) |
|||
{ |
|||
var t = values[first]; |
|||
values[first] = values[second]; |
|||
values[second] = t; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,260 @@ |
|||
// <copyright file="IncompleteLU.cs" company="Math.NET">
|
|||
// Math.NET Numerics, part of the Math.NET Project
|
|||
// http://numerics.mathdotnet.com
|
|||
// http://github.com/mathnet/mathnet-numerics
|
|||
// http://mathnetnumerics.codeplex.com
|
|||
//
|
|||
// Copyright (c) 2009-2010 Math.NET
|
|||
//
|
|||
// Permission is hereby granted, free of charge, to any person
|
|||
// obtaining a copy of this software and associated documentation
|
|||
// files (the "Software"), to deal in the Software without
|
|||
// restriction, including without limitation the rights to use,
|
|||
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|||
// copies of the Software, and to permit persons to whom the
|
|||
// Software is furnished to do so, subject to the following
|
|||
// conditions:
|
|||
//
|
|||
// The above copyright notice and this permission notice shall be
|
|||
// included in all copies or substantial portions of the Software.
|
|||
//
|
|||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|||
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|||
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|||
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|||
// OTHER DEALINGS IN THE SOFTWARE.
|
|||
// </copyright>
|
|||
|
|||
namespace MathNet.Numerics.LinearAlgebra.Single.Solvers.Preconditioners |
|||
{ |
|||
using System; |
|||
using Generic; |
|||
using Generic.Solvers.Preconditioners; |
|||
using Properties; |
|||
|
|||
/// <summary>
|
|||
/// An incomplete, level 0, LU factorization preconditioner.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// The ILU(0) algorithm was taken from: <br/>
|
|||
/// Iterative methods for sparse linear systems <br/>
|
|||
/// Yousef Saad <br/>
|
|||
/// Algorithm is described in Chapter 10, section 10.3.2, page 275 <br/>
|
|||
/// </remarks>
|
|||
public sealed class IncompleteLU : IPreConditioner<float> |
|||
{ |
|||
/// <summary>
|
|||
/// The matrix holding the lower (L) and upper (U) matrices. The
|
|||
/// decomposition matrices are combined to reduce storage.
|
|||
/// </summary>
|
|||
private SparseMatrix _decompositionLU; |
|||
|
|||
/// <summary>
|
|||
/// Returns the upper triagonal matrix that was created during the LU decomposition.
|
|||
/// </summary>
|
|||
/// <returns>A new matrix containing the upper triagonal elements.</returns>
|
|||
internal Matrix<float> UpperTriangle() |
|||
{ |
|||
var result = new SparseMatrix(_decompositionLU.RowCount); |
|||
for (var i = 0; i < _decompositionLU.RowCount; i++) |
|||
{ |
|||
for (var j = i; j < _decompositionLU.ColumnCount; j++) |
|||
{ |
|||
result[i, j] = _decompositionLU[i, j]; |
|||
} |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns the lower triagonal matrix that was created during the LU decomposition.
|
|||
/// </summary>
|
|||
/// <returns>A new matrix containing the lower triagonal elements.</returns>
|
|||
internal Matrix<float> 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.0f; |
|||
} |
|||
else |
|||
{ |
|||
result[i, j] = _decompositionLU[i, j]; |
|||
} |
|||
} |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes the preconditioner and loads the internal data structures.
|
|||
/// </summary>
|
|||
/// <param name="matrix">The matrix upon which the preconditioner is based. </param>
|
|||
/// <exception cref="ArgumentNullException">If <paramref name="matrix"/> is <see langword="null" />.</exception>
|
|||
/// <exception cref="ArgumentException">If <paramref name="matrix"/> is not a square matrix.</exception>
|
|||
public void Initialize(Matrix<float> matrix) |
|||
{ |
|||
if (matrix == null) |
|||
{ |
|||
throw new ArgumentNullException("matrix"); |
|||
} |
|||
|
|||
if (matrix.RowCount != matrix.ColumnCount) |
|||
{ |
|||
throw new ArgumentException(Resources.ArgumentMatrixSquare, "matrix"); |
|||
} |
|||
|
|||
_decompositionLU = new SparseMatrix(matrix.ToArray()); |
|||
|
|||
// M == A
|
|||
// for i = 2, ... , n do
|
|||
// for k = 1, .... , i - 1 do
|
|||
// if (i,k) == NZ(Z) then
|
|||
// compute z(i,k) = z(i,k) / z(k,k);
|
|||
// for j = k + 1, ...., n do
|
|||
// if (i,j) == NZ(Z) then
|
|||
// compute z(i,j) = z(i,j) - z(i,k) * z(k,j)
|
|||
// end
|
|||
// end
|
|||
// end
|
|||
// end
|
|||
// end
|
|||
for (var i = 0; i < _decompositionLU.RowCount; i++) |
|||
{ |
|||
for (var k = 0; k < i; k++) |
|||
{ |
|||
if (_decompositionLU[i, k] != 0.0) |
|||
{ |
|||
var t = _decompositionLU[i, k] / _decompositionLU[k, k]; |
|||
_decompositionLU[i, k] = t; |
|||
if (_decompositionLU[k, i] != 0.0) |
|||
{ |
|||
_decompositionLU[i, i] = _decompositionLU[i, i] - (t * _decompositionLU[k, i]); |
|||
} |
|||
|
|||
for (var j = k + 1; j < _decompositionLU.RowCount; j++) |
|||
{ |
|||
if (j == i) |
|||
{ |
|||
continue; |
|||
} |
|||
|
|||
if (_decompositionLU[i, j] != 0.0) |
|||
{ |
|||
_decompositionLU[i, j] = _decompositionLU[i, j] - (t * _decompositionLU[k, j]); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Approximates the solution to the matrix equation <b>Ax = b</b>.
|
|||
/// </summary>
|
|||
/// <param name="rhs">The right hand side vector.</param>
|
|||
/// <returns>The left hand side vector.</returns>
|
|||
public Vector<float> Approximate(Vector<float> 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<float> result = new DenseVector(rhs.Count); |
|||
Approximate(rhs, result); |
|||
return result; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Approximates the solution to the matrix equation <b>Ax = b</b>.
|
|||
/// </summary>
|
|||
/// <param name="rhs">The right hand side vector.</param>
|
|||
/// <param name="lhs">The left hand side vector. Also known as the result vector.</param>
|
|||
public void Approximate(Vector<float> rhs, Vector<float> lhs) |
|||
{ |
|||
if (rhs == null) |
|||
{ |
|||
throw new ArgumentNullException("rhs"); |
|||
} |
|||
|
|||
if (lhs == null) |
|||
{ |
|||
throw new ArgumentNullException("lhs"); |
|||
} |
|||
|
|||
if (_decompositionLU == null) |
|||
{ |
|||
throw new ArgumentException(Resources.ArgumentMatrixDoesNotExist); |
|||
} |
|||
|
|||
if ((lhs.Count != rhs.Count) || (lhs.Count != _decompositionLU.RowCount)) |
|||
{ |
|||
throw new ArgumentException(Resources.ArgumentVectorsSameLength); |
|||
} |
|||
|
|||
// Solve:
|
|||
// Lz = y
|
|||
// Which gives
|
|||
// for (int i = 1; i < matrix.RowLength; i++)
|
|||
// {
|
|||
// z_i = l_ii^-1 * (y_i - SUM_(j<i) l_ij * z_j)
|
|||
// }
|
|||
// NOTE: l_ii should be 1 because u_ii has to be the value
|
|||
Vector<float> rowValues = new DenseVector(_decompositionLU.RowCount); |
|||
for (var i = 0; i < _decompositionLU.RowCount; i++) |
|||
{ |
|||
// Clear the rowValues
|
|||
rowValues.Clear(); |
|||
_decompositionLU.Row(i, rowValues); |
|||
|
|||
var sum = 0.0f; |
|||
for (var j = 0; j < i; j++) |
|||
{ |
|||
sum += rowValues[j] * lhs[j]; |
|||
} |
|||
|
|||
lhs[i] = rhs[i] - sum; |
|||
} |
|||
|
|||
// Solve:
|
|||
// Ux = z
|
|||
// Which gives
|
|||
// for (int i = matrix.RowLength - 1; i > -1; i--)
|
|||
// {
|
|||
// x_i = u_ii^-1 * (z_i - SUM_(j > i) u_ij * x_j)
|
|||
// }
|
|||
for (var i = _decompositionLU.RowCount - 1; i > -1; i--) |
|||
{ |
|||
_decompositionLU.Row(i, rowValues); |
|||
|
|||
var sum = 0.0f; |
|||
for (var j = _decompositionLU.RowCount - 1; j > i; j--) |
|||
{ |
|||
sum += rowValues[j] * lhs[j]; |
|||
} |
|||
|
|||
lhs[i] = 1 / rowValues[i] * (lhs[i] - sum); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,139 @@ |
|||
// <copyright file="UnitPreconditioner.cs" company="Math.NET">
|
|||
// Math.NET Numerics, part of the Math.NET Project
|
|||
// http://numerics.mathdotnet.com
|
|||
// http://github.com/mathnet/mathnet-numerics
|
|||
// http://mathnetnumerics.codeplex.com
|
|||
//
|
|||
// Copyright (c) 2009-2010 Math.NET
|
|||
//
|
|||
// Permission is hereby granted, free of charge, to any person
|
|||
// obtaining a copy of this software and associated documentation
|
|||
// files (the "Software"), to deal in the Software without
|
|||
// restriction, including without limitation the rights to use,
|
|||
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|||
// copies of the Software, and to permit persons to whom the
|
|||
// Software is furnished to do so, subject to the following
|
|||
// conditions:
|
|||
//
|
|||
// The above copyright notice and this permission notice shall be
|
|||
// included in all copies or substantial portions of the Software.
|
|||
//
|
|||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|||
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|||
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|||
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|||
// OTHER DEALINGS IN THE SOFTWARE.
|
|||
// </copyright>
|
|||
|
|||
namespace MathNet.Numerics.LinearAlgebra.Single.Solvers.Preconditioners |
|||
{ |
|||
using System; |
|||
using Generic; |
|||
using Generic.Solvers; |
|||
using Generic.Solvers.Preconditioners; |
|||
using Properties; |
|||
|
|||
/// <summary>
|
|||
/// A unit preconditioner. This preconditioner does not actually do anything
|
|||
/// it is only used when running an <see cref="IIterativeSolver{T}"/> without
|
|||
/// a preconditioner.
|
|||
/// </summary>
|
|||
internal sealed class UnitPreconditioner : IPreConditioner<float> |
|||
{ |
|||
/// <summary>
|
|||
/// The coefficient matrix on which this preconditioner operates.
|
|||
/// Is used to check dimensions on the different vectors that are processed.
|
|||
/// </summary>
|
|||
private int _size; |
|||
|
|||
/// <summary>
|
|||
/// Initializes the preconditioner and loads the internal data structures.
|
|||
/// </summary>
|
|||
/// <param name="matrix">
|
|||
/// The matrix upon which the preconditioner is based.
|
|||
/// </param>
|
|||
/// <exception cref="ArgumentNullException">If <paramref name="matrix"/> is <see langword="null"/>. </exception>
|
|||
/// <exception cref="ArgumentException">If <paramref name="matrix"/> is not a square matrix.</exception>
|
|||
public void Initialize(Matrix<float> matrix) |
|||
{ |
|||
if (matrix == null) |
|||
{ |
|||
throw new ArgumentNullException("matrix"); |
|||
} |
|||
|
|||
if (matrix.RowCount != matrix.ColumnCount) |
|||
{ |
|||
throw new ArgumentException(Resources.ArgumentMatrixSquare, "matrix"); |
|||
} |
|||
|
|||
_size = matrix.RowCount; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Approximates the solution to the matrix equation <b>Ax = b</b>.
|
|||
/// </summary>
|
|||
/// <param name="rhs">The right hand side vector.</param>
|
|||
/// <param name="lhs">The left hand side vector. Also known as the result vector.</param>
|
|||
/// <exception cref="ArgumentNullException">If <paramref name="rhs"/> is <see langword="null"/>.</exception>
|
|||
/// <exception cref="ArgumentNullException">If <paramref name="lhs"/> is <see langword="null"/>. </exception>
|
|||
/// <exception cref="ArgumentException">
|
|||
/// <para>
|
|||
/// If <paramref name="rhs"/> and <paramref name="lhs"/> do not have the same size.
|
|||
/// </para>
|
|||
/// <para>
|
|||
/// - or -
|
|||
/// </para>
|
|||
/// <para>
|
|||
/// If the size of <paramref name="rhs"/> is different the number of rows of the coefficient matrix.
|
|||
/// </para>
|
|||
/// </exception>
|
|||
public void Approximate(Vector<float> rhs, Vector<float> lhs) |
|||
{ |
|||
if (rhs == null) |
|||
{ |
|||
throw new ArgumentNullException("rhs"); |
|||
} |
|||
|
|||
if (lhs == null) |
|||
{ |
|||
throw new ArgumentNullException("lhs"); |
|||
} |
|||
|
|||
if ((lhs.Count != rhs.Count) || (lhs.Count != _size)) |
|||
{ |
|||
throw new ArgumentException(Resources.ArgumentVectorsSameLength); |
|||
} |
|||
|
|||
rhs.CopyTo(lhs); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Approximates the solution to the matrix equation <b>Ax = b</b>.
|
|||
/// </summary>
|
|||
/// <param name="rhs">The right hand side vector.</param>
|
|||
/// <returns>The left hand side vector.</returns>
|
|||
/// <exception cref="ArgumentNullException">If <paramref name="rhs"/> is <see langword="null"/>. </exception>
|
|||
/// <exception cref="ArgumentException">
|
|||
/// If the size of <paramref name="rhs"/> is different the number of rows of the coefficient matrix.
|
|||
/// </exception>
|
|||
public Vector<float> Approximate(Vector<float> rhs) |
|||
{ |
|||
if (rhs == null) |
|||
{ |
|||
throw new ArgumentNullException("rhs"); |
|||
} |
|||
|
|||
if (rhs.Count != _size) |
|||
{ |
|||
throw new ArgumentException(Resources.ArgumentMatrixDimensions); |
|||
} |
|||
|
|||
Vector<float> result = new DenseVector(rhs.Count); |
|||
Approximate(rhs, result); |
|||
return result; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,393 @@ |
|||
// <copyright file="DivergenceStopCriterium.cs" company="Math.NET">
|
|||
// Math.NET Numerics, part of the Math.NET Project
|
|||
// http://numerics.mathdotnet.com
|
|||
// http://github.com/mathnet/mathnet-numerics
|
|||
// http://mathnetnumerics.codeplex.com
|
|||
//
|
|||
// Copyright (c) 2009-2010 Math.NET
|
|||
//
|
|||
// Permission is hereby granted, free of charge, to any person
|
|||
// obtaining a copy of this software and associated documentation
|
|||
// files (the "Software"), to deal in the Software without
|
|||
// restriction, including without limitation the rights to use,
|
|||
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|||
// copies of the Software, and to permit persons to whom the
|
|||
// Software is furnished to do so, subject to the following
|
|||
// conditions:
|
|||
//
|
|||
// The above copyright notice and this permission notice shall be
|
|||
// included in all copies or substantial portions of the Software.
|
|||
//
|
|||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|||
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|||
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|||
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|||
// OTHER DEALINGS IN THE SOFTWARE.
|
|||
// </copyright>
|
|||
|
|||
namespace MathNet.Numerics.LinearAlgebra.Single.Solvers.StopCriterium |
|||
{ |
|||
using System; |
|||
using System.Diagnostics; |
|||
using Generic; |
|||
using Generic.Solvers.Status; |
|||
using Generic.Solvers.StopCriterium; |
|||
|
|||
/// <summary>
|
|||
/// Monitors an iterative calculation for signs of divergence.
|
|||
/// </summary>
|
|||
public sealed class DivergenceStopCriterium : IIterationStopCriterium<float> |
|||
{ |
|||
/// <summary>
|
|||
/// Default value for the maximum relative increase that the
|
|||
/// residual may experience before a divergence warning is issued.
|
|||
/// </summary>
|
|||
public const double DefaultMaximumRelativeIncrease = 0.08; |
|||
|
|||
/// <summary>
|
|||
/// Default value for the minimum number of iterations over which
|
|||
/// the residual must grow before a divergence warning is issued.
|
|||
/// </summary>
|
|||
public const int DefaultMinimumNumberOfIterations = 10; |
|||
|
|||
/// <summary>
|
|||
/// Defines the default last iteration number. Set to -1 because iterations normally
|
|||
/// start at 0.
|
|||
/// </summary>
|
|||
private const int DefaultLastIterationNumber = -1; |
|||
|
|||
/// <summary>
|
|||
/// The default status.
|
|||
/// </summary>
|
|||
private static readonly ICalculationStatus DefaultStatus = new CalculationIndetermined(); |
|||
|
|||
/// <summary>
|
|||
/// The maximum relative increase the residual may experience without triggering a divergence warning.
|
|||
/// </summary>
|
|||
private double _maximumRelativeIncrease; |
|||
|
|||
/// <summary>
|
|||
/// The number of iterations over which a residual increase should be tracked before issuing a divergence warning.
|
|||
/// </summary>
|
|||
private int _minimumNumberOfIterations; |
|||
|
|||
/// <summary>
|
|||
/// The status of the calculation
|
|||
/// </summary>
|
|||
private ICalculationStatus _status = DefaultStatus; |
|||
|
|||
/// <summary>
|
|||
/// The array that holds the tracking information.
|
|||
/// </summary>
|
|||
private double[] _residualHistory; |
|||
|
|||
/// <summary>
|
|||
/// The iteration number of the last iteration.
|
|||
/// </summary>
|
|||
private int _lastIteration = DefaultLastIterationNumber; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="DivergenceStopCriterium"/> class with the default maximum
|
|||
/// relative increase and the default minimum number of tracking iterations.
|
|||
/// </summary>
|
|||
public DivergenceStopCriterium() : this(DefaultMaximumRelativeIncrease, DefaultMinimumNumberOfIterations) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="DivergenceStopCriterium"/> class with the specified maximum
|
|||
/// relative increase and the default minimum number of tracking iterations.
|
|||
/// </summary>
|
|||
/// <param name="maximumRelativeIncrease">The maximum relative increase that the residual may experience before a divergence warning is issued.</param>
|
|||
public DivergenceStopCriterium(double maximumRelativeIncrease) : this(maximumRelativeIncrease, DefaultMinimumNumberOfIterations) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="DivergenceStopCriterium"/> class with the default maximum
|
|||
/// relative increase and the specified minimum number of tracking iterations.
|
|||
/// </summary>
|
|||
/// <param name="minimumIterations">The minimum number of iterations over which the residual must grow before a divergence warning is issued. </param>
|
|||
public DivergenceStopCriterium(int minimumIterations) : this(DefaultMinimumNumberOfIterations, minimumIterations) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="DivergenceStopCriterium"/> class with the specified maximum
|
|||
/// relative increase and the specified minimum number of tracking iterations.
|
|||
/// </summary>
|
|||
/// <param name="maximumRelativeIncrease">The maximum relative increase that the residual may experience before a divergence warning is issued. </param>
|
|||
/// <param name="minimumIterations">The minimum number of iterations over which the residual must grow before a divergence warning is issued.</param>
|
|||
public DivergenceStopCriterium(double maximumRelativeIncrease, int minimumIterations) |
|||
{ |
|||
if (maximumRelativeIncrease <= 0) |
|||
{ |
|||
throw new ArgumentOutOfRangeException("maximumRelativeIncrease"); |
|||
} |
|||
|
|||
// There must be at least three iterations otherwise we can't calculate the relative increase
|
|||
if (minimumIterations < 3) |
|||
{ |
|||
throw new ArgumentOutOfRangeException("minimumIterations"); |
|||
} |
|||
|
|||
_maximumRelativeIncrease = maximumRelativeIncrease; |
|||
_minimumNumberOfIterations = minimumIterations; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the maximum relative increase that the residual may experience before a divergence warning is issued.
|
|||
/// </summary>
|
|||
/// <exception cref="ArgumentOutOfRangeException">Thrown if the <c>Maximum</c> is set to zero or below.</exception>
|
|||
public double MaximumRelativeIncrease |
|||
{ |
|||
[DebuggerStepThrough] |
|||
get |
|||
{ |
|||
return _maximumRelativeIncrease; |
|||
} |
|||
|
|||
[DebuggerStepThrough] |
|||
set |
|||
{ |
|||
if (value <= 0) |
|||
{ |
|||
throw new ArgumentOutOfRangeException("value"); |
|||
} |
|||
|
|||
_maximumRelativeIncrease = value; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns the maximum relative increase to the default.
|
|||
/// </summary>
|
|||
public void ResetMaximumRelativeIncreaseToDefault() |
|||
{ |
|||
_maximumRelativeIncrease = DefaultMaximumRelativeIncrease; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the minimum number of iterations over which the residual must grow before
|
|||
/// issuing a divergence warning.
|
|||
/// </summary>
|
|||
/// <exception cref="ArgumentOutOfRangeException">Thrown if the <c>value</c> is set to less than one.</exception>
|
|||
public int MinimumNumberOfIterations |
|||
{ |
|||
[DebuggerStepThrough] |
|||
get |
|||
{ |
|||
return _minimumNumberOfIterations; |
|||
} |
|||
|
|||
[DebuggerStepThrough] |
|||
set |
|||
{ |
|||
// There must be at least three iterations otherwise we can't calculate
|
|||
// the relative increase
|
|||
if (value < 3) |
|||
{ |
|||
throw new ArgumentOutOfRangeException("value"); |
|||
} |
|||
|
|||
_minimumNumberOfIterations = value; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns the minimum number of iterations to the default.
|
|||
/// </summary>
|
|||
public void ResetNumberOfIterationsToDefault() |
|||
{ |
|||
_minimumNumberOfIterations = DefaultMinimumNumberOfIterations; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Determines the status of the iterative calculation based on the stop criteria stored
|
|||
/// by the current <see cref="IIterationStopCriterium{T}"/>. Result is set into <c>Status</c> field.
|
|||
/// </summary>
|
|||
/// <param name="iterationNumber">The number of iterations that have passed so far.</param>
|
|||
/// <param name="solutionVector">The vector containing the current solution values.</param>
|
|||
/// <param name="sourceVector">The right hand side vector.</param>
|
|||
/// <param name="residualVector">The vector containing the current residual vectors.</param>
|
|||
/// <remarks>
|
|||
/// The individual stop criteria may internally track the progress of the calculation based
|
|||
/// on the invocation of this method. Therefore this method should only be called if the
|
|||
/// calculation has moved forwards at least one step.
|
|||
/// </remarks>
|
|||
public void DetermineStatus(int iterationNumber, Vector<float> solutionVector, Vector<float> sourceVector, Vector<float> residualVector) |
|||
{ |
|||
if (iterationNumber < 0) |
|||
{ |
|||
throw new ArgumentOutOfRangeException("iterationNumber"); |
|||
} |
|||
|
|||
if (residualVector == null) |
|||
{ |
|||
throw new ArgumentNullException("residualVector"); |
|||
} |
|||
|
|||
if (_lastIteration >= iterationNumber) |
|||
{ |
|||
// We have already stored the actual last iteration number
|
|||
// For now do nothing. We only care about the next step.
|
|||
return; |
|||
} |
|||
|
|||
if ((_residualHistory == null) || (_residualHistory.Length != RequiredHistoryLength)) |
|||
{ |
|||
_residualHistory = new double[RequiredHistoryLength]; |
|||
} |
|||
|
|||
// We always track the residual.
|
|||
// Move the old versions one element up in the array.
|
|||
for (var i = 1; i < _residualHistory.Length; i++) |
|||
{ |
|||
_residualHistory[i - 1] = _residualHistory[i]; |
|||
} |
|||
|
|||
// Store the infinity norms of both the solution and residual vectors
|
|||
// These values will be used to calculate the relative drop in residuals later on.
|
|||
_residualHistory[_residualHistory.Length - 1] = residualVector.Norm(Double.PositiveInfinity); |
|||
|
|||
// Check if we have NaN's. If so we've gone way beyond normal divergence.
|
|||
// Stop the iteration.
|
|||
if (double.IsNaN(_residualHistory[_residualHistory.Length - 1])) |
|||
{ |
|||
SetStatusToDiverged(); |
|||
return; |
|||
} |
|||
|
|||
// Check if we are diverging and if so set the status
|
|||
if (IsDiverging()) |
|||
{ |
|||
SetStatusToDiverged(); |
|||
} |
|||
else |
|||
{ |
|||
SetStatusToRunning(); |
|||
} |
|||
|
|||
_lastIteration = iterationNumber; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Detect if solution is diverging
|
|||
/// </summary>
|
|||
/// <returns><c>true</c> if diverging, otherwise <c>false</c></returns>
|
|||
private bool IsDiverging() |
|||
{ |
|||
// Run for each variable
|
|||
for (var i = 1; i < _residualHistory.Length; i++) |
|||
{ |
|||
var difference = _residualHistory[i] - _residualHistory[i - 1]; |
|||
|
|||
// Divergence is occurring if:
|
|||
// - the last residual is larger than the previous one
|
|||
// - the relative increase of the residual is larger than the setting allows
|
|||
if ((difference < 0) || (_residualHistory[i - 1] * (1 + _maximumRelativeIncrease) >= _residualHistory[i])) |
|||
{ |
|||
// No divergence taking place within the required number of iterations
|
|||
// So reset and stop the iteration. There is no way we can get to the
|
|||
// required number of iterations anymore.
|
|||
return false; |
|||
} |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets required history lenght
|
|||
/// </summary>
|
|||
private int RequiredHistoryLength |
|||
{ |
|||
[DebuggerStepThrough] |
|||
get |
|||
{ |
|||
return _minimumNumberOfIterations + 1; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Set status to <see cref="CalculationDiverged"/>
|
|||
/// </summary>
|
|||
private void SetStatusToDiverged() |
|||
{ |
|||
if (!(_status is CalculationDiverged)) |
|||
{ |
|||
_status = new CalculationDiverged(); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Set status to <see cref="CalculationRunning"/>
|
|||
/// </summary>
|
|||
private void SetStatusToRunning() |
|||
{ |
|||
if (!(_status is CalculationRunning)) |
|||
{ |
|||
_status = new CalculationRunning(); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the current calculation status.
|
|||
/// </summary>
|
|||
public ICalculationStatus Status |
|||
{ |
|||
[DebuggerStepThrough] |
|||
get |
|||
{ |
|||
return _status; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Resets the <see cref="IIterationStopCriterium{T}"/> to the pre-calculation state.
|
|||
/// </summary>
|
|||
public void ResetToPrecalculationState() |
|||
{ |
|||
_status = DefaultStatus; |
|||
_lastIteration = DefaultLastIterationNumber; |
|||
_residualHistory = null; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the <see cref="StopLevel"/> which indicates what sort of stop criterium this
|
|||
/// <see cref="IIterationStopCriterium{T}"/> monitors.
|
|||
/// </summary>
|
|||
/// <value>Returns <see cref="Generic.Solvers.StopCriterium.StopLevel.Divergence"/>.</value>
|
|||
public StopLevel StopLevel |
|||
{ |
|||
[DebuggerStepThrough] |
|||
get |
|||
{ |
|||
return StopLevel.Divergence; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Clones the current <see cref="DivergenceStopCriterium"/> and its settings.
|
|||
/// </summary>
|
|||
/// <returns>A new instance of the <see cref="DivergenceStopCriterium"/> class.</returns>
|
|||
public IIterationStopCriterium<float> Clone() |
|||
{ |
|||
return new DivergenceStopCriterium(_maximumRelativeIncrease, _minimumNumberOfIterations); |
|||
} |
|||
|
|||
#if !SILVERLIGHT
|
|||
/// <summary>
|
|||
/// Clone this object
|
|||
/// </summary>
|
|||
/// <returns>Object clone</returns>
|
|||
object ICloneable.Clone() |
|||
{ |
|||
return Clone(); |
|||
} |
|||
#endif
|
|||
} |
|||
} |
|||
@ -0,0 +1,201 @@ |
|||
// <copyright file="FailureStopCriterium.cs" company="Math.NET">
|
|||
// Math.NET Numerics, part of the Math.NET Project
|
|||
// http://numerics.mathdotnet.com
|
|||
// http://github.com/mathnet/mathnet-numerics
|
|||
// http://mathnetnumerics.codeplex.com
|
|||
//
|
|||
// Copyright (c) 2009-2010 Math.NET
|
|||
//
|
|||
// Permission is hereby granted, free of charge, to any person
|
|||
// obtaining a copy of this software and associated documentation
|
|||
// files (the "Software"), to deal in the Software without
|
|||
// restriction, including without limitation the rights to use,
|
|||
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|||
// copies of the Software, and to permit persons to whom the
|
|||
// Software is furnished to do so, subject to the following
|
|||
// conditions:
|
|||
//
|
|||
// The above copyright notice and this permission notice shall be
|
|||
// included in all copies or substantial portions of the Software.
|
|||
//
|
|||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|||
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|||
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|||
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|||
// OTHER DEALINGS IN THE SOFTWARE.
|
|||
// </copyright>
|
|||
|
|||
namespace MathNet.Numerics.LinearAlgebra.Single.Solvers.StopCriterium |
|||
{ |
|||
using System; |
|||
using System.Diagnostics; |
|||
using Generic; |
|||
using Generic.Solvers.Status; |
|||
using Generic.Solvers.StopCriterium; |
|||
using Properties; |
|||
|
|||
/// <summary>
|
|||
/// Defines an <see cref="IIterationStopCriterium{T}"/> that monitors residuals for NaN's.
|
|||
/// </summary>
|
|||
public sealed class FailureStopCriterium : IIterationStopCriterium<float> |
|||
{ |
|||
/// <summary>
|
|||
/// Defines the default last iteration number. Set to -1 because iterations normally
|
|||
/// start at 0.
|
|||
/// </summary>
|
|||
private const int DefaultLastIterationNumber = -1; |
|||
|
|||
/// <summary>
|
|||
/// The default status.
|
|||
/// </summary>
|
|||
private static readonly ICalculationStatus DefaultStatus = new CalculationIndetermined(); |
|||
|
|||
/// <summary>
|
|||
/// The status of the calculation
|
|||
/// </summary>
|
|||
private ICalculationStatus _status = DefaultStatus; |
|||
|
|||
/// <summary>
|
|||
/// The iteration number of the last iteration.
|
|||
/// </summary>
|
|||
private int _lastIteration = DefaultLastIterationNumber; |
|||
|
|||
/// <summary>
|
|||
/// Determines the status of the iterative calculation based on the stop criteria stored
|
|||
/// by the current <see cref="IIterationStopCriterium{T}"/>. Result is set into <c>Status</c> field.
|
|||
/// </summary>
|
|||
/// <param name="iterationNumber">The number of iterations that have passed so far.</param>
|
|||
/// <param name="solutionVector">The vector containing the current solution values.</param>
|
|||
/// <param name="sourceVector">The right hand side vector.</param>
|
|||
/// <param name="residualVector">The vector containing the current residual vectors.</param>
|
|||
/// <remarks>
|
|||
/// The individual stop criteria may internally track the progress of the calculation based
|
|||
/// on the invocation of this method. Therefore this method should only be called if the
|
|||
/// calculation has moved forwards at least one step.
|
|||
/// </remarks>
|
|||
public void DetermineStatus(int iterationNumber, Vector<float> solutionVector, Vector<float> sourceVector, Vector<float> residualVector) |
|||
{ |
|||
if (iterationNumber < 0) |
|||
{ |
|||
throw new ArgumentOutOfRangeException("iterationNumber"); |
|||
} |
|||
|
|||
if (solutionVector == null) |
|||
{ |
|||
throw new ArgumentNullException("solutionVector"); |
|||
} |
|||
|
|||
if (residualVector == null) |
|||
{ |
|||
throw new ArgumentNullException("residualVector"); |
|||
} |
|||
|
|||
if (solutionVector.Count != residualVector.Count) |
|||
{ |
|||
throw new ArgumentException(Resources.ArgumentArraysSameLength); |
|||
} |
|||
|
|||
if (_lastIteration >= iterationNumber) |
|||
{ |
|||
// We have already stored the actual last iteration number
|
|||
// For now do nothing. We only care about the next step.
|
|||
return; |
|||
} |
|||
|
|||
// Store the infinity norms of both the solution and residual vectors
|
|||
var residualNorm = residualVector.Norm(Double.PositiveInfinity); |
|||
var solutionNorm = solutionVector.Norm(Double.PositiveInfinity); |
|||
|
|||
if (Double.IsNaN(solutionNorm) || Double.IsNaN(residualNorm)) |
|||
{ |
|||
SetStatusToFailed(); |
|||
} |
|||
else |
|||
{ |
|||
SetStatusToRunning(); |
|||
} |
|||
|
|||
_lastIteration = iterationNumber; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Set status to <see cref="CalculationFailure"/>
|
|||
/// </summary>
|
|||
private void SetStatusToFailed() |
|||
{ |
|||
if (!(_status is CalculationFailure)) |
|||
{ |
|||
_status = new CalculationFailure(); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Set status to <see cref="CalculationRunning"/>
|
|||
/// </summary>
|
|||
private void SetStatusToRunning() |
|||
{ |
|||
if (!(_status is CalculationRunning)) |
|||
{ |
|||
_status = new CalculationRunning(); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the current calculation status.
|
|||
/// </summary>
|
|||
public ICalculationStatus Status |
|||
{ |
|||
[DebuggerStepThrough] |
|||
get |
|||
{ |
|||
return _status; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Resets the <see cref="IIterationStopCriterium{T}"/> to the pre-calculation state.
|
|||
/// </summary>
|
|||
public void ResetToPrecalculationState() |
|||
{ |
|||
_status = DefaultStatus; |
|||
_lastIteration = DefaultLastIterationNumber; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the <see cref="StopLevel"/>which indicates what sort of stop criterium this
|
|||
/// <see cref="IIterationStopCriterium{T}"/> monitors.
|
|||
/// </summary>
|
|||
/// <value>Returns <see cref="CalculationFailure"/>.</value>
|
|||
public StopLevel StopLevel |
|||
{ |
|||
[DebuggerStepThrough] |
|||
get |
|||
{ |
|||
return StopLevel.CalculationFailure; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Clones the current <see cref="FailureStopCriterium"/> and its settings.
|
|||
/// </summary>
|
|||
/// <returns>A new instance of the <see cref="FailureStopCriterium"/> class.</returns>
|
|||
public IIterationStopCriterium<float> Clone() |
|||
{ |
|||
return new FailureStopCriterium(); |
|||
} |
|||
|
|||
#if !SILVERLIGHT
|
|||
/// <summary>
|
|||
/// Clones the current <see cref="FailureStopCriterium"/> and its settings.
|
|||
/// </summary>
|
|||
/// <returns>A new instance of the <see cref="FailureStopCriterium"/> class.</returns>
|
|||
object ICloneable.Clone() |
|||
{ |
|||
return Clone(); |
|||
} |
|||
#endif
|
|||
} |
|||
} |
|||
@ -0,0 +1,227 @@ |
|||
// <copyright file="IterationCountStopCriterium.cs" company="Math.NET">
|
|||
// Math.NET Numerics, part of the Math.NET Project
|
|||
// http://numerics.mathdotnet.com
|
|||
// http://github.com/mathnet/mathnet-numerics
|
|||
// http://mathnetnumerics.codeplex.com
|
|||
//
|
|||
// Copyright (c) 2009-2010 Math.NET
|
|||
//
|
|||
// Permission is hereby granted, free of charge, to any person
|
|||
// obtaining a copy of this software and associated documentation
|
|||
// files (the "Software"), to deal in the Software without
|
|||
// restriction, including without limitation the rights to use,
|
|||
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|||
// copies of the Software, and to permit persons to whom the
|
|||
// Software is furnished to do so, subject to the following
|
|||
// conditions:
|
|||
//
|
|||
// The above copyright notice and this permission notice shall be
|
|||
// included in all copies or substantial portions of the Software.
|
|||
//
|
|||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|||
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|||
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|||
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|||
// OTHER DEALINGS IN THE SOFTWARE.
|
|||
// </copyright>
|
|||
|
|||
namespace MathNet.Numerics.LinearAlgebra.Single.Solvers.StopCriterium |
|||
{ |
|||
using System; |
|||
using System.Diagnostics; |
|||
using Generic; |
|||
using Generic.Solvers.Status; |
|||
using Generic.Solvers.StopCriterium; |
|||
|
|||
/// <summary>
|
|||
/// Defines an <see cref="IIterationStopCriterium{T}"/> that monitors the numbers of iteration
|
|||
/// steps as stop criterium.
|
|||
/// </summary>
|
|||
public sealed class IterationCountStopCriterium : IIterationStopCriterium<float> |
|||
{ |
|||
/// <summary>
|
|||
/// The default value for the maximum number of iterations the process is allowed
|
|||
/// to perform.
|
|||
/// </summary>
|
|||
public const int DefaultMaximumNumberOfIterations = 1000; |
|||
|
|||
/// <summary>
|
|||
/// The default status.
|
|||
/// </summary>
|
|||
private static readonly ICalculationStatus DefaultStatus = new CalculationIndetermined(); |
|||
|
|||
/// <summary>
|
|||
/// The maximum number of iterations the calculation is allowed to perform.
|
|||
/// </summary>
|
|||
private int _maximumNumberOfIterations; |
|||
|
|||
/// <summary>
|
|||
/// The status of the calculation
|
|||
/// </summary>
|
|||
private ICalculationStatus _status = DefaultStatus; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="IterationCountStopCriterium"/> class with the default maximum
|
|||
/// number of iterations.
|
|||
/// </summary>
|
|||
public IterationCountStopCriterium() : this(DefaultMaximumNumberOfIterations) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="IterationCountStopCriterium"/> class with the specified maximum
|
|||
/// number of iterations.
|
|||
/// </summary>
|
|||
/// <param name="maximumNumberOfIterations">The maximum number of iterations the calculation is allowed to perform.</param>
|
|||
public IterationCountStopCriterium(int maximumNumberOfIterations) |
|||
{ |
|||
if (maximumNumberOfIterations < 1) |
|||
{ |
|||
throw new ArgumentOutOfRangeException("maximumNumberOfIterations"); |
|||
} |
|||
|
|||
_maximumNumberOfIterations = maximumNumberOfIterations; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the maximum number of iterations the calculation is allowed to perform.
|
|||
/// </summary>
|
|||
/// <exception cref="ArgumentOutOfRangeException">Thrown if the <c>Maximum</c> is set to a negative value.</exception>
|
|||
public int MaximumNumberOfIterations |
|||
{ |
|||
[DebuggerStepThrough] |
|||
get |
|||
{ |
|||
return _maximumNumberOfIterations; |
|||
} |
|||
|
|||
[DebuggerStepThrough] |
|||
set |
|||
{ |
|||
if (value < 1) |
|||
{ |
|||
throw new ArgumentOutOfRangeException("value"); |
|||
} |
|||
|
|||
_maximumNumberOfIterations = value; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns the maximum number of iterations to the default.
|
|||
/// </summary>
|
|||
public void ResetMaximumNumberOfIterationsToDefault() |
|||
{ |
|||
_maximumNumberOfIterations = DefaultMaximumNumberOfIterations; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Determines the status of the iterative calculation based on the stop criteria stored
|
|||
/// by the current <see cref="IterationCountStopCriterium"/>. Result is set into <c>Status</c> field.
|
|||
/// </summary>
|
|||
/// <param name="iterationNumber">The number of iterations that have passed so far.</param>
|
|||
/// <param name="solutionVector">The vector containing the current solution values.</param>
|
|||
/// <param name="sourceVector">The right hand side vector.</param>
|
|||
/// <param name="residualVector">The vector containing the current residual vectors.</param>
|
|||
/// <remarks>
|
|||
/// The individual stop criteria may internally track the progress of the calculation based
|
|||
/// on the invocation of this method. Therefore this method should only be called if the
|
|||
/// calculation has moved forwards at least one step.
|
|||
/// </remarks>
|
|||
public void DetermineStatus(int iterationNumber, Vector<float> solutionVector, Vector<float> sourceVector, Vector<float> residualVector) |
|||
{ |
|||
if (iterationNumber < 0) |
|||
{ |
|||
throw new ArgumentOutOfRangeException("iterationNumber"); |
|||
} |
|||
|
|||
if (iterationNumber >= _maximumNumberOfIterations) |
|||
{ |
|||
SetStatusToFinished(); |
|||
} |
|||
else |
|||
{ |
|||
SetStatusToRunning(); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Set status to <see cref="CalculationFailure"/>
|
|||
/// </summary>
|
|||
private void SetStatusToFinished() |
|||
{ |
|||
if (!(_status is CalculationStoppedWithoutConvergence)) |
|||
{ |
|||
_status = new CalculationStoppedWithoutConvergence(); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Set status to <see cref="CalculationRunning"/>
|
|||
/// </summary>
|
|||
private void SetStatusToRunning() |
|||
{ |
|||
if (!(_status is CalculationRunning)) |
|||
{ |
|||
_status = new CalculationRunning(); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the current calculation status.
|
|||
/// </summary>
|
|||
public ICalculationStatus Status |
|||
{ |
|||
[DebuggerStepThrough] |
|||
get |
|||
{ |
|||
return _status; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Resets the <see cref="IterationCountStopCriterium"/> to the pre-calculation state.
|
|||
/// </summary>
|
|||
public void ResetToPrecalculationState() |
|||
{ |
|||
_status = DefaultStatus; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the <see cref="StopLevel"/> which indicates what sort of stop criterium this
|
|||
/// <see cref="IterationCountStopCriterium"/> monitors.
|
|||
/// </summary>
|
|||
/// <value>Returns <see cref="StopLevel"/>.</value>
|
|||
public StopLevel StopLevel |
|||
{ |
|||
[DebuggerStepThrough] |
|||
get |
|||
{ |
|||
return StopLevel.StoppedWithoutConvergence; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Clones the current <see cref="IterationCountStopCriterium"/> and its settings.
|
|||
/// </summary>
|
|||
/// <returns>A new instance of the <see cref="IterationCountStopCriterium"/> class.</returns>
|
|||
public IIterationStopCriterium<float> Clone() |
|||
{ |
|||
return new IterationCountStopCriterium(_maximumNumberOfIterations); |
|||
} |
|||
|
|||
#if !SILVERLIGHT
|
|||
/// <summary>
|
|||
/// Clones the current <see cref="IterationCountStopCriterium"/> and its settings.
|
|||
/// </summary>
|
|||
/// <returns>A new instance of the <see cref="IterationCountStopCriterium"/> object.</returns>
|
|||
object ICloneable.Clone() |
|||
{ |
|||
return Clone(); |
|||
} |
|||
#endif
|
|||
} |
|||
} |
|||
@ -0,0 +1,409 @@ |
|||
// <copyright file="ResidualStopCriterium.cs" company="Math.NET">
|
|||
// Math.NET Numerics, part of the Math.NET Project
|
|||
// http://numerics.mathdotnet.com
|
|||
// http://github.com/mathnet/mathnet-numerics
|
|||
// http://mathnetnumerics.codeplex.com
|
|||
//
|
|||
// Copyright (c) 2009-2010 Math.NET
|
|||
//
|
|||
// Permission is hereby granted, free of charge, to any person
|
|||
// obtaining a copy of this software and associated documentation
|
|||
// files (the "Software"), to deal in the Software without
|
|||
// restriction, including without limitation the rights to use,
|
|||
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|||
// copies of the Software, and to permit persons to whom the
|
|||
// Software is furnished to do so, subject to the following
|
|||
// conditions:
|
|||
//
|
|||
// The above copyright notice and this permission notice shall be
|
|||
// included in all copies or substantial portions of the Software.
|
|||
//
|
|||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|||
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|||
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|||
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|||
// OTHER DEALINGS IN THE SOFTWARE.
|
|||
// </copyright>
|
|||
|
|||
namespace MathNet.Numerics.LinearAlgebra.Single.Solvers.StopCriterium |
|||
{ |
|||
using System; |
|||
using System.Diagnostics; |
|||
using Generic; |
|||
using Generic.Solvers.Status; |
|||
using Generic.Solvers.StopCriterium; |
|||
using Properties; |
|||
|
|||
/// <summary>
|
|||
/// Defines an <see cref="IIterationStopCriterium{T}"/> that monitors residuals as stop criterium.
|
|||
/// </summary>
|
|||
public sealed class ResidualStopCriterium : IIterationStopCriterium<float> |
|||
{ |
|||
/// <summary>
|
|||
/// The default value for the maximum value of the residual.
|
|||
/// </summary>
|
|||
public const double DefaultMaximumResidual = 1e-6; |
|||
|
|||
/// <summary>
|
|||
/// The default value for the minimum number of iterations.
|
|||
/// </summary>
|
|||
public const int DefaultMinimumIterationsBelowMaximum = 0; |
|||
|
|||
/// <summary>
|
|||
/// Defines the default last iteration number. Set to -1 because iterations normally start at 0.
|
|||
/// </summary>
|
|||
private const int DefaultLastIterationNumber = -1; |
|||
|
|||
/// <summary>
|
|||
/// The default status.
|
|||
/// </summary>
|
|||
private static readonly ICalculationStatus DefaultStatus = new CalculationIndetermined(); |
|||
|
|||
/// <summary>
|
|||
/// The maximum value for the residual below which the calculation is considered converged.
|
|||
/// </summary>
|
|||
private double _maximum; |
|||
|
|||
/// <summary>
|
|||
/// The minimum number of iterations for which the residual has to be below the maximum before
|
|||
/// the calculation is considered converged.
|
|||
/// </summary>
|
|||
private int _minimumIterationsBelowMaximum; |
|||
|
|||
/// <summary>
|
|||
/// The status of the calculation
|
|||
/// </summary>
|
|||
private ICalculationStatus _status = DefaultStatus; |
|||
|
|||
/// <summary>
|
|||
/// The number of iterations since the residuals got below the maximum.
|
|||
/// </summary>
|
|||
private int _iterationCount; |
|||
|
|||
/// <summary>
|
|||
/// The iteration number of the last iteration.
|
|||
/// </summary>
|
|||
private int _lastIteration = DefaultLastIterationNumber; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="ResidualStopCriterium"/> class with the default maximum
|
|||
/// residual and the default minimum number of iterations.
|
|||
/// </summary>
|
|||
public ResidualStopCriterium() : this(DefaultMaximumResidual, DefaultMinimumIterationsBelowMaximum) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="ResidualStopCriterium"/> class with the specified
|
|||
/// maximum residual and the default minimum number of iterations.
|
|||
/// </summary>
|
|||
/// <param name="maximum">The maximum value for the residual below which the calculation is considered converged.</param>
|
|||
public ResidualStopCriterium(double maximum) : this(maximum, DefaultMinimumIterationsBelowMaximum) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="ResidualStopCriterium"/> class with the default maximum residual
|
|||
/// and specified minimum number of iterations.
|
|||
/// </summary>
|
|||
/// <param name="minimumIterationsBelowMaximum">
|
|||
/// The minimum number of iterations for which the residual has to be below the maximum before
|
|||
/// the calculation is considered converged.
|
|||
/// </param>
|
|||
public ResidualStopCriterium(int minimumIterationsBelowMaximum) : this(DefaultMaximumResidual, minimumIterationsBelowMaximum) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="ResidualStopCriterium"/> class with the specified
|
|||
/// maximum residual and minimum number of iterations.
|
|||
/// </summary>
|
|||
/// <param name="maximum">
|
|||
/// The maximum value for the residual below which the calculation is considered converged.
|
|||
/// </param>
|
|||
/// <param name="minimumIterationsBelowMaximum">
|
|||
/// The minimum number of iterations for which the residual has to be below the maximum before
|
|||
/// the calculation is considered converged.
|
|||
/// </param>
|
|||
public ResidualStopCriterium(double maximum, int minimumIterationsBelowMaximum) |
|||
{ |
|||
if (maximum < 0) |
|||
{ |
|||
throw new ArgumentOutOfRangeException("maximum"); |
|||
} |
|||
|
|||
if (minimumIterationsBelowMaximum < 0) |
|||
{ |
|||
throw new ArgumentOutOfRangeException("minimumIterationsBelowMaximum"); |
|||
} |
|||
|
|||
_maximum = maximum; |
|||
_minimumIterationsBelowMaximum = minimumIterationsBelowMaximum; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the maximum value for the residual below which the calculation is considered
|
|||
/// converged.
|
|||
/// </summary>
|
|||
/// <exception cref="ArgumentOutOfRangeException">Thrown if the <c>Maximum</c> is set to a negative value.</exception>
|
|||
public double Maximum |
|||
{ |
|||
[DebuggerStepThrough] |
|||
get |
|||
{ |
|||
return _maximum; |
|||
} |
|||
|
|||
[DebuggerStepThrough] |
|||
set |
|||
{ |
|||
if (value < 0) |
|||
{ |
|||
throw new ArgumentOutOfRangeException("value"); |
|||
} |
|||
|
|||
_maximum = value; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns the maximum residual to the default.
|
|||
/// </summary>
|
|||
public void ResetMaximumResidualToDefault() |
|||
{ |
|||
_maximum = DefaultMaximumResidual; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the minimum number of iterations for which the residual has to be
|
|||
/// below the maximum before the calculation is considered converged.
|
|||
/// </summary>
|
|||
/// <exception cref="ArgumentOutOfRangeException">Thrown if the <c>BelowMaximumFor</c> is set to a value less than 1.</exception>
|
|||
public int MinimumIterationsBelowMaximum |
|||
{ |
|||
[DebuggerStepThrough] |
|||
get |
|||
{ |
|||
return _minimumIterationsBelowMaximum; |
|||
} |
|||
|
|||
[DebuggerStepThrough] |
|||
set |
|||
{ |
|||
if (value < 0) |
|||
{ |
|||
throw new ArgumentOutOfRangeException("value"); |
|||
} |
|||
|
|||
_minimumIterationsBelowMaximum = value; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns the minimum number of iterations to the default.
|
|||
/// </summary>
|
|||
public void ResetMinimumIterationsBelowMaximumToDefault() |
|||
{ |
|||
_minimumIterationsBelowMaximum = DefaultMinimumIterationsBelowMaximum; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Determines the status of the iterative calculation based on the stop criteria stored
|
|||
/// by the current <see cref="IIterationStopCriterium{T}"/>. Result is set into <c>Status</c> field.
|
|||
/// </summary>
|
|||
/// <param name="iterationNumber">The number of iterations that have passed so far.</param>
|
|||
/// <param name="solutionVector">The vector containing the current solution values.</param>
|
|||
/// <param name="sourceVector">The right hand side vector.</param>
|
|||
/// <param name="residualVector">The vector containing the current residual vectors.</param>
|
|||
/// <remarks>
|
|||
/// The individual stop criteria may internally track the progress of the calculation based
|
|||
/// on the invocation of this method. Therefore this method should only be called if the
|
|||
/// calculation has moved forwards at least one step.
|
|||
/// </remarks>
|
|||
public void DetermineStatus(int iterationNumber, Vector<float> solutionVector, Vector<float> sourceVector, Vector<float> residualVector) |
|||
{ |
|||
if (iterationNumber < 0) |
|||
{ |
|||
throw new ArgumentOutOfRangeException("iterationNumber"); |
|||
} |
|||
|
|||
if (solutionVector == null) |
|||
{ |
|||
throw new ArgumentNullException("solutionVector"); |
|||
} |
|||
|
|||
if (sourceVector == null) |
|||
{ |
|||
throw new ArgumentNullException("sourceVector"); |
|||
} |
|||
|
|||
if (residualVector == null) |
|||
{ |
|||
throw new ArgumentNullException("residualVector"); |
|||
} |
|||
|
|||
if (solutionVector.Count != sourceVector.Count) |
|||
{ |
|||
throw new ArgumentException(Resources.ArgumentVectorsSameLength, "sourceVector"); |
|||
} |
|||
|
|||
if (solutionVector.Count != residualVector.Count) |
|||
{ |
|||
throw new ArgumentException(Resources.ArgumentVectorsSameLength, "residualVector"); |
|||
} |
|||
|
|||
// Store the infinity norms of both the solution and residual vectors
|
|||
// These values will be used to calculate the relative drop in residuals
|
|||
// later on.
|
|||
var residualNorm = residualVector.Norm(Double.PositiveInfinity); |
|||
|
|||
// Check the residuals by calculating:
|
|||
// ||r_i|| <= stop_tol * ||b||
|
|||
var stopCriterium = ComputeStopCriterium(sourceVector.Norm(Double.PositiveInfinity)); |
|||
|
|||
// First check that we have real numbers not NaN's.
|
|||
// NaN's can occur when the iterative process diverges so we
|
|||
// stop if that is the case.
|
|||
if (double.IsNaN(stopCriterium) || double.IsNaN(residualNorm)) |
|||
{ |
|||
_iterationCount = 0; |
|||
SetStatusToDiverged(); |
|||
return; |
|||
} |
|||
|
|||
// ||r_i|| <= stop_tol * ||b||
|
|||
// Stop the calculation if it's clearly smaller than the tolerance
|
|||
var decimalMagnitude = Math.Abs(stopCriterium.Magnitude()) + 1; |
|||
if (residualNorm.IsSmallerWithDecimalPlaces(stopCriterium, decimalMagnitude)) |
|||
{ |
|||
if (_lastIteration <= iterationNumber) |
|||
{ |
|||
_iterationCount = iterationNumber - _lastIteration; |
|||
if (_iterationCount >= _minimumIterationsBelowMaximum) |
|||
{ |
|||
SetStatusToConverged(); |
|||
} |
|||
else |
|||
{ |
|||
SetStatusToRunning(); |
|||
} |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
_iterationCount = 0; |
|||
SetStatusToRunning(); |
|||
} |
|||
|
|||
_lastIteration = iterationNumber; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Calculate stop criterium
|
|||
/// </summary>
|
|||
/// <param name="solutionNorm">Solution vector norm</param>
|
|||
/// <returns>Criterium value</returns>
|
|||
private double ComputeStopCriterium(double solutionNorm) |
|||
{ |
|||
// This is criterium 1 from Templates for the solution of linear systems.
|
|||
// The problem with this criterium is that it's not limiting enough. For now
|
|||
// we won't use it. Later on we might get back to it.
|
|||
// return mMaximumResidual * (System.Math.Abs(mMatrixNorm) * System.Math.Abs(solutionNorm) + System.Math.Abs(mVectorNorm));
|
|||
|
|||
// For now use criterium 2 from Templates for the solution of linear systems. See page 60.
|
|||
return _maximum * Math.Abs(solutionNorm); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Set status to <see cref="CalculationDiverged"/>
|
|||
/// </summary>
|
|||
private void SetStatusToDiverged() |
|||
{ |
|||
if (!(_status is CalculationDiverged)) |
|||
{ |
|||
_status = new CalculationDiverged(); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Set status to <see cref="CalculationConverged"/>
|
|||
/// </summary>
|
|||
private void SetStatusToConverged() |
|||
{ |
|||
if (!(_status is CalculationConverged)) |
|||
{ |
|||
_status = new CalculationConverged(); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Set status to <see cref="CalculationRunning"/>
|
|||
/// </summary>
|
|||
private void SetStatusToRunning() |
|||
{ |
|||
if (!(_status is CalculationRunning)) |
|||
{ |
|||
_status = new CalculationRunning(); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the current calculation status.
|
|||
/// </summary>
|
|||
public ICalculationStatus Status |
|||
{ |
|||
[DebuggerStepThrough] |
|||
get |
|||
{ |
|||
return _status; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Resets the <see cref="IIterationStopCriterium{T}"/> to the pre-calculation state.
|
|||
/// </summary>
|
|||
public void ResetToPrecalculationState() |
|||
{ |
|||
_status = DefaultStatus; |
|||
_iterationCount = 0; |
|||
_lastIteration = DefaultLastIterationNumber; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the <see cref="StopLevel"/> which indicates what sort of stop criterium this
|
|||
/// <see cref="IIterationStopCriterium{T}"/> monitors.
|
|||
/// </summary>
|
|||
/// <value>Returns <see cref="StopLevel"/>.</value>
|
|||
public StopLevel StopLevel |
|||
{ |
|||
[DebuggerStepThrough] |
|||
get |
|||
{ |
|||
return StopLevel.Convergence; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Clones the current <see cref="ResidualStopCriterium"/> and its settings.
|
|||
/// </summary>
|
|||
/// <returns>A new instance of the <see cref="ResidualStopCriterium"/> class.</returns>
|
|||
public IIterationStopCriterium<float> Clone() |
|||
{ |
|||
return new ResidualStopCriterium(_maximum, _minimumIterationsBelowMaximum); |
|||
} |
|||
|
|||
#if !SILVERLIGHT
|
|||
/// <summary>
|
|||
/// Clones the current <see cref="ResidualStopCriterium"/> and its settings.
|
|||
/// </summary>
|
|||
/// <returns>A new instance of the <see cref="ResidualStopCriterium"/> object.</returns>
|
|||
object ICloneable.Clone() |
|||
{ |
|||
return Clone(); |
|||
} |
|||
#endif
|
|||
} |
|||
} |
|||
File diff suppressed because it is too large
File diff suppressed because it is too large
@ -0,0 +1,156 @@ |
|||
// <copyright file="DenseMatrixTests.cs" company="Math.NET">
|
|||
// Math.NET Numerics, part of the Math.NET Project
|
|||
// http://numerics.mathdotnet.com
|
|||
// http://github.com/mathnet/mathnet-numerics
|
|||
// http://mathnetnumerics.codeplex.com
|
|||
//
|
|||
// Copyright (c) 2009-2010 Math.NET
|
|||
//
|
|||
// Permission is hereby granted, free of charge, to any person
|
|||
// obtaining a copy of this software and associated documentation
|
|||
// files (the "Software"), to deal in the Software without
|
|||
// restriction, including without limitation the rights to use,
|
|||
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|||
// copies of the Software, and to permit persons to whom the
|
|||
// Software is furnished to do so, subject to the following
|
|||
// conditions:
|
|||
//
|
|||
// The above copyright notice and this permission notice shall be
|
|||
// included in all copies or substantial portions of the Software.
|
|||
//
|
|||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|||
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|||
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|||
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|||
// OTHER DEALINGS IN THE SOFTWARE.
|
|||
// </copyright>
|
|||
|
|||
namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single |
|||
{ |
|||
using System.Collections.Generic; |
|||
using LinearAlgebra.Generic; |
|||
using MbUnit.Framework; |
|||
using LinearAlgebra.Single; |
|||
|
|||
public class DenseMatrixTests : MatrixTests |
|||
{ |
|||
protected override Matrix<float> CreateMatrix(int rows, int columns) |
|||
{ |
|||
return new DenseMatrix(rows, columns); |
|||
} |
|||
|
|||
protected override Matrix<float> CreateMatrix(float[,] data) |
|||
{ |
|||
return new DenseMatrix(data); |
|||
} |
|||
|
|||
protected override Vector<float> CreateVector(int size) |
|||
{ |
|||
return new DenseVector(size); |
|||
} |
|||
|
|||
protected override Vector<float> CreateVector(float[] data) |
|||
{ |
|||
return new DenseVector(data); |
|||
} |
|||
|
|||
[Test] |
|||
public void CanCreateMatrixFrom1DArray() |
|||
{ |
|||
var testData = new Dictionary<string, Matrix<float>> |
|||
{ |
|||
{ "Singular3x3", new DenseMatrix(3, 3, new[] { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 2.0f, 2.0f, 2.0f }) }, |
|||
{ "Square3x3", new DenseMatrix(3, 3, new[] { -1.1f, 0.0f, -4.4f, -2.2f, 1.1f, 5.5f, -3.3f, 2.2f, 6.6f }) }, |
|||
{ "Square4x4", new DenseMatrix(4, 4, new[] { -1.1f, 0.0f, 1.0f, -4.4f, -2.2f, 1.1f, 2.1f, 5.5f, -3.3f, 2.2f, 6.2f, 6.6f, -4.4f, 3.3f, 4.3f, -7.7f }) }, |
|||
{ "Tall3x2", new DenseMatrix(3, 2, new[] { -1.1f, 0.0f, -4.4f, -2.2f, 1.1f, 5.5f }) }, |
|||
{ "Wide2x3", new DenseMatrix(2, 3, new[] { -1.1f, 0.0f, -2.2f, 1.1f, -3.3f, 2.2f }) } |
|||
}; |
|||
|
|||
foreach (var name in testData.Keys) |
|||
{ |
|||
Assert.AreEqual(TestMatrices[name], testData[name]); |
|||
} |
|||
} |
|||
|
|||
[Test] |
|||
public void MatrixFrom1DArrayIsReference() |
|||
{ |
|||
var data = new float[] { 1, 1, 1, 1, 1, 1, 2, 2, 2 }; |
|||
var matrix = new DenseMatrix(3, 3, data); |
|||
matrix[0, 0] = 10.0f; |
|||
Assert.AreEqual(10.0, data[0]); |
|||
} |
|||
|
|||
[Test] |
|||
public void MatrixFrom2DArrayIsCopy() |
|||
{ |
|||
var matrix = new DenseMatrix(TestData2D["Singular3x3"]); |
|||
matrix[0, 0] = 10.0f; |
|||
Assert.AreEqual(1.0, TestData2D["Singular3x3"][0, 0]); |
|||
} |
|||
|
|||
[Test] |
|||
[Row("Singular3x3")] |
|||
[Row("Singular3x3")] |
|||
[Row("Square3x3")] |
|||
[Row("Square4x4")] |
|||
[Row("Tall3x2")] |
|||
[Row("Wide2x3")] |
|||
public void CanCreateMatrixFrom2DArray(string name) |
|||
{ |
|||
var matrix = new DenseMatrix(TestData2D[name]); |
|||
for (var i = 0; i < TestData2D[name].GetLength(0); i++) |
|||
{ |
|||
for (var j = 0; j < TestData2D[name].GetLength(1); j++) |
|||
{ |
|||
Assert.AreEqual(TestData2D[name][i, j], matrix[i, j]); |
|||
} |
|||
} |
|||
} |
|||
|
|||
[Test] |
|||
public void CanCreateMatrixWithUniformValues() |
|||
{ |
|||
var matrix = new DenseMatrix(10, 10, 10.0f); |
|||
for (var i = 0; i < matrix.RowCount; i++) |
|||
{ |
|||
for (var j = 0; j < matrix.ColumnCount; j++) |
|||
{ |
|||
Assert.AreEqual(matrix[i, j], 10.0); |
|||
} |
|||
} |
|||
} |
|||
|
|||
[Test] |
|||
public void CanCreateIdentity() |
|||
{ |
|||
var matrix = DenseMatrix.Identity(5); |
|||
for (var i = 0; i < matrix.RowCount; i++) |
|||
{ |
|||
for (var j = 0; j < matrix.ColumnCount; j++) |
|||
{ |
|||
if (i == j) |
|||
{ |
|||
Assert.AreEqual(1.0, matrix[i, j]); |
|||
} |
|||
else |
|||
{ |
|||
Assert.AreEqual(0.0, matrix[i, j]); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
[Test] |
|||
[Row(0)] |
|||
[Row(-1)] |
|||
[ExpectedArgumentException] |
|||
public void IdentityFailsWithZeroOrNegativeOrder(int order) |
|||
{ |
|||
DenseMatrix.Identity(order); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,133 @@ |
|||
// <copyright file="DenseVectorTest.TextHandling.cs" company="Math.NET">
|
|||
// Math.NET Numerics, part of the Math.NET Project
|
|||
// http://numerics.mathdotnet.com
|
|||
// http://github.com/mathnet/mathnet-numerics
|
|||
// http://mathnetnumerics.codeplex.com
|
|||
//
|
|||
// Copyright (c) 2009-2010 Math.NET
|
|||
//
|
|||
// Permission is hereby granted, free of charge, to any person
|
|||
// obtaining a copy of this software and associated documentation
|
|||
// files (the "Software"), to deal in the Software without
|
|||
// restriction, including without limitation the rights to use,
|
|||
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|||
// copies of the Software, and to permit persons to whom the
|
|||
// Software is furnished to do so, subject to the following
|
|||
// conditions:
|
|||
//
|
|||
// The above copyright notice and this permission notice shall be
|
|||
// included in all copies or substantial portions of the Software.
|
|||
//
|
|||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|||
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|||
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|||
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|||
// OTHER DEALINGS IN THE SOFTWARE.
|
|||
// </copyright>
|
|||
|
|||
namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single |
|||
{ |
|||
using System; |
|||
using System.Globalization; |
|||
using LinearAlgebra.Single; |
|||
using MbUnit.Framework; |
|||
|
|||
[TestFixture] |
|||
public class DenseVectorTextHandlingTest |
|||
{ |
|||
[Test] |
|||
[Row("2", "2")] |
|||
[Row("(3)", "3")] |
|||
[Row("[1,2,3]", "1,2,3")]
|
|||
[Row(" [ 1 , 2 , 3 ] ", "1,2,3")]
|
|||
[Row(" [ -1 , 2 , +3 ] ", "-1,2,3")]
|
|||
[Row(" [1.2,3.4 , 5.6] ", "1.2,3.4,5.6")]
|
|||
public void CanParseDoubleDenseVectorsWithInvariant(string stringToParse, string expectedToString) |
|||
{ |
|||
var formatProvider = CultureInfo.InvariantCulture; |
|||
DenseVector vector = DenseVector.Parse(stringToParse, formatProvider); |
|||
|
|||
Assert.AreEqual(expectedToString, vector.ToString(formatProvider)); |
|||
} |
|||
|
|||
[Test] |
|||
[Row(" 1.2,3.4 , 5.6 ", "1.2,3.4,5.6", "en-US")] |
|||
[Row(" 1.2;3.4 ; 5.6 ", "1.2;3.4;5.6", "de-CH")] |
|||
[Row(" 1,2;3,4 ; 5,6 ", "1,2;3,4;5,6", "de-DE")] |
|||
public void CanParseDoubleDenseVectorsWithCulture(string stringToParse, string expectedToString, string culture) |
|||
{ |
|||
var formatProvider = CultureInfo.GetCultureInfo(culture); |
|||
DenseVector vector = DenseVector.Parse(stringToParse, formatProvider); |
|||
|
|||
Assert.AreEqual(expectedToString, vector.ToString(formatProvider)); |
|||
} |
|||
|
|||
[Test] |
|||
[Row("15")] |
|||
[Row("1{0}2{1}3{0}4{1}5{0}6")] |
|||
public void CanParseDoubleDenseVectors(string vectorAsString) |
|||
{ |
|||
var mappedString = String.Format( |
|||
vectorAsString, |
|||
CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator, |
|||
CultureInfo.CurrentCulture.TextInfo.ListSeparator); |
|||
|
|||
DenseVector vector = DenseVector.Parse(mappedString); |
|||
|
|||
Assert.AreEqual(mappedString, vector.ToString()); |
|||
} |
|||
|
|||
[Test] |
|||
[MultipleAsserts] |
|||
public void ParseThrowsFormatExceptionIfMissingClosingParen() |
|||
{ |
|||
Assert.Throws<FormatException>(() => DenseVector.Parse("(1")); |
|||
Assert.Throws<FormatException>(() => DenseVector.Parse("[1")); |
|||
} |
|||
|
|||
[Test] |
|||
public void CanTryParseDoubleDenseVector() |
|||
{ |
|||
var data = new[] { 1.2f, 3.4f, 5.6e-78f }; |
|||
var text = String.Format( |
|||
"{1}{0}{2}{0}{3}", |
|||
CultureInfo.CurrentCulture.TextInfo.ListSeparator, |
|||
data[0], |
|||
data[1], |
|||
data[2]); |
|||
|
|||
DenseVector vector; |
|||
var ret = DenseVector.TryParse(text, out vector); |
|||
Assert.IsTrue(ret); |
|||
AssertHelpers.AlmostEqualList(data, vector.Data, 1e-15); |
|||
|
|||
ret = DenseVector.TryParse(text, CultureInfo.CurrentCulture, out vector); |
|||
Assert.IsTrue(ret); |
|||
AssertHelpers.AlmostEqualList(data, vector.Data, 1e-15); |
|||
} |
|||
|
|||
[Test] |
|||
[Row(null)] |
|||
[Row("")] |
|||
[Row(",")] |
|||
[Row("1,")] |
|||
[Row(",1")] |
|||
[Row("1,2,")] |
|||
[Row(",1,2,")] |
|||
[Row("1,,2,,3")] |
|||
[Row("1e+")] |
|||
[Row("1e")] |
|||
[Row("()")] |
|||
[Row("[ ]")]
|
|||
public void TryParseReturnsFalseWhenGivenBadValueWithInvariant(string str) |
|||
{ |
|||
DenseVector vector; |
|||
var ret = DenseVector.TryParse(str, CultureInfo.InvariantCulture, out vector); |
|||
Assert.IsFalse(ret); |
|||
Assert.IsNull(vector); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,290 @@ |
|||
// <copyright file="DenseVectorTests.cs" company="Math.NET">
|
|||
// Math.NET Numerics, part of the Math.NET Project
|
|||
// http://numerics.mathdotnet.com
|
|||
// http://github.com/mathnet/mathnet-numerics
|
|||
// http://mathnetnumerics.codeplex.com
|
|||
// Copyright (c) 2009-2010 Math.NET
|
|||
// Permission is hereby granted, free of charge, to any person
|
|||
// obtaining a copy of this software and associated documentation
|
|||
// files (the "Software"), to deal in the Software without
|
|||
// restriction, including without limitation the rights to use,
|
|||
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|||
// copies of the Software, and to permit persons to whom the
|
|||
// Software is furnished to do so, subject to the following
|
|||
// conditions:
|
|||
// The above copyright notice and this permission notice shall be
|
|||
// included in all copies or substantial portions of the Software.
|
|||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|||
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|||
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|||
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|||
// OTHER DEALINGS IN THE SOFTWARE.
|
|||
// </copyright>
|
|||
|
|||
namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single |
|||
{ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using LinearAlgebra.Single; |
|||
using LinearAlgebra.Generic; |
|||
using MbUnit.Framework; |
|||
|
|||
public class DenseVectorTests : VectorTests |
|||
{ |
|||
protected override Vector<float> CreateVector(int size) |
|||
{ |
|||
return new DenseVector(size); |
|||
} |
|||
|
|||
protected override Vector<float> CreateVector(IList<float> data) |
|||
{ |
|||
var vector = new DenseVector(data.Count); |
|||
for (var index = 0; index < data.Count; index++) |
|||
{ |
|||
vector[index] = data[index]; |
|||
} |
|||
|
|||
return vector; |
|||
} |
|||
|
|||
[Test] |
|||
[MultipleAsserts] |
|||
public void CanCreateDenseVectorFromArray() |
|||
{ |
|||
var data = new float[Data.Length]; |
|||
Array.Copy(Data, data, Data.Length); |
|||
var vector = new DenseVector(data); |
|||
|
|||
Assert.AreSame(data, vector.Data); |
|||
for (var i = 0; i < data.Length; i++) |
|||
{ |
|||
Assert.AreEqual(data[i], vector[i]); |
|||
} |
|||
|
|||
vector[0] = 100.0f; |
|||
Assert.AreEqual(100.0, data[0]); |
|||
} |
|||
|
|||
[Test] |
|||
[MultipleAsserts] |
|||
public void CanCreateDenseVectorFromAnotherDenseVector() |
|||
{ |
|||
var vector = new DenseVector(Data); |
|||
var other = new DenseVector(vector); |
|||
|
|||
Assert.AreNotSame(vector, other); |
|||
for (var i = 0; i < Data.Length; i++) |
|||
{ |
|||
Assert.AreEqual(vector[i], other[i]); |
|||
} |
|||
} |
|||
|
|||
[Test] |
|||
[MultipleAsserts] |
|||
public void CanCreateDenseVectorFromAnotherVector() |
|||
{ |
|||
var vector = (Vector<float>)new DenseVector(Data); |
|||
var other = new DenseVector(vector); |
|||
|
|||
Assert.AreNotSame(vector, other); |
|||
for (var i = 0; i < Data.Length; i++) |
|||
{ |
|||
Assert.AreEqual(vector[i], other[i]); |
|||
} |
|||
} |
|||
|
|||
[Test] |
|||
[MultipleAsserts] |
|||
public void CanCreateDenseVectorFromUserDefinedVector() |
|||
{ |
|||
var vector = new UserDefinedVector(Data); |
|||
var other = new DenseVector(vector); |
|||
|
|||
for (var i = 0; i < Data.Length; i++) |
|||
{ |
|||
Assert.AreEqual(vector[i], other[i]); |
|||
} |
|||
} |
|||
|
|||
[Test] |
|||
public void CanCreateDenseVectorWithConstantValues() |
|||
{ |
|||
var vector = new DenseVector(5, 5); |
|||
Assert.ForAll(vector, value => value == 5); |
|||
} |
|||
|
|||
[Test] |
|||
[MultipleAsserts] |
|||
public void CanCreateDenseMatrix() |
|||
{ |
|||
var vector = new DenseVector(3); |
|||
var matrix = vector.CreateMatrix(2, 3); |
|||
Assert.AreEqual(2, matrix.RowCount); |
|||
Assert.AreEqual(3, matrix.ColumnCount); |
|||
} |
|||
|
|||
|
|||
[Test] |
|||
[MultipleAsserts] |
|||
public void CanConvertDenseVectorToArray() |
|||
{ |
|||
var vector = new DenseVector(Data); |
|||
var array = (float[])vector; |
|||
Assert.IsInstanceOfType(typeof(float[]), array); |
|||
Assert.AreSame(vector.Data, array); |
|||
Assert.AreElementsEqual(vector, array); |
|||
} |
|||
|
|||
[Test] |
|||
[MultipleAsserts] |
|||
public void CanConvertArrayToDenseVector() |
|||
{ |
|||
var array = new[] { 0.0f, 1.0f, 2.0f, 3.0f, 4.0f }; |
|||
var vector = (DenseVector)array; |
|||
Assert.IsInstanceOfType(typeof(DenseVector), vector); |
|||
Assert.AreElementsEqual(array, array); |
|||
} |
|||
|
|||
[Test] |
|||
public void CanCallUnaryPlusOperatorOnDenseVector() |
|||
{ |
|||
var vector = new DenseVector(Data); |
|||
var other = +vector; |
|||
for (var i = 0; i < Data.Length; i++) |
|||
{ |
|||
Assert.AreEqual(vector[i], other[i]); |
|||
} |
|||
} |
|||
|
|||
[Test] |
|||
[MultipleAsserts] |
|||
public void CanAddTwoDenseVectorsUsingOperator() |
|||
{ |
|||
var vector = new DenseVector(Data); |
|||
var other = new DenseVector(Data); |
|||
var result = vector + other; |
|||
|
|||
for (var i = 0; i < Data.Length; i++) |
|||
{ |
|||
Assert.AreEqual(Data[i], vector[i], "Making sure the original vector wasn't modified."); |
|||
Assert.AreEqual(Data[i], other[i], "Making sure the original vector wasn't modified."); |
|||
Assert.AreEqual(Data[i] * 2.0, result[i]); |
|||
} |
|||
} |
|||
|
|||
[Test] |
|||
public void CanCallUnaryNegationOperatorOnDenseVector() |
|||
{ |
|||
var vector = new DenseVector(Data); |
|||
var other = -vector; |
|||
for (var i = 0; i < Data.Length; i++) |
|||
{ |
|||
Assert.AreEqual(-Data[i], other[i]); |
|||
} |
|||
} |
|||
|
|||
[Test] |
|||
[MultipleAsserts] |
|||
public void CanSubtractTwoDenseVectorsUsingOperator() |
|||
{ |
|||
var vector = new DenseVector(Data); |
|||
var other = new DenseVector(Data); |
|||
var result = vector - other; |
|||
|
|||
for (var i = 0; i < Data.Length; i++) |
|||
{ |
|||
Assert.AreEqual(Data[i], vector[i], "Making sure the original vector wasn't modified."); |
|||
Assert.AreEqual(Data[i], other[i], "Making sure the original vector wasn't modified."); |
|||
Assert.AreEqual(0.0, result[i]); |
|||
} |
|||
} |
|||
|
|||
[Test] |
|||
[MultipleAsserts] |
|||
public void CanMultiplyDenseVectorByScalarUsingOperators() |
|||
{ |
|||
var vector = new DenseVector(Data); |
|||
vector = vector * 2.0f; |
|||
|
|||
for (var i = 0; i < Data.Length; i++) |
|||
{ |
|||
Assert.AreEqual(Data[i] * 2.0, vector[i]); |
|||
} |
|||
|
|||
vector = vector * 1.0f; |
|||
for (var i = 0; i < Data.Length; i++) |
|||
{ |
|||
Assert.AreEqual(Data[i] * 2.0, vector[i]); |
|||
} |
|||
|
|||
vector = new DenseVector(Data); |
|||
vector = 2.0f * vector; |
|||
|
|||
for (var i = 0; i < Data.Length; i++) |
|||
{ |
|||
Assert.AreEqual(Data[i] * 2.0, vector[i]); |
|||
} |
|||
|
|||
vector = 1.0f * vector; |
|||
for (var i = 0; i < Data.Length; i++) |
|||
{ |
|||
Assert.AreEqual(Data[i] * 2.0, vector[i]); |
|||
} |
|||
} |
|||
|
|||
[Test] |
|||
[MultipleAsserts] |
|||
public void CanDivideDenseVectorByScalarUsingOperators() |
|||
{ |
|||
var vector = new DenseVector(Data); |
|||
vector = vector / 2.0f; |
|||
|
|||
for (var i = 0; i < Data.Length; i++) |
|||
{ |
|||
Assert.AreEqual(Data[i] / 2.0, vector[i]); |
|||
} |
|||
|
|||
vector = vector / 1.0f; |
|||
for (var i = 0; i < Data.Length; i++) |
|||
{ |
|||
Assert.AreEqual(Data[i] / 2.0, vector[i]); |
|||
} |
|||
} |
|||
|
|||
[Test] |
|||
public void CanCalculateOuterProductForDenseVector() |
|||
{ |
|||
var vector1 = CreateVector(Data); |
|||
var vector2 = CreateVector(Data); |
|||
Matrix<float> m = Vector<float>.OuterProduct(vector1, vector2); |
|||
for (var i = 0; i < vector1.Count; i++) |
|||
{ |
|||
for (var j = 0; j < vector2.Count; j++) |
|||
{ |
|||
Assert.AreEqual(m[i, j], vector1[i] * vector2[j]); |
|||
} |
|||
} |
|||
} |
|||
|
|||
[Test] |
|||
[ExpectedArgumentNullException] |
|||
public void OuterProducForDenseVectortWithFirstParameterNullShouldThrowException() |
|||
{ |
|||
DenseVector vector1 = null; |
|||
var vector2 = CreateVector(Data); |
|||
Vector<float>.OuterProduct(vector1, vector2); |
|||
} |
|||
|
|||
[Test] |
|||
[ExpectedArgumentNullException] |
|||
public void OuterProductForDenseVectorWithSecondParameterNullShouldThrowException() |
|||
{ |
|||
var vector1 = CreateVector(Data); |
|||
DenseVector vector2 = null; |
|||
Vector<float>.OuterProduct(vector1, vector2); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,450 @@ |
|||
// <copyright file="DiagonalMatrixTests.cs" company="Math.NET">
|
|||
// Math.NET Numerics, part of the Math.NET Project
|
|||
// http://numerics.mathdotnet.com
|
|||
// http://github.com/mathnet/mathnet-numerics
|
|||
// http://mathnetnumerics.codeplex.com
|
|||
//
|
|||
// Copyright (c) 2009-2010 Math.NET
|
|||
//
|
|||
// Permission is hereby granted, free of charge, to any person
|
|||
// obtaining a copy of this software and associated documentation
|
|||
// files (the "Software"), to deal in the Software without
|
|||
// restriction, including without limitation the rights to use,
|
|||
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|||
// copies of the Software, and to permit persons to whom the
|
|||
// Software is furnished to do so, subject to the following
|
|||
// conditions:
|
|||
//
|
|||
// The above copyright notice and this permission notice shall be
|
|||
// included in all copies or substantial portions of the Software.
|
|||
//
|
|||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|||
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|||
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|||
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|||
// OTHER DEALINGS IN THE SOFTWARE.
|
|||
// </copyright>
|
|||
|
|||
namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single |
|||
{ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using LinearAlgebra.Generic; |
|||
using MbUnit.Framework; |
|||
using LinearAlgebra.Single; |
|||
|
|||
public class DiagonalMatrixTests : MatrixTests |
|||
{ |
|||
[SetUp] |
|||
public override void SetupMatrices() |
|||
{ |
|||
TestData2D = new Dictionary<string, float[,]> |
|||
{ |
|||
{ "Singular3x3", new [,] { { 1.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 3.0f } } }, |
|||
{ "Square3x3", new [,] { { -1.1f, 0.0f, 0.0f }, { 0.0f, 1.1f, 0.0f }, { 0.0f, 0.0f, 6.6f } } }, |
|||
{ "Square4x4", new [,] { { -1.1f, 0.0f, 0.0f, 0.0f }, { 0.0f, 1.1f, 0.0f, 0.0f }, { 0.0f, 0.0f, 6.2f, 0.0f}, { 0.0f, 0.0f, 0.0f, -7.7f} } }, |
|||
{ "Singular4x4", new [,] { { -1.1f, 0.0f, 0.0f, 0.0f }, { 0.0f, -2.2f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f, 0.0f}, { 0.0f, 0.0f, 0.0f, -4.4f } } }, |
|||
{ "Tall3x2", new [,] { { -1.1f, 0.0f }, { 0.0f, 1.1f }, { 0.0f, 0.0f } } }, |
|||
{ "Wide2x3", new [,] { { -1.1f, 0.0f, 0.0f }, { 0.0f, 1.1f, 0.0f } } } |
|||
}; |
|||
|
|||
TestMatrices = new Dictionary<string, Matrix<float>>(); |
|||
foreach (var name in TestData2D.Keys) |
|||
{ |
|||
TestMatrices.Add(name, CreateMatrix(TestData2D[name])); |
|||
} |
|||
} |
|||
|
|||
protected override Matrix<float> CreateMatrix(int rows, int columns) |
|||
{ |
|||
return new DiagonalMatrix(rows, columns); |
|||
} |
|||
|
|||
protected override Matrix<float> CreateMatrix(float[,] data) |
|||
{ |
|||
return new DiagonalMatrix(data); |
|||
} |
|||
|
|||
protected override Vector<float> CreateVector(int size) |
|||
{ |
|||
return new DenseVector(size); |
|||
} |
|||
|
|||
protected override Vector<float> CreateVector(float[] data) |
|||
{ |
|||
return new DenseVector(data); |
|||
} |
|||
|
|||
[Test] |
|||
public void CanCreateMatrixFromDiagonalArray() |
|||
{ |
|||
var testData = new Dictionary<string, Matrix<float>> |
|||
{ |
|||
{ "Singular3x3", new DiagonalMatrix(3, 3, new[] { 1.0f, 0.0f, 3.0f}) }, |
|||
{ "Square3x3", new DiagonalMatrix(4, 4, new[] { -1.1f, 1.1f, 6.6f }) }, |
|||
{ "Square4x4", new DiagonalMatrix(4, 4, new[] { -1.1f, 1.1f, 6.2f, -7.7f }) }, |
|||
{ "Tall3x2", new DiagonalMatrix(3, 2, new[] { -1.1f, 1.1f }) }, |
|||
{ "Wide2x3", new DiagonalMatrix(2, 3, new[] { -1.1f, 1.1f }) }, |
|||
}; |
|||
|
|||
foreach (var name in testData.Keys) |
|||
{ |
|||
Assert.AreEqual(TestMatrices[name], testData[name]); |
|||
} |
|||
} |
|||
|
|||
[Test] |
|||
public void MatrixFrom1DArrayIsReference() |
|||
{ |
|||
var data = new float[] { 1, 2, 3, 4, 5}; |
|||
var matrix = new DiagonalMatrix(5, 5, data); |
|||
matrix[0, 0] = 10.0f; |
|||
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.0f); |
|||
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, float[] 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, float[] 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, float[] 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, float[] 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((float)denseMatrix.FrobeniusNorm(), (float)matrix.FrobeniusNorm(), 7); |
|||
|
|||
matrix = TestMatrices["Wide2x3"]; |
|||
denseMatrix = new DenseMatrix(TestData2D["Wide2x3"]); |
|||
AssertHelpers.AlmostEqual((float)denseMatrix.FrobeniusNorm(), (float)matrix.FrobeniusNorm(), 7); |
|||
|
|||
matrix = TestMatrices["Tall3x2"]; |
|||
denseMatrix = new DenseMatrix(TestData2D["Tall3x2"]); |
|||
AssertHelpers.AlmostEqual((float)denseMatrix.FrobeniusNorm(), (float)matrix.FrobeniusNorm(), 7); |
|||
} |
|||
|
|||
public override void InfinityNorm() |
|||
{ |
|||
var matrix = TestMatrices["Square3x3"]; |
|||
var denseMatrix = new DenseMatrix(TestData2D["Square3x3"]); |
|||
AssertHelpers.AlmostEqual((float)denseMatrix.InfinityNorm(), (float)matrix.InfinityNorm(), 7); |
|||
|
|||
matrix = TestMatrices["Wide2x3"]; |
|||
denseMatrix = new DenseMatrix(TestData2D["Wide2x3"]); |
|||
AssertHelpers.AlmostEqual((float)denseMatrix.InfinityNorm(), (float)matrix.InfinityNorm(), 7); |
|||
|
|||
matrix = TestMatrices["Tall3x2"]; |
|||
denseMatrix = new DenseMatrix(TestData2D["Tall3x2"]); |
|||
AssertHelpers.AlmostEqual((float)denseMatrix.InfinityNorm(), (float)matrix.InfinityNorm(), 7); |
|||
} |
|||
|
|||
public override void L1Norm() |
|||
{ |
|||
var matrix = TestMatrices["Square3x3"]; |
|||
var denseMatrix = new DenseMatrix(TestData2D["Square3x3"]); |
|||
AssertHelpers.AlmostEqual((float)denseMatrix.L1Norm(), (float)matrix.L1Norm(), 7); |
|||
|
|||
matrix = TestMatrices["Wide2x3"]; |
|||
denseMatrix = new DenseMatrix(TestData2D["Wide2x3"]); |
|||
AssertHelpers.AlmostEqual((float)denseMatrix.L1Norm(), (float)matrix.L1Norm(), 7); |
|||
|
|||
matrix = TestMatrices["Tall3x2"]; |
|||
denseMatrix = new DenseMatrix(TestData2D["Tall3x2"]); |
|||
AssertHelpers.AlmostEqual((float)denseMatrix.L1Norm(), (float)matrix.L1Norm(), 7); |
|||
} |
|||
|
|||
public override void L2Norm() |
|||
{ |
|||
var matrix = TestMatrices["Square3x3"]; |
|||
var denseMatrix = new DenseMatrix(TestData2D["Square3x3"]); |
|||
AssertHelpers.AlmostEqual((float)denseMatrix.L2Norm(), (float)matrix.L2Norm(), 7); |
|||
|
|||
matrix = TestMatrices["Wide2x3"]; |
|||
denseMatrix = new DenseMatrix(TestData2D["Wide2x3"]); |
|||
AssertHelpers.AlmostEqual((float)denseMatrix.L2Norm(), (float)matrix.L2Norm(), 7); |
|||
|
|||
matrix = TestMatrices["Tall3x2"]; |
|||
denseMatrix = new DenseMatrix(TestData2D["Tall3x2"]); |
|||
AssertHelpers.AlmostEqual((float)denseMatrix.L2Norm(), (float)matrix.L2Norm(), 7); |
|||
} |
|||
|
|||
[Test] |
|||
[MultipleAsserts] |
|||
public void Determinant() |
|||
{ |
|||
var matrix = TestMatrices["Square3x3"]; |
|||
var denseMatrix = new DenseMatrix(TestData2D["Square3x3"]); |
|||
AssertHelpers.AlmostEqual(denseMatrix.Determinant(), matrix.Determinant(), 7); |
|||
|
|||
matrix = TestMatrices["Square4x4"]; |
|||
denseMatrix = new DenseMatrix(TestData2D["Square4x4"]); |
|||
AssertHelpers.AlmostEqual(denseMatrix.Determinant(), matrix.Determinant(), 7); |
|||
} |
|||
|
|||
[Test] |
|||
[ExpectedArgumentException] |
|||
public void DeterminantNotSquareMatrixThrowException() |
|||
{ |
|||
var matrix = TestMatrices["Tall3x2"]; |
|||
matrix.Determinant(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,300 @@ |
|||
// <copyright file="CholeskyTests.cs" company="Math.NET">
|
|||
// Math.NET Numerics, part of the Math.NET Project
|
|||
// http://numerics.mathdotnet.com
|
|||
// http://github.com/mathnet/mathnet-numerics
|
|||
// http://mathnetnumerics.codeplex.com
|
|||
//
|
|||
// Copyright (c) 2009-2010 Math.NET
|
|||
//
|
|||
// Permission is hereby granted, free of charge, to any person
|
|||
// obtaining a copy of this software and associated documentation
|
|||
// files (the "Software"), to deal in the Software without
|
|||
// restriction, including without limitation the rights to use,
|
|||
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|||
// copies of the Software, and to permit persons to whom the
|
|||
// Software is furnished to do so, subject to the following
|
|||
// conditions:
|
|||
//
|
|||
// The above copyright notice and this permission notice shall be
|
|||
// included in all copies or substantial portions of the Software.
|
|||
//
|
|||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|||
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|||
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|||
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|||
// OTHER DEALINGS IN THE SOFTWARE.
|
|||
// </copyright>
|
|||
|
|||
namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single.Factorization |
|||
{ |
|||
using LinearAlgebra.Generic.Factorization; |
|||
using MbUnit.Framework; |
|||
using LinearAlgebra.Single; |
|||
|
|||
public class CholeskyTests |
|||
{ |
|||
[Test] |
|||
[Row(1)] |
|||
[Row(10)] |
|||
[Row(100)] |
|||
public void CanFactorizeIdentity(int order) |
|||
{ |
|||
var I = DenseMatrix.Identity(order); |
|||
var factorC = I.Cholesky(); |
|||
|
|||
Assert.AreEqual(I.RowCount, factorC.Factor.RowCount); |
|||
Assert.AreEqual(I.ColumnCount, factorC.Factor.ColumnCount); |
|||
|
|||
for (var i = 0; i < factorC.Factor.RowCount; i++) |
|||
{ |
|||
for (var j = 0; j < factorC.Factor.ColumnCount; j++) |
|||
{ |
|||
Assert.AreEqual(i == j ? 1.0 : 0.0, factorC.Factor[i, j]); |
|||
} |
|||
} |
|||
} |
|||
|
|||
[Test] |
|||
[ExpectedArgumentException] |
|||
public void CholeskyFailsWithDiagonalNonPositiveDefiniteMatrix() |
|||
{ |
|||
var I = DenseMatrix.Identity(10); |
|||
I[3, 3] = -4.0f; |
|||
I.Cholesky(); |
|||
} |
|||
|
|||
[Test] |
|||
[Row(3,5)] |
|||
[Row(5,3)] |
|||
[ExpectedArgumentException] |
|||
public void CholeskyFailsWithNonSquareMatrix(int row, int col) |
|||
{ |
|||
var I = new DenseMatrix(row, col); |
|||
I.Cholesky(); |
|||
} |
|||
|
|||
[Test] |
|||
[Row(1)] |
|||
[Row(10)] |
|||
[Row(100)] |
|||
public void IdentityDeterminantIsOne(int order) |
|||
{ |
|||
var I = DenseMatrix.Identity(order); |
|||
var factorC = I.Cholesky(); |
|||
Assert.AreEqual(1.0, factorC.Determinant); |
|||
Assert.AreEqual(0.0, factorC.DeterminantLn); |
|||
} |
|||
|
|||
[Test] |
|||
[Row(1)] |
|||
[Row(2)] |
|||
[Row(5)] |
|||
[Row(10)] |
|||
[Row(50)] |
|||
[Row(100)] |
|||
[MultipleAsserts] |
|||
public void CanFactorizeRandomMatrix(int order) |
|||
{ |
|||
var matrixX = MatrixLoader.GenerateRandomPositiveDefiniteDenseMatrix(order); |
|||
var chol = matrixX.Cholesky(); |
|||
var factorC = chol.Factor; |
|||
|
|||
// Make sure the Cholesky factor has the right dimensions.
|
|||
Assert.AreEqual(order, factorC.RowCount); |
|||
Assert.AreEqual(order, factorC.ColumnCount); |
|||
|
|||
// Make sure the Cholesky factor is lower triangular.
|
|||
for (var i = 0; i < factorC.RowCount; i++) |
|||
{ |
|||
for (var j = i+1; j < factorC.ColumnCount; j++) |
|||
{ |
|||
Assert.AreEqual(0.0, factorC[i, j]); |
|||
} |
|||
} |
|||
|
|||
// Make sure the cholesky factor times it's transpose is the original matrix.
|
|||
var matrixXfromC = factorC * factorC.Transpose(); |
|||
for (var i = 0; i < matrixXfromC.RowCount; i++) |
|||
{ |
|||
for (var j = 0; j < matrixXfromC.ColumnCount; j++) |
|||
{ |
|||
Assert.AreApproximatelyEqual(matrixX[i,j], matrixXfromC[i, j], 10e-3f); |
|||
} |
|||
} |
|||
} |
|||
|
|||
[Test] |
|||
[Row(1)] |
|||
[Row(2)] |
|||
[Row(5)] |
|||
[Row(10)] |
|||
[Row(50)] |
|||
[Row(100)] |
|||
[MultipleAsserts] |
|||
public void CanSolveForRandomVector(int order) |
|||
{ |
|||
var matrixA = MatrixLoader.GenerateRandomPositiveDefiniteDenseMatrix(order); |
|||
var matrixACopy = matrixA.Clone(); |
|||
var chol = matrixA.Cholesky(); |
|||
var b = MatrixLoader.GenerateRandomDenseVector(order); |
|||
var x = chol.Solve(b); |
|||
|
|||
Assert.AreEqual(b.Count, x.Count); |
|||
|
|||
var bReconstruct = matrixA * x; |
|||
|
|||
// Check the reconstruction.
|
|||
for (var i = 0; i < order; i++) |
|||
{ |
|||
Assert.AreApproximatelyEqual(b[i], bReconstruct[i], 10e-3f); |
|||
} |
|||
|
|||
// Make sure A didn't change.
|
|||
for (var i = 0; i < matrixA.RowCount; i++) |
|||
{ |
|||
for (var j = 0; j < matrixA.ColumnCount; j++) |
|||
{ |
|||
Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]); |
|||
} |
|||
} |
|||
} |
|||
|
|||
[Test] |
|||
[Row(1,1)] |
|||
[Row(2,4)] |
|||
[Row(5,8)] |
|||
[Row(10,3)] |
|||
[Row(50,10)] |
|||
[Row(100,100)] |
|||
[MultipleAsserts] |
|||
public void CanSolveForRandomMatrix(int row, int col) |
|||
{ |
|||
var matrixA = MatrixLoader.GenerateRandomPositiveDefiniteDenseMatrix(row); |
|||
var matrixACopy = matrixA.Clone(); |
|||
var chol = matrixA.Cholesky(); |
|||
var matrixB = MatrixLoader.GenerateRandomDenseMatrix(row, col); |
|||
var matrixX = chol.Solve(matrixB); |
|||
|
|||
Assert.AreEqual(matrixB.RowCount, matrixX.RowCount); |
|||
Assert.AreEqual(matrixB.ColumnCount, matrixX.ColumnCount); |
|||
|
|||
var matrixBReconstruct = matrixA * matrixX; |
|||
|
|||
// Check the reconstruction.
|
|||
for (var i = 0; i < matrixB.RowCount; i++) |
|||
{ |
|||
for (var j = 0; j < matrixB.ColumnCount; j++) |
|||
{ |
|||
Assert.AreApproximatelyEqual(matrixB[i, j], matrixBReconstruct[i, j], 10e-3f); |
|||
} |
|||
} |
|||
|
|||
// 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.GenerateRandomPositiveDefiniteDenseMatrix(order); |
|||
var matrixACopy = matrixA.Clone(); |
|||
var chol = matrixA.Cholesky(); |
|||
var b = MatrixLoader.GenerateRandomDenseVector(order); |
|||
var bCopy = b.Clone(); |
|||
var x = new DenseVector(order); |
|||
chol.Solve(b, x); |
|||
|
|||
Assert.AreEqual(b.Count, x.Count); |
|||
|
|||
var bReconstruct = matrixA * x; |
|||
|
|||
// Check the reconstruction.
|
|||
for (var i = 0; i < order; i++) |
|||
{ |
|||
Assert.AreApproximatelyEqual(b[i], bReconstruct[i], 10e-3f); |
|||
} |
|||
|
|||
// Make sure A didn't change.
|
|||
for (var i = 0; i < matrixA.RowCount; i++) |
|||
{ |
|||
for (var j = 0; j < matrixA.ColumnCount; j++) |
|||
{ |
|||
Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]); |
|||
} |
|||
} |
|||
|
|||
// Make sure b didn't change.
|
|||
for (var i = 0; i < order; i++) |
|||
{ |
|||
Assert.AreEqual(bCopy[i], b[i]); |
|||
} |
|||
} |
|||
|
|||
[Test] |
|||
[Row(1, 1)] |
|||
[Row(2, 4)] |
|||
[Row(5, 8)] |
|||
[Row(10, 3)] |
|||
[Row(50, 10)] |
|||
[Row(100, 100)] |
|||
[MultipleAsserts] |
|||
public void CanSolveForRandomMatrixWhenResultMatrixGiven(int row, int col) |
|||
{ |
|||
var matrixA = MatrixLoader.GenerateRandomPositiveDefiniteDenseMatrix(row); |
|||
var matrixACopy = matrixA.Clone(); |
|||
var chol = matrixA.Cholesky(); |
|||
var matrixB = MatrixLoader.GenerateRandomDenseMatrix(row, col); |
|||
var matrixBCopy = matrixB.Clone(); |
|||
var matrixX = new DenseMatrix(row, col); |
|||
chol.Solve(matrixB, matrixX); |
|||
|
|||
Assert.AreEqual(matrixB.RowCount, matrixX.RowCount); |
|||
Assert.AreEqual(matrixB.ColumnCount, matrixX.ColumnCount); |
|||
|
|||
var matrixBReconstruct = matrixA * matrixX; |
|||
|
|||
// Check the reconstruction.
|
|||
for (var i = 0; i < matrixB.RowCount; i++) |
|||
{ |
|||
for (var j = 0; j < matrixB.ColumnCount; j++) |
|||
{ |
|||
Assert.AreApproximatelyEqual(matrixB[i, j], matrixBReconstruct[i, j], 10e-3f); |
|||
} |
|||
} |
|||
|
|||
// 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]); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,331 @@ |
|||
// <copyright file="GramSchmidtTests.cs" company="Math.NET">
|
|||
// Math.NET Numerics, part of the Math.NET Project
|
|||
// http://numerics.mathdotnet.com
|
|||
// http://github.com/mathnet/mathnet-numerics
|
|||
// http://mathnetnumerics.codeplex.com
|
|||
//
|
|||
// Copyright (c) 2009-2010 Math.NET
|
|||
//
|
|||
// Permission is hereby granted, free of charge, to any person
|
|||
// obtaining a copy of this software and associated documentation
|
|||
// files (the "Software"), to deal in the Software without
|
|||
// restriction, including without limitation the rights to use,
|
|||
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|||
// copies of the Software, and to permit persons to whom the
|
|||
// Software is furnished to do so, subject to the following
|
|||
// conditions:
|
|||
//
|
|||
// The above copyright notice and this permission notice shall be
|
|||
// included in all copies or substantial portions of the Software.
|
|||
//
|
|||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|||
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|||
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|||
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|||
// OTHER DEALINGS IN THE SOFTWARE.
|
|||
// </copyright>
|
|||
|
|||
namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single.Factorization |
|||
{ |
|||
using LinearAlgebra.Generic.Factorization; |
|||
using MbUnit.Framework; |
|||
using LinearAlgebra.Single.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], 10e-5f); |
|||
} |
|||
} |
|||
} |
|||
|
|||
[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], 10e-5f); |
|||
} |
|||
|
|||
// 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], 10e-5f); |
|||
} |
|||
} |
|||
|
|||
// 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], 10e-5f); |
|||
} |
|||
|
|||
// 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], 10e-5f); |
|||
} |
|||
} |
|||
|
|||
// 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]); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,364 @@ |
|||
// <copyright file="LUTests.cs" company="Math.NET">
|
|||
// Math.NET Numerics, part of the Math.NET Project
|
|||
// http://numerics.mathdotnet.com
|
|||
// http://github.com/mathnet/mathnet-numerics
|
|||
// http://mathnetnumerics.codeplex.com
|
|||
//
|
|||
// Copyright (c) 2009-2010 Math.NET
|
|||
//
|
|||
// Permission is hereby granted, free of charge, to any person
|
|||
// obtaining a copy of this software and associated documentation
|
|||
// files (the "Software"), to deal in the Software without
|
|||
// restriction, including without limitation the rights to use,
|
|||
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|||
// copies of the Software, and to permit persons to whom the
|
|||
// Software is furnished to do so, subject to the following
|
|||
// conditions:
|
|||
//
|
|||
// The above copyright notice and this permission notice shall be
|
|||
// included in all copies or substantial portions of the Software.
|
|||
//
|
|||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|||
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|||
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|||
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|||
// OTHER DEALINGS IN THE SOFTWARE.
|
|||
// </copyright>
|
|||
|
|||
namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single.Factorization |
|||
{ |
|||
using LinearAlgebra.Generic.Factorization; |
|||
using MbUnit.Framework; |
|||
using LinearAlgebra.Single; |
|||
|
|||
public class LUTests |
|||
{ |
|||
[Test] |
|||
[Row(1)] |
|||
[Row(10)] |
|||
[Row(100)] |
|||
public void CanFactorizeIdentity(int order) |
|||
{ |
|||
var matrixI = DenseMatrix.Identity(order); |
|||
var factorLU = matrixI.LU(); |
|||
|
|||
// Check lower triangular part.
|
|||
var matrixL = factorLU.L; |
|||
Assert.AreEqual(matrixI.RowCount, matrixL.RowCount); |
|||
Assert.AreEqual(matrixI.ColumnCount, matrixL.ColumnCount); |
|||
for (var i = 0; i < matrixL.RowCount; i++) |
|||
{ |
|||
for (var j = 0; j < matrixL.ColumnCount; j++) |
|||
{ |
|||
Assert.AreEqual(i == j ? 1.0 : 0.0, matrixL[i, j]); |
|||
} |
|||
} |
|||
|
|||
// Check upper triangular part.
|
|||
var matrixU = factorLU.U; |
|||
Assert.AreEqual(matrixI.RowCount, matrixU.RowCount); |
|||
Assert.AreEqual(matrixI.ColumnCount, matrixU.ColumnCount); |
|||
for (var i = 0; i < matrixU.RowCount; i++) |
|||
{ |
|||
for (var j = 0; j < matrixU.ColumnCount; j++) |
|||
{ |
|||
Assert.AreEqual(i == j ? 1.0 : 0.0, matrixU[i, j]); |
|||
} |
|||
} |
|||
} |
|||
|
|||
[Test] |
|||
[Row(3,5)] |
|||
[Row(5,3)] |
|||
[ExpectedArgumentException] |
|||
public void LUFailsWithNonSquareMatrix(int row, int col) |
|||
{ |
|||
var I = new DenseMatrix(row, col); |
|||
I.LU(); |
|||
} |
|||
|
|||
[Test] |
|||
[Row(1)] |
|||
[Row(10)] |
|||
[Row(100)] |
|||
public void IdentityDeterminantIsOne(int order) |
|||
{ |
|||
var I = DenseMatrix.Identity(order); |
|||
var lu = I.LU(); |
|||
Assert.AreEqual(1.0, lu.Determinant); |
|||
} |
|||
|
|||
[Test] |
|||
[Row(1)] |
|||
[Row(2)] |
|||
[Row(5)] |
|||
[Row(10)] |
|||
[Row(50)] |
|||
[Row(100)] |
|||
[MultipleAsserts] |
|||
public void CanFactorizeRandomMatrix(int order) |
|||
{ |
|||
var matrixX = MatrixLoader.GenerateRandomDenseMatrix(order, order); |
|||
var factorLU = matrixX.LU(); |
|||
var matrixL = factorLU.L; |
|||
var matrixU = factorLU.U; |
|||
|
|||
// Make sure the factors have the right dimensions.
|
|||
Assert.AreEqual(order, matrixL.RowCount); |
|||
Assert.AreEqual(order, matrixL.ColumnCount); |
|||
Assert.AreEqual(order, matrixU.RowCount); |
|||
Assert.AreEqual(order, matrixU.ColumnCount); |
|||
|
|||
// Make sure the L factor is lower triangular.
|
|||
for (var i = 0; i < matrixL.RowCount; i++) |
|||
{ |
|||
Assert.AreEqual(1.0, matrixL[i, i]); |
|||
for (var j = i+1; j < matrixL.ColumnCount; j++) |
|||
{ |
|||
Assert.AreEqual(0.0, matrixL[i, j]); |
|||
} |
|||
} |
|||
|
|||
// Make sure the U factor is upper triangular.
|
|||
for (var i = 0; i < matrixL.RowCount; i++) |
|||
{ |
|||
for (var j = 0; j < i; j++) |
|||
{ |
|||
Assert.AreEqual(0.0, matrixU[i, j]); |
|||
} |
|||
} |
|||
|
|||
// Make sure the LU factor times it's transpose is the original matrix.
|
|||
var matrixXfromLU = matrixL * matrixU; |
|||
var permutationInverse = factorLU.P.Inverse(); |
|||
matrixXfromLU.PermuteRows(permutationInverse); |
|||
for (var i = 0; i < matrixXfromLU.RowCount; i++) |
|||
{ |
|||
for (var j = 0; j < matrixXfromLU.ColumnCount; j++) |
|||
{ |
|||
Assert.AreApproximatelyEqual(matrixX[i, j], matrixXfromLU[i, j], 10e-5f); |
|||
} |
|||
} |
|||
} |
|||
|
|||
[Test] |
|||
[Row(1)] |
|||
[Row(2)] |
|||
[Row(5)] |
|||
[Row(10)] |
|||
[Row(50)] |
|||
[Row(100)] |
|||
[MultipleAsserts] |
|||
public void CanSolveForRandomVector(int order) |
|||
{ |
|||
var matrixA = MatrixLoader.GenerateRandomDenseMatrix(order, order); |
|||
var matrixACopy = matrixA.Clone(); |
|||
var factorLU = matrixA.LU(); |
|||
|
|||
var vectorb = MatrixLoader.GenerateRandomDenseVector(order); |
|||
var resultx = factorLU.Solve(vectorb); |
|||
|
|||
Assert.AreEqual(matrixA.ColumnCount, resultx.Count); |
|||
|
|||
var bReconstruct = matrixA * resultx; |
|||
|
|||
// Check the reconstruction.
|
|||
for (var i = 0; i < order; i++) |
|||
{ |
|||
Assert.AreApproximatelyEqual(vectorb[i], bReconstruct[i], 10e-5f); |
|||
} |
|||
|
|||
// Make sure A didn't change.
|
|||
for (var i = 0; i < matrixA.RowCount; i++) |
|||
{ |
|||
for (var j = 0; j < matrixA.ColumnCount; j++) |
|||
{ |
|||
Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]); |
|||
} |
|||
} |
|||
} |
|||
|
|||
[Test] |
|||
[Row(1)] |
|||
[Row(4)] |
|||
[Row(8)] |
|||
[Row(10)] |
|||
[Row(50)] |
|||
[Row(100)] |
|||
[MultipleAsserts] |
|||
public void CanSolveForRandomMatrix(int order) |
|||
{ |
|||
var matrixA = MatrixLoader.GenerateRandomDenseMatrix(order, order); |
|||
var matrixACopy = matrixA.Clone(); |
|||
var factorLU = matrixA.LU(); |
|||
|
|||
var matrixB = MatrixLoader.GenerateRandomDenseMatrix(order, order); |
|||
var matrixX = factorLU.Solve(matrixB); |
|||
|
|||
// The solution X row dimension is equal to the column dimension of A
|
|||
Assert.AreEqual(matrixA.ColumnCount, matrixX.RowCount); |
|||
// The solution X has the same number of columns as B
|
|||
Assert.AreEqual(matrixB.ColumnCount, matrixX.ColumnCount); |
|||
|
|||
var matrixBReconstruct = matrixA * matrixX; |
|||
|
|||
// Check the reconstruction.
|
|||
for (var i = 0; i < matrixB.RowCount; i++) |
|||
{ |
|||
for (var j = 0; j < matrixB.ColumnCount; j++) |
|||
{ |
|||
Assert.AreApproximatelyEqual(matrixB[i, j], matrixBReconstruct[i, j], 10e-5f); |
|||
} |
|||
} |
|||
|
|||
// Make sure A didn't change.
|
|||
for (var i = 0; i < matrixA.RowCount; i++) |
|||
{ |
|||
for (var j = 0; j < matrixA.ColumnCount; j++) |
|||
{ |
|||
Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]); |
|||
} |
|||
} |
|||
} |
|||
|
|||
[Test] |
|||
[Row(1)] |
|||
[Row(2)] |
|||
[Row(5)] |
|||
[Row(10)] |
|||
[Row(50)] |
|||
[Row(100)] |
|||
[MultipleAsserts] |
|||
public void CanSolveForRandomVectorWhenResultVectorGiven(int order) |
|||
{ |
|||
var matrixA = MatrixLoader.GenerateRandomDenseMatrix(order, order); |
|||
var matrixACopy = matrixA.Clone(); |
|||
var factorLU = matrixA.LU(); |
|||
var vectorb = MatrixLoader.GenerateRandomDenseVector(order); |
|||
var vectorbCopy = vectorb.Clone(); |
|||
var resultx = new DenseVector(order); |
|||
factorLU.Solve(vectorb, resultx); |
|||
|
|||
Assert.AreEqual(vectorb.Count, resultx.Count); |
|||
|
|||
var bReconstruct = matrixA * resultx; |
|||
|
|||
// Check the reconstruction.
|
|||
for (var i = 0; i < vectorb.Count; i++) |
|||
{ |
|||
Assert.AreApproximatelyEqual(vectorb[i], bReconstruct[i], 10e-5f); |
|||
} |
|||
|
|||
// Make sure A didn't change.
|
|||
for (var i = 0; i < matrixA.RowCount; i++) |
|||
{ |
|||
for (var j = 0; j < matrixA.ColumnCount; j++) |
|||
{ |
|||
Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]); |
|||
} |
|||
} |
|||
|
|||
// Make sure b didn't change.
|
|||
for (var i = 0; i < vectorb.Count; i++) |
|||
{ |
|||
Assert.AreEqual(vectorbCopy[i], vectorb[i]); |
|||
} |
|||
} |
|||
|
|||
[Test] |
|||
[Row(1)] |
|||
[Row(4)] |
|||
[Row(8)] |
|||
[Row(10)] |
|||
[Row(50)] |
|||
[Row(100)] |
|||
[MultipleAsserts] |
|||
public void CanSolveForRandomMatrixWhenResultMatrixGiven(int order) |
|||
{ |
|||
var matrixA = MatrixLoader.GenerateRandomDenseMatrix(order, order); |
|||
var matrixACopy = matrixA.Clone(); |
|||
var factorLU = matrixA.LU(); |
|||
|
|||
var matrixB = MatrixLoader.GenerateRandomDenseMatrix(order, order); |
|||
var matrixBCopy = matrixB.Clone(); |
|||
|
|||
var matrixX = new DenseMatrix(order, order); |
|||
factorLU.Solve(matrixB, matrixX); |
|||
|
|||
// The solution X row dimension is equal to the column dimension of A
|
|||
Assert.AreEqual(matrixA.ColumnCount, matrixX.RowCount); |
|||
// The solution X has the same number of columns as B
|
|||
Assert.AreEqual(matrixB.ColumnCount, matrixX.ColumnCount); |
|||
|
|||
var matrixBReconstruct = matrixA * matrixX; |
|||
|
|||
// Check the reconstruction.
|
|||
for (var i = 0; i < matrixB.RowCount; i++) |
|||
{ |
|||
for (var j = 0; j < matrixB.ColumnCount; j++) |
|||
{ |
|||
Assert.AreApproximatelyEqual(matrixB[i, j], matrixBReconstruct[i, j], 10e-5f); |
|||
} |
|||
} |
|||
|
|||
// Make sure A didn't change.
|
|||
for (var i = 0; i < matrixA.RowCount; i++) |
|||
{ |
|||
for (var j = 0; j < matrixA.ColumnCount; j++) |
|||
{ |
|||
Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]); |
|||
} |
|||
} |
|||
|
|||
// Make sure B didn't change.
|
|||
for (var i = 0; i < matrixB.RowCount; i++) |
|||
{ |
|||
for (var j = 0; j < matrixB.ColumnCount; j++) |
|||
{ |
|||
Assert.AreEqual(matrixBCopy[i, j], matrixB[i, j]); |
|||
} |
|||
} |
|||
} |
|||
|
|||
[Test] |
|||
[Row(1)] |
|||
[Row(4)] |
|||
[Row(8)] |
|||
[Row(10)] |
|||
[Row(50)] |
|||
[Row(100)] |
|||
[MultipleAsserts] |
|||
public void CanInverse(int order) |
|||
{ |
|||
var matrixA = MatrixLoader.GenerateRandomDenseMatrix(order, order); |
|||
var matrixACopy = matrixA.Clone(); |
|||
var factorLU = matrixA.LU(); |
|||
|
|||
var matrixAInverse = factorLU.Inverse(); |
|||
|
|||
// The inverse dimension is equal A
|
|||
Assert.AreEqual(matrixAInverse.RowCount, matrixAInverse.RowCount); |
|||
Assert.AreEqual(matrixAInverse.ColumnCount, matrixAInverse.ColumnCount); |
|||
|
|||
var matrixIdentity = matrixA * matrixAInverse; |
|||
|
|||
// Make sure A didn't change.
|
|||
for (var i = 0; i < matrixA.RowCount; i++) |
|||
{ |
|||
for (var j = 0; j < matrixA.ColumnCount; j++) |
|||
{ |
|||
Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]); |
|||
} |
|||
} |
|||
|
|||
// Check if multiplication of A and AI produced identity matrix.
|
|||
for (var i = 0; i < matrixIdentity.RowCount; i++) |
|||
{ |
|||
Assert.AreApproximatelyEqual(matrixIdentity[i, i], 1.0f, 10e-5f); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,318 @@ |
|||
// <copyright file="QRTests.cs" company="Math.NET">
|
|||
// Math.NET Numerics, part of the Math.NET Project
|
|||
// http://numerics.mathdotnet.com
|
|||
// http://github.com/mathnet/mathnet-numerics
|
|||
// http://mathnetnumerics.codeplex.com
|
|||
//
|
|||
// Copyright (c) 2009-2010 Math.NET
|
|||
//
|
|||
// Permission is hereby granted, free of charge, to any person
|
|||
// obtaining a copy of this software and associated documentation
|
|||
// files (the "Software"), to deal in the Software without
|
|||
// restriction, including without limitation the rights to use,
|
|||
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|||
// copies of the Software, and to permit persons to whom the
|
|||
// Software is furnished to do so, subject to the following
|
|||
// conditions:
|
|||
//
|
|||
// The above copyright notice and this permission notice shall be
|
|||
// included in all copies or substantial portions of the Software.
|
|||
//
|
|||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|||
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|||
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|||
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|||
// OTHER DEALINGS IN THE SOFTWARE.
|
|||
// </copyright>
|
|||
|
|||
namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single.Factorization |
|||
{ |
|||
using LinearAlgebra.Generic.Factorization; |
|||
using MbUnit.Framework; |
|||
using LinearAlgebra.Single; |
|||
using LinearAlgebra.Single.Factorization; |
|||
|
|||
public class QRTests |
|||
{ |
|||
|
|||
[Test] |
|||
[ExpectedArgumentNullException] |
|||
public void ConstructorNull() |
|||
{ |
|||
new DenseQR(null); |
|||
} |
|||
|
|||
[Test] |
|||
[ExpectedArgumentException] |
|||
public void WideMatrixThrowsInvalidMatrixOperationException() |
|||
{ |
|||
new DenseQR(new DenseMatrix(3, 4)); |
|||
} |
|||
|
|||
[Test] |
|||
[Row(1)] |
|||
[Row(10)] |
|||
[Row(100)] |
|||
public void CanFactorizeIdentity(int order) |
|||
{ |
|||
var I = DenseMatrix.Identity(order); |
|||
var factorQR = I.QR(); |
|||
|
|||
Assert.AreEqual(I.RowCount, factorQR.R.RowCount); |
|||
Assert.AreEqual(I.ColumnCount, factorQR.R.ColumnCount); |
|||
|
|||
for (var i = 0; i < factorQR.R.RowCount; i++) |
|||
{ |
|||
for (var j = 0; j < factorQR.R.ColumnCount; j++) |
|||
{ |
|||
if (i == j) |
|||
{ |
|||
Assert.AreEqual(-1.0, factorQR.R[i, j]); |
|||
} |
|||
else |
|||
{ |
|||
Assert.AreEqual(0.0, factorQR.R[i, j]); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
[Test] |
|||
[Row(1)] |
|||
[Row(10)] |
|||
[Row(100)] |
|||
public void IdentityDeterminantIsOne(int order) |
|||
{ |
|||
var I = DenseMatrix.Identity(order); |
|||
var factorQR = I.QR(); |
|||
Assert.AreEqual(1.0, factorQR.Determinant); |
|||
} |
|||
|
|||
[Test] |
|||
[Row(1,1)] |
|||
[Row(2,2)] |
|||
[Row(5,5)] |
|||
[Row(10,6)] |
|||
[Row(50,48)] |
|||
[Row(100,98)] |
|||
[MultipleAsserts] |
|||
public void CanFactorizeRandomMatrix(int row, int column) |
|||
{ |
|||
var matrixA = MatrixLoader.GenerateRandomDenseMatrix(row, column); |
|||
var factorQR = matrixA.QR(); |
|||
|
|||
// Make sure the R has the right dimensions.
|
|||
Assert.AreEqual(row, factorQR.R.RowCount); |
|||
Assert.AreEqual(column, factorQR.R.ColumnCount); |
|||
|
|||
// Make sure the Q has the right dimensions.
|
|||
Assert.AreEqual(row, factorQR.Q.RowCount); |
|||
Assert.AreEqual(row, factorQR.Q.ColumnCount); |
|||
|
|||
// Make sure the R factor is upper triangular.
|
|||
for (var i = 0; i < factorQR.R.RowCount; i++) |
|||
{ |
|||
for (var j = 0; j < factorQR.R.ColumnCount; j++) |
|||
{ |
|||
if (i > j) |
|||
{ |
|||
Assert.AreEqual(0.0, factorQR.R[i, j]); |
|||
} |
|||
} |
|||
} |
|||
|
|||
// Make sure the Q*R is the original matrix.
|
|||
var matrixQfromR = factorQR.Q * factorQR.R; |
|||
for (int i = 0; i < matrixQfromR.RowCount; i++) |
|||
{ |
|||
for (int j = 0; j < matrixQfromR.ColumnCount; j++) |
|||
{ |
|||
Assert.AreApproximatelyEqual(matrixA[i, j], matrixQfromR[i, j], 10e-5f); |
|||
} |
|||
} |
|||
} |
|||
|
|||
[Test] |
|||
[Row(1)] |
|||
[Row(2)] |
|||
[Row(5)] |
|||
[Row(10)] |
|||
[Row(50)] |
|||
[Row(100)] |
|||
[MultipleAsserts] |
|||
public void CanSolveForRandomVector(int order) |
|||
{ |
|||
var matrixA = MatrixLoader.GenerateRandomDenseMatrix(order, order); |
|||
var matrixACopy = matrixA.Clone(); |
|||
var factorQR = matrixA.QR(); |
|||
|
|||
var vectorb = MatrixLoader.GenerateRandomDenseVector(order); |
|||
var resultx = factorQR.Solve(vectorb); |
|||
|
|||
Assert.AreEqual(matrixA.ColumnCount, resultx.Count); |
|||
|
|||
var bReconstruct = matrixA * resultx; |
|||
|
|||
// Check the reconstruction.
|
|||
for (var i = 0; i < order; i++) |
|||
{ |
|||
Assert.AreApproximatelyEqual(vectorb[i], bReconstruct[i], 10e-5f); |
|||
} |
|||
|
|||
// Make sure A didn't change.
|
|||
for (var i = 0; i < matrixA.RowCount; i++) |
|||
{ |
|||
for (var j = 0; j < matrixA.ColumnCount; j++) |
|||
{ |
|||
Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]); |
|||
} |
|||
} |
|||
} |
|||
|
|||
[Test] |
|||
[Row(1)] |
|||
[Row(4)] |
|||
[Row(8)] |
|||
[Row(10)] |
|||
[Row(50)] |
|||
[Row(100)] |
|||
[MultipleAsserts] |
|||
public void CanSolveForRandomMatrix(int order) |
|||
{ |
|||
var matrixA = MatrixLoader.GenerateRandomDenseMatrix(order, order); |
|||
var matrixACopy = matrixA.Clone(); |
|||
var factorQR = matrixA.QR(); |
|||
|
|||
var matrixB = MatrixLoader.GenerateRandomDenseMatrix(order, order); |
|||
var matrixX = factorQR.Solve(matrixB); |
|||
|
|||
// The solution X row dimension is equal to the column dimension of A
|
|||
Assert.AreEqual(matrixA.ColumnCount, matrixX.RowCount); |
|||
// The solution X has the same number of columns as B
|
|||
Assert.AreEqual(matrixB.ColumnCount, matrixX.ColumnCount); |
|||
|
|||
var matrixBReconstruct = matrixA * matrixX; |
|||
|
|||
// Check the reconstruction.
|
|||
for (var i = 0; i < matrixB.RowCount; i++) |
|||
{ |
|||
for (var j = 0; j < matrixB.ColumnCount; j++) |
|||
{ |
|||
Assert.AreApproximatelyEqual(matrixB[i, j], matrixBReconstruct[i, j], 10e-5f); |
|||
} |
|||
} |
|||
|
|||
// Make sure A didn't change.
|
|||
for (var i = 0; i < matrixA.RowCount; i++) |
|||
{ |
|||
for (var j = 0; j < matrixA.ColumnCount; j++) |
|||
{ |
|||
Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]); |
|||
} |
|||
} |
|||
} |
|||
|
|||
[Test] |
|||
[Row(1)] |
|||
[Row(2)] |
|||
[Row(5)] |
|||
[Row(10)] |
|||
[Row(50)] |
|||
[Row(100)] |
|||
[MultipleAsserts] |
|||
public void CanSolveForRandomVectorWhenResultVectorGiven(int order) |
|||
{ |
|||
var matrixA = MatrixLoader.GenerateRandomDenseMatrix(order, order); |
|||
var matrixACopy = matrixA.Clone(); |
|||
var factorQR = matrixA.QR(); |
|||
var vectorb = MatrixLoader.GenerateRandomDenseVector(order); |
|||
var vectorbCopy = vectorb.Clone(); |
|||
var resultx = new DenseVector(order); |
|||
factorQR.Solve(vectorb,resultx); |
|||
|
|||
Assert.AreEqual(vectorb.Count, resultx.Count); |
|||
|
|||
var bReconstruct = matrixA * resultx; |
|||
|
|||
// Check the reconstruction.
|
|||
for (var i = 0; i < vectorb.Count; i++) |
|||
{ |
|||
Assert.AreApproximatelyEqual(vectorb[i], bReconstruct[i], 10e-5f); |
|||
} |
|||
|
|||
// Make sure A didn't change.
|
|||
for (var i = 0; i < matrixA.RowCount; i++) |
|||
{ |
|||
for (var j = 0; j < matrixA.ColumnCount; j++) |
|||
{ |
|||
Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]); |
|||
} |
|||
} |
|||
|
|||
// Make sure b didn't change.
|
|||
for (var i = 0; i < vectorb.Count; i++) |
|||
{ |
|||
Assert.AreEqual(vectorbCopy[i], vectorb[i]); |
|||
} |
|||
} |
|||
|
|||
[Test] |
|||
[Row(1)] |
|||
[Row(4)] |
|||
[Row(8)] |
|||
[Row(10)] |
|||
[Row(50)] |
|||
[Row(100)] |
|||
[MultipleAsserts] |
|||
public void CanSolveForRandomMatrixWhenResultMatrixGiven(int order) |
|||
{ |
|||
var matrixA = MatrixLoader.GenerateRandomDenseMatrix(order, order); |
|||
var matrixACopy = matrixA.Clone(); |
|||
var factorQR = matrixA.QR(); |
|||
|
|||
var matrixB = MatrixLoader.GenerateRandomDenseMatrix(order, order); |
|||
var matrixBCopy = matrixB.Clone(); |
|||
|
|||
var matrixX = new DenseMatrix(order, order); |
|||
factorQR.Solve(matrixB,matrixX); |
|||
|
|||
// The solution X row dimension is equal to the column dimension of A
|
|||
Assert.AreEqual(matrixA.ColumnCount, matrixX.RowCount); |
|||
// The solution X has the same number of columns as B
|
|||
Assert.AreEqual(matrixB.ColumnCount, matrixX.ColumnCount); |
|||
|
|||
var matrixBReconstruct = matrixA * matrixX; |
|||
|
|||
// Check the reconstruction.
|
|||
for (var i = 0; i < matrixB.RowCount; i++) |
|||
{ |
|||
for (var j = 0; j < matrixB.ColumnCount; j++) |
|||
{ |
|||
Assert.AreApproximatelyEqual(matrixB[i, j], matrixBReconstruct[i, j], 10e-5f); |
|||
} |
|||
} |
|||
|
|||
// 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]); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,371 @@ |
|||
// <copyright file="SvdTests.cs" company="Math.NET">
|
|||
// Math.NET Numerics, part of the Math.NET Project
|
|||
// http://numerics.mathdotnet.com
|
|||
// http://github.com/mathnet/mathnet-numerics
|
|||
// http://mathnetnumerics.codeplex.com
|
|||
//
|
|||
// Copyright (c) 2009-2010 Math.NET
|
|||
//
|
|||
// Permission is hereby granted, free of charge, to any person
|
|||
// obtaining a copy of this software and associated documentation
|
|||
// files (the "Software"), to deal in the Software without
|
|||
// restriction, including without limitation the rights to use,
|
|||
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|||
// copies of the Software, and to permit persons to whom the
|
|||
// Software is furnished to do so, subject to the following
|
|||
// conditions:
|
|||
//
|
|||
// The above copyright notice and this permission notice shall be
|
|||
// included in all copies or substantial portions of the Software.
|
|||
//
|
|||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|||
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|||
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|||
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|||
// OTHER DEALINGS IN THE SOFTWARE.
|
|||
// </copyright>
|
|||
|
|||
namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single.Factorization |
|||
{ |
|||
using System; |
|||
using LinearAlgebra.Generic.Factorization; |
|||
using MbUnit.Framework; |
|||
using LinearAlgebra.Single; |
|||
using LinearAlgebra.Single.Factorization; |
|||
|
|||
public class SvdTests |
|||
{ |
|||
|
|||
[Test] |
|||
[ExpectedArgumentNullException] |
|||
public void ConstructorNull() |
|||
{ |
|||
new DenseSvd(null, true); |
|||
} |
|||
|
|||
[Test] |
|||
[Row(1)] |
|||
[Row(10)] |
|||
[Row(100)] |
|||
public void CanFactorizeIdentity(int order) |
|||
{ |
|||
var I = DenseMatrix.Identity(order); |
|||
var factorSvd = I.Svd(true); |
|||
|
|||
Assert.AreEqual(I.RowCount, factorSvd.U().RowCount); |
|||
Assert.AreEqual(I.RowCount, factorSvd.U().ColumnCount); |
|||
|
|||
Assert.AreEqual(I.ColumnCount, factorSvd.VT().RowCount); |
|||
Assert.AreEqual(I.ColumnCount, factorSvd.VT().ColumnCount); |
|||
|
|||
Assert.AreEqual(I.RowCount, factorSvd.W().RowCount); |
|||
Assert.AreEqual(I.ColumnCount, factorSvd.W().ColumnCount); |
|||
|
|||
for (var i = 0; i < factorSvd.W().RowCount; i++) |
|||
{ |
|||
for (var j = 0; j < factorSvd.W().ColumnCount; j++) |
|||
{ |
|||
Assert.AreEqual(i == j ? 1.0 : 0.0, factorSvd.W()[i, j]); |
|||
} |
|||
} |
|||
} |
|||
|
|||
[Test] |
|||
[Row(1,1)] |
|||
[Row(2,2)] |
|||
[Row(5,5)] |
|||
[Row(10,6)] |
|||
[Row(48,52)] |
|||
[Row(100,93)] |
|||
[MultipleAsserts] |
|||
public void CanFactorizeRandomMatrix(int row, int column) |
|||
{ |
|||
var matrixA = MatrixLoader.GenerateRandomDenseMatrix(row, column); |
|||
var factorSvd = matrixA.Svd(true); |
|||
|
|||
// Make sure the U has the right dimensions.
|
|||
Assert.AreEqual(row, factorSvd.U().RowCount); |
|||
Assert.AreEqual(row, factorSvd.U().ColumnCount); |
|||
|
|||
// Make sure the VT has the right dimensions.
|
|||
Assert.AreEqual(column, factorSvd.VT().RowCount); |
|||
Assert.AreEqual(column, factorSvd.VT().ColumnCount); |
|||
|
|||
// Make sure the W has the right dimensions.
|
|||
Assert.AreEqual(row, factorSvd.W().RowCount); |
|||
Assert.AreEqual(column, factorSvd.W().ColumnCount); |
|||
|
|||
// Make sure the U*W*VT is the original matrix.
|
|||
var matrix = factorSvd.U() * factorSvd.W() * factorSvd.VT(); |
|||
for (var i = 0; i < matrix.RowCount; i++) |
|||
{ |
|||
for (var j = 0; j < matrix.ColumnCount; j++) |
|||
{ |
|||
Assert.AreApproximatelyEqual(matrixA[i, j], matrix[i, j], 10e-5f); |
|||
} |
|||
} |
|||
} |
|||
|
|||
[Test] |
|||
[Row(10, 8)] |
|||
[Row(48, 52)] |
|||
[Row(100, 93)] |
|||
[MultipleAsserts] |
|||
public void CheckRankOfNonSquare(int row, int column) |
|||
{ |
|||
var matrixA = MatrixLoader.GenerateRandomDenseMatrix(row, column); |
|||
var factorSvd = matrixA.Svd(true); |
|||
|
|||
var mn = Math.Min(row, column); |
|||
Assert.AreEqual(factorSvd.Rank, mn); |
|||
} |
|||
|
|||
[Test] |
|||
[Row(1)] |
|||
[Row(2)] |
|||
[Row(5)] |
|||
[Row(9)] |
|||
[Row(50)] |
|||
[Row(90)] |
|||
[MultipleAsserts] |
|||
public void CheckRankSquare(int order) |
|||
{ |
|||
var matrixA = MatrixLoader.GenerateRandomDenseMatrix(order, order); |
|||
var factorSvd = matrixA.Svd(true); |
|||
|
|||
if (factorSvd.Determinant != 0) |
|||
{ |
|||
Assert.AreEqual(factorSvd.Rank, order); |
|||
} |
|||
else |
|||
{ |
|||
Assert.AreEqual(factorSvd.Rank, order - 1); |
|||
} |
|||
} |
|||
|
|||
[Test] |
|||
[Row(10)] |
|||
[Row(50)] |
|||
[Row(100)] |
|||
[MultipleAsserts] |
|||
public void CheckRankOfSquareSingular(int order) |
|||
{ |
|||
var matrixA = new DenseMatrix(order, order); |
|||
matrixA[0, 0] = 1; |
|||
matrixA[order - 1, order - 1] = 1; |
|||
for (var i = 1; i < order - 1; i++) |
|||
{ |
|||
matrixA[i, i - 1] = 1; |
|||
matrixA[i, i + 1] = 1; |
|||
matrixA[i - 1, i] = 1; |
|||
matrixA[i + 1, i] = 1; |
|||
} |
|||
var factorSvd = matrixA.Svd(true); |
|||
|
|||
Assert.AreEqual(factorSvd.Determinant, 0.0); |
|||
Assert.AreEqual(factorSvd.Rank, order - 1); |
|||
} |
|||
|
|||
[Test] |
|||
[ExpectedException(typeof(InvalidOperationException))] |
|||
public void CannotSolveMatrixIfVectorsNotComputed() |
|||
{ |
|||
var matrixA = MatrixLoader.GenerateRandomDenseMatrix(10, 10); |
|||
var factorSvd = matrixA.Svd(false); |
|||
|
|||
var matrixB = MatrixLoader.GenerateRandomDenseMatrix(10, 10); |
|||
factorSvd.Solve(matrixB); |
|||
} |
|||
|
|||
[Test] |
|||
[ExpectedException(typeof(InvalidOperationException))] |
|||
public void CannotSolveVectorIfVectorsNotComputed() |
|||
{ |
|||
var matrixA = MatrixLoader.GenerateRandomDenseMatrix(10, 10); |
|||
var factorSvd = matrixA.Svd(false); |
|||
|
|||
var vectorb = MatrixLoader.GenerateRandomDenseVector(10); |
|||
factorSvd.Solve(vectorb); |
|||
} |
|||
|
|||
[Test] |
|||
[Row(1, 1)] |
|||
[Row(2, 2)] |
|||
[Row(5, 5)] |
|||
[Row(9, 10)] |
|||
[Row(50, 50)] |
|||
[Row(90, 100)] |
|||
[MultipleAsserts] |
|||
public void CanSolveForRandomVector(int row, int column) |
|||
{ |
|||
var matrixA = MatrixLoader.GenerateRandomDenseMatrix(row, column); |
|||
var matrixACopy = matrixA.Clone(); |
|||
var factorSvd = matrixA.Svd(true); |
|||
|
|||
var vectorb = MatrixLoader.GenerateRandomDenseVector(row); |
|||
var resultx = factorSvd.Solve(vectorb); |
|||
|
|||
Assert.AreEqual(matrixA.ColumnCount, resultx.Count); |
|||
|
|||
var bReconstruct = matrixA * resultx; |
|||
|
|||
// Check the reconstruction.
|
|||
for (var i = 0; i < vectorb.Count; i++) |
|||
{ |
|||
Assert.AreApproximatelyEqual(vectorb[i], bReconstruct[i], 10e-5f); |
|||
} |
|||
|
|||
// Make sure A didn't change.
|
|||
for (var i = 0; i < matrixA.RowCount; i++) |
|||
{ |
|||
for (var j = 0; j < matrixA.ColumnCount; j++) |
|||
{ |
|||
Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]); |
|||
} |
|||
} |
|||
} |
|||
|
|||
[Test] |
|||
[Row(1, 1)] |
|||
[Row(4, 4)] |
|||
[Row(7, 8)] |
|||
[Row(10, 10)] |
|||
[Row(45, 50)] |
|||
[Row(80, 100)] |
|||
[MultipleAsserts] |
|||
public void CanSolveForRandomMatrix(int row, int count) |
|||
{ |
|||
var matrixA = MatrixLoader.GenerateRandomDenseMatrix(row, count); |
|||
var matrixACopy = matrixA.Clone(); |
|||
var factorSvd = matrixA.Svd(true); |
|||
|
|||
var matrixB = MatrixLoader.GenerateRandomDenseMatrix(row, count); |
|||
var matrixX = factorSvd.Solve(matrixB); |
|||
|
|||
// The solution X row dimension is equal to the column dimension of A
|
|||
Assert.AreEqual(matrixA.ColumnCount, matrixX.RowCount); |
|||
// The solution X has the same number of columns as B
|
|||
Assert.AreEqual(matrixB.ColumnCount, matrixX.ColumnCount); |
|||
|
|||
var matrixBReconstruct = matrixA * matrixX; |
|||
|
|||
// Check the reconstruction.
|
|||
for (var i = 0; i < matrixB.RowCount; i++) |
|||
{ |
|||
for (var j = 0; j < matrixB.ColumnCount; j++) |
|||
{ |
|||
Assert.AreApproximatelyEqual(matrixB[i, j], matrixBReconstruct[i, j], 10e-5f); |
|||
} |
|||
} |
|||
|
|||
// Make sure A didn't change.
|
|||
for (var i = 0; i < matrixA.RowCount; i++) |
|||
{ |
|||
for (var j = 0; j < matrixA.ColumnCount; j++) |
|||
{ |
|||
Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]); |
|||
} |
|||
} |
|||
} |
|||
|
|||
[Test] |
|||
[Row(1, 1)] |
|||
[Row(2, 2)] |
|||
[Row(5, 5)] |
|||
[Row(9, 10)] |
|||
[Row(50, 50)] |
|||
[Row(90, 100)] |
|||
[MultipleAsserts] |
|||
public void CanSolveForRandomVectorWhenResultVectorGiven(int row, int column) |
|||
{ |
|||
var matrixA = MatrixLoader.GenerateRandomDenseMatrix(row, column); |
|||
var matrixACopy = matrixA.Clone(); |
|||
var factorSvd = matrixA.Svd(true); |
|||
var vectorb = MatrixLoader.GenerateRandomDenseVector(row); |
|||
var vectorbCopy = vectorb.Clone(); |
|||
var resultx = new DenseVector(column); |
|||
factorSvd.Solve(vectorb,resultx); |
|||
|
|||
var bReconstruct = matrixA * resultx; |
|||
|
|||
// Check the reconstruction.
|
|||
for (var i = 0; i < vectorb.Count; i++) |
|||
{ |
|||
Assert.AreApproximatelyEqual(vectorb[i], bReconstruct[i], 10e-5f); |
|||
} |
|||
|
|||
// Make sure A didn't change.
|
|||
for (var i = 0; i < matrixA.RowCount; i++) |
|||
{ |
|||
for (var j = 0; j < matrixA.ColumnCount; j++) |
|||
{ |
|||
Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]); |
|||
} |
|||
} |
|||
|
|||
// Make sure b didn't change.
|
|||
for (var i = 0; i < vectorb.Count; i++) |
|||
{ |
|||
Assert.AreEqual(vectorbCopy[i], vectorb[i]); |
|||
} |
|||
} |
|||
|
|||
[Test] |
|||
[Row(1, 1)] |
|||
[Row(4, 4)] |
|||
[Row(7, 8)] |
|||
[Row(10, 10)] |
|||
[Row(45, 50)] |
|||
[Row(80, 100)] |
|||
[MultipleAsserts] |
|||
public void CanSolveForRandomMatrixWhenResultMatrixGiven(int row, int column) |
|||
{ |
|||
var matrixA = MatrixLoader.GenerateRandomDenseMatrix(row, column); |
|||
var matrixACopy = matrixA.Clone(); |
|||
var factorSvd = matrixA.Svd(true); |
|||
|
|||
var matrixB = MatrixLoader.GenerateRandomDenseMatrix(row, column); |
|||
var matrixBCopy = matrixB.Clone(); |
|||
|
|||
var matrixX = new DenseMatrix(column, column); |
|||
factorSvd.Solve(matrixB,matrixX); |
|||
|
|||
// The solution X row dimension is equal to the column dimension of A
|
|||
Assert.AreEqual(matrixA.ColumnCount, matrixX.RowCount); |
|||
// The solution X has the same number of columns as B
|
|||
Assert.AreEqual(matrixB.ColumnCount, matrixX.ColumnCount); |
|||
|
|||
var matrixBReconstruct = matrixA * matrixX; |
|||
|
|||
// Check the reconstruction.
|
|||
for (var i = 0; i < matrixB.RowCount; i++) |
|||
{ |
|||
for (var j = 0; j < matrixB.ColumnCount; j++) |
|||
{ |
|||
Assert.AreApproximatelyEqual(matrixB[i, j], matrixBReconstruct[i, j], 10e-5f); |
|||
} |
|||
} |
|||
|
|||
// 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]); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue