Browse Source

added Andriy's Householder QR, added ISolver, and fixed stylecop warnings

la-knuth
Marcus Cuda 16 years ago
parent
commit
f89831ebea
  1. 1344
      src/Numerics/Algorithms/LinearAlgebra/ManagedLinearAlgebraProvider.cs
  2. 37
      src/Numerics/LinearAlgebra/Double/Factorization/Cholesky.cs
  3. 12
      src/Numerics/LinearAlgebra/Double/Factorization/DenseCholesky.cs
  4. 28
      src/Numerics/LinearAlgebra/Double/Factorization/DenseLU.cs
  5. 183
      src/Numerics/LinearAlgebra/Double/Factorization/DenseQR.cs
  6. 15
      src/Numerics/LinearAlgebra/Double/Factorization/ExtensionMethods.cs
  7. 63
      src/Numerics/LinearAlgebra/Double/Factorization/LU.cs
  8. 195
      src/Numerics/LinearAlgebra/Double/Factorization/QR.cs
  9. 64
      src/Numerics/LinearAlgebra/Double/ISolver.cs
  10. 3
      src/Numerics/Numerics.csproj
  11. 9
      src/Silverlight/Silverlight.csproj
  12. 317
      src/UnitTests/LinearAlgebraTests/Double/Factorization/QRTests.cs
  13. 1
      src/UnitTests/UnitTests.csproj

1344
src/Numerics/Algorithms/LinearAlgebra/ManagedLinearAlgebraProvider.cs

File diff suppressed because it is too large

37
src/Numerics/LinearAlgebra/Double/Factorization/Cholesky.cs

@ -31,7 +31,6 @@
namespace MathNet.Numerics.LinearAlgebra.Double.Factorization
{
using System;
using Properties;
/// <summary>
/// <para>A class which encapsulates the functionality of a Cholesky factorization.</para>
@ -42,13 +41,8 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Factorization
/// 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 abstract class Cholesky
public abstract class Cholesky : ISolver
{
/// <summary>
/// Stores the Cholesky factor.
/// </summary>
protected Matrix mFactor;
/// <summary>
/// Internal method which routes the call to perform the Cholesky factorization to the appropriate class.
/// </summary>
@ -66,41 +60,44 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Factorization
}
/// <summary>
/// Returns the lower triangular form of the Cholesky matrix.
/// Gets or sets the lower triangular form of the Cholesky matrix.
/// </summary>
public virtual Matrix Factor
{
get { return mFactor; }
get;
protected set;
}
/// <summary>
/// The determinant of the matrix for which the Cholesky matrix was computed.
/// Gets the determinant of the matrix for which the Cholesky matrix was computed.
/// </summary>
public virtual double Determinant
{
get
{
double det = 1.0;
for (int j = 0; j < mFactor.RowCount; j++)
var det = 1.0;
for (var j = 0; j < Factor.RowCount; j++)
{
det *= (mFactor[j, j] * mFactor[j, j]);
det *= Factor[j, j] * Factor[j, j];
}
return det;
}
}
/// <summary>
/// The log determinant of the matrix for which the Cholesky matrix was computed.
/// Gets the log determinant of the matrix for which the Cholesky matrix was computed.
/// </summary>
public virtual double DeterminantLn
{
get
{
double det = 0.0;
for (int j = 0; j < mFactor.RowCount; j++)
var det = 0.0;
for (var j = 0; j < Factor.RowCount; j++)
{
det += (2.0 * Math.Log(mFactor[j, j]));
det += 2.0 * Math.Log(Factor[j, j]);
}
return det;
}
}
@ -118,9 +115,9 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Factorization
throw new ArgumentNullException("input");
}
var X = input.CreateMatrix(input.RowCount, input.ColumnCount);
Solve(input, X);
return X;
var x = input.CreateMatrix(input.RowCount, input.ColumnCount);
Solve(input, x);
return x;
}
/// <summary>

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

@ -49,7 +49,7 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Factorization
/// 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 <b>null</b>.</exception>
/// <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)
@ -67,7 +67,7 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Factorization
// 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);
mFactor = factor;
Factor = factor;
}
/// <summary>
@ -99,7 +99,7 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Factorization
throw new ArgumentException(Resources.ArgumentMatrixSameColumnDimension);
}
if (input.RowCount != mFactor.RowCount)
if (input.RowCount != Factor.RowCount)
{
throw new ArgumentException(Resources.ArgumentMatrixDimensions);
}
@ -120,7 +120,7 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Factorization
Buffer.BlockCopy(dinput.Data, 0, dresult.Data, 0, dinput.Data.Length * Constants.SizeOfDouble);
// Cholesky solve by overwriting result.
var dfactor = mFactor as DenseMatrix;
var dfactor = (DenseMatrix)Factor;
Control.LinearAlgebraProvider.CholeskySolveFactored(dfactor.Data, dfactor.RowCount, dresult.Data, dresult.RowCount, dresult.ColumnCount);
}
@ -148,7 +148,7 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Factorization
throw new ArgumentException(Resources.ArgumentVectorsSameLength);
}
if (input.Count != mFactor.RowCount)
if (input.Count != Factor.RowCount)
{
throw new ArgumentException(Resources.ArgumentMatrixDimensions);
}
@ -169,7 +169,7 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Factorization
Buffer.BlockCopy(dinput.Data, 0, dresult.Data, 0, dinput.Data.Length * Constants.SizeOfDouble);
// Cholesky solve by overwriting result.
var dfactor = mFactor as DenseMatrix;
var dfactor = (DenseMatrix)Factor;
Control.LinearAlgebraProvider.CholeskySolveFactored(dfactor.Data, dfactor.RowCount, dresult.Data, dresult.Count, 1);
}
}

28
src/Numerics/LinearAlgebra/Double/Factorization/DenseLU.cs

@ -48,7 +48,7 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Factorization
/// 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 <b>null</b>.</exception>
/// <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)
{
@ -63,19 +63,19 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Factorization
}
// Create an array for the pivot indices.
mPivots = new int[matrix.RowCount];
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, mPivots);
mFactors = factors;
Control.LinearAlgebraProvider.LUFactor(factors.Data, factors.RowCount, Pivots);
Factors = factors;
}
/// <summary>
/// Solves a system of linear equations, <b>AX = B</b>, with A LU factorized.
/// 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"/>, <b>B</b>.</param>
/// <param name="result">The left hand side <see cref="Matrix"/>, <b>X</b>.</param>
/// <param name="input">The right hand side <see cref="Matrix"/>, <c>B</c>.</param>
/// <param name="result">The left hand side <see cref="Matrix"/>, <c>X</c>.</param>
public override void Solve(Matrix input, Matrix result)
{
// Check for proper arguments.
@ -100,7 +100,7 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Factorization
throw new ArgumentException(Resources.ArgumentMatrixSameColumnDimension);
}
if (input.RowCount != mFactors.RowCount)
if (input.RowCount != Factors.RowCount)
{
throw new ArgumentException(Resources.ArgumentMatrixDimensions);
}
@ -121,16 +121,16 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Factorization
Buffer.BlockCopy(dinput.Data, 0, dresult.Data, 0, dinput.Data.Length * Constants.SizeOfDouble);
// LU solve by overwriting result.
var dfactors = mFactors as DenseMatrix;
var dfactors = (DenseMatrix)Factors;
throw new NotImplementedException();
//Control.LinearAlgebraProvider.LUSolveFactored(dfactors.Data, dfactors.RowCount, dresult.Data, dresult.RowCount, dresult.ColumnCount);
}
/// <summary>
/// Solves a system of linear equations, <b>Ax = b</b>, with A LU factorized.
/// Solves a system of linear equations, <c>Ax = b</c>, with A LU factorized.
/// </summary>
/// <param name="input">The right hand side vector, <b>b</b>.</param>
/// <param name="result">The left hand side <see cref="Matrix"/>, <b>x</b>.</param>
/// <param name="input">The right hand side vector, <c>b</c>.</param>
/// <param name="result">The left hand side <see cref="Matrix"/>, <c>x</c>.</param>
public override void Solve(Vector input, Vector result)
{
// Check for proper arguments.
@ -150,7 +150,7 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Factorization
throw new ArgumentException(Resources.ArgumentVectorsSameLength);
}
if (input.Count != mFactors.RowCount)
if (input.Count != Factors.RowCount)
{
throw new ArgumentException(Resources.ArgumentMatrixDimensions);
}
@ -171,7 +171,7 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Factorization
Buffer.BlockCopy(dinput.Data, 0, dresult.Data, 0, dinput.Data.Length * Constants.SizeOfDouble);
// LU solve by overwriting result.
var dfactors = mFactors as DenseMatrix;
var dfactors = Factors as DenseMatrix;
throw new NotImplementedException();
//Control.LinearAlgebraProvider.LUSolveFactored(dfactors.Data, dfactors.RowCount, dresult.Data, dresult.Count, 1);
}

183
src/Numerics/LinearAlgebra/Double/Factorization/DenseQR.cs

@ -0,0 +1,183 @@
// <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.Double.Factorization
{
using System;
using Properties;
using Threading;
/// <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
{
/// <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>
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, ((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"/>, <b>B</b>.</param>
/// <param name="result">The left hand side <see cref="Matrix"/>, <b>X</b>.</param>
public override void Solve(Matrix input, Matrix result)
{
// Check for proper arguments.
if (input == null)
{
throw new ArgumentNullException("input");
}
if (result == null)
{
throw new ArgumentNullException("result");
}
// The solution X should have the same number of columns as B
if (input.ColumnCount != result.ColumnCount)
{
throw new ArgumentException(Resources.ArgumentMatrixSameColumnDimension);
}
// The dimension compatibility conditions for X = A\B require the two matrices A and B to have the same number of rows
if (MatrixR.RowCount != input.RowCount)
{
throw new ArgumentException(Resources.ArgumentMatrixSameRowDimension);
}
// The solution X row dimension is equal to the column dimension of A
if (MatrixR.ColumnCount != result.RowCount)
{
throw new ArgumentException(Resources.ArgumentMatrixSameColumnDimension);
}
var 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.");
}
var solution = new double[dinput.Data.Length];
Control.LinearAlgebraProvider.QRSolveFactored(input.ColumnCount, ((DenseMatrix)MatrixQ).Data, ((DenseMatrix)MatrixR).Data, dinput.Data, solution);
CommonParallel.For(
0,
dresult.RowCount,
row =>
{
for (var col = 0; col < dresult.ColumnCount; col++)
{
dresult[row, col] = solution[row + (col * dinput.RowCount)];
}
});
}
/// <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"/>, <b>x</b>.</param>
public override void Solve(Vector input, Vector result)
{
if (input == null)
{
throw new ArgumentNullException("input");
}
if (result == null)
{
throw new ArgumentNullException("result");
}
// Ax=b where A is an m x n matrix
// Check that b is a column vector with m entries
if (MatrixR.RowCount != input.Count)
{
throw new ArgumentException(Resources.ArgumentVectorsSameLength);
}
// Check that x is a column vector with n entries
if (MatrixR.ColumnCount != result.Count)
{
throw new ArgumentException(Resources.ArgumentMatrixDimensions);
}
var 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.");
}
var solution = new double[dinput.Data.Length];
Control.LinearAlgebraProvider.QRSolveFactored(1, ((DenseMatrix)MatrixQ).Data, ((DenseMatrix)MatrixR).Data, dinput.Data, solution);
CommonParallel.For(
0,
dresult.Count,
index => { dresult[index] = solution[index]; });
}
}
}

15
src/Numerics/LinearAlgebra/Double/Factorization/ExtensionMethods.cs

@ -30,9 +30,6 @@
namespace MathNet.Numerics.LinearAlgebra.Double.Factorization
{
using System;
using Properties;
/// <summary>
/// Extension methods which return factorizations for the various matrix classes.
/// </summary>
@ -43,7 +40,7 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Factorization
/// </summary>
/// <param name="matrix">The matrix to factor.</param>
/// <returns>The Cholesky decomposition object.</returns>
public static Factorization.Cholesky Cholesky(this Matrix matrix)
public static Cholesky Cholesky(this Matrix matrix)
{
return Factorization.Cholesky.Create(matrix);
}
@ -57,5 +54,15 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Factorization
{
return Factorization.LU.Create(matrix);
}
/// <summary>
/// Computes the QR decomposition for a matrix.
/// </summary>
/// <param name="matrix">The matrix to factor.</param>
/// <returns>The QR decomposition object.</returns>
public static QR QR(this Matrix matrix)
{
return Factorization.QR.Create(matrix);
}
}
}

63
src/Numerics/LinearAlgebra/Double/Factorization/LU.cs

@ -3,9 +3,7 @@
// 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
@ -14,10 +12,8 @@
// 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
@ -31,7 +27,6 @@
namespace MathNet.Numerics.LinearAlgebra.Double.Factorization
{
using System;
using Properties;
/// <summary>
/// <para>A class which encapsulates the functionality of an LU factorization.</para>
@ -43,17 +38,25 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Factorization
/// <remarks>
/// The computation of the LU factorization is done at construction time.
/// </remarks>
public abstract class LU
public abstract class LU : ISolver
{
/// <summary>
/// Stores both the L and U factors in the same matrix..
/// Gets or sets both the L and U factors in the same matrix.
/// </summary>
protected Matrix mFactors;
protected Matrix Factors
{
get;
set;
}
/// <summary>
/// Stores the pivot indices of the LU factorization.
/// Gets or sets the pivot indices of the LU factorization.
/// </summary>
protected int[] mPivots;
protected int[] Pivots
{
get;
set;
}
/// <summary>
/// Internal method which routes the call to perform the LU factorization to the appropriate class.
@ -72,56 +75,64 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Factorization
}
/// <summary>
/// Returns the lower triangular factor.
/// Gets the lower triangular factor.
/// </summary>
public virtual Matrix L
{
get
{
Matrix result = mFactors.LowerTriangle();
for (int i = 0; i < result.RowCount; i++)
var result = Factors.LowerTriangle();
for (var i = 0; i < result.RowCount; i++)
{
result.At(i, i, 1);
}
return result;
}
}
/// <summary>
/// Returns the upper triangular factor.
/// Gets the upper triangular factor.
/// </summary>
public virtual Matrix U
{
get { return mFactors.UpperTriangle(); }
get
{
return Factors.UpperTriangle();
}
}
/// <summary>
/// Return the permutation applied to LU factorization.
/// Gets the permutation applied to LU factorization.
/// </summary>
public virtual Permutation P
{
get { return Permutation.FromInversions(mPivots); }
get
{
return Permutation.FromInversions(Pivots);
}
}
/// <summary>
/// The determinant of the matrix for which the LU factorization was computed.
/// Gets the determinant of the matrix for which the LU factorization was computed.
/// </summary>
public virtual double Determinant
{
get
{
double det = 1.0;
for (int j = 0; j < mFactors.RowCount; j++)
var det = 1.0;
for (var j = 0; j < Factors.RowCount; j++)
{
if (mPivots[j] != j)
if (Pivots[j] != j)
{
det = -det * mFactors.At(j, j);
det = -det * Factors.At(j, j);
}
else
{
det *= mFactors.At(j, j);
det *= Factors.At(j, j);
}
}
return det;
}
}
@ -139,9 +150,9 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Factorization
throw new ArgumentNullException("input");
}
var X = input.CreateMatrix(input.RowCount, input.ColumnCount);
Solve(input, X);
return X;
var x = input.CreateMatrix(input.RowCount, input.ColumnCount);
Solve(input, x);
return x;
}
/// <summary>

195
src/Numerics/LinearAlgebra/Double/Factorization/QR.cs

@ -0,0 +1,195 @@
// <copyright file="QR.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.Double.Factorization
{
using System;
using Properties;
/// <summary>
/// <para>A class which encapsulates the functionality of the QR decomposition.</para>
/// <para>Any real square matrix A (m x n) may be decomposed as A = QR where Q is an orthogonal matrix (m x m)
/// (its columns are orthogonal unit vectors meaning QTQ = I) and R (m x n) 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 abstract class QR : ISolver
{
/// <summary>
/// Gets or sets orthogonal Q matrix
/// </summary>
protected virtual Matrix MatrixQ
{
get;
set;
}
/// <summary>
/// Gets or sets upper triangular factor R
/// </summary>
protected virtual Matrix MatrixR
{
get;
set;
}
/// <summary>
/// Internal method which routes the call to perform the QR factorization to the appropriate class.
/// </summary>
/// <param name="matrix">The matrix to factor.</param>
/// <returns>A QR factorization object.</returns>
internal static QR Create(Matrix matrix)
{
var dense = matrix as DenseMatrix;
if (dense != null)
{
return new DenseQR(dense);
}
throw new NotImplementedException();
}
/// <summary>
/// Gets orthogonal Q matrix
/// </summary>
public virtual Matrix Q
{
get
{
return MatrixQ;
}
}
/// <summary>
/// Gets the upper triangular factor R.
/// </summary>
public virtual Matrix R
{
get
{
return MatrixR.UpperTriangle();
}
}
/// <summary>
/// Gets the determinant of the matrix for which the QR matrix was computed.
/// </summary>
public virtual double Determinant
{
get
{
if (MatrixR.RowCount != MatrixR.ColumnCount)
{
throw new ArgumentException(Resources.ArgumentMatrixSquare);
}
var det = 1.0;
for (var i = 0; i < MatrixR.ColumnCount; i++)
{
det *= MatrixR.At(i, i);
if (Math.Abs(MatrixR.At(i, i)).AlmostEqualInDecimalPlaces(0.0, 15))
{
return 0;
}
}
return Math.Abs(det);
}
}
/// <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 virtual bool IsFullRank
{
get
{
for (var i = 0; i < MatrixR.ColumnCount; i++)
{
if (Math.Abs(MatrixR.At(i, i)).AlmostEqualInDecimalPlaces(0.0, 15))
{
return false;
}
}
return true;
}
}
/// <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"/>, <b>B</b>.</param>
/// <returns>The left hand side <see cref="Matrix"/>, <b>X</b>.</returns>
public virtual Matrix Solve(Matrix input)
{
// Check for proper arguments.
if (input == null)
{
throw new ArgumentNullException("input");
}
var matrixX = input.CreateMatrix(MatrixR.ColumnCount, input.ColumnCount);
Solve(input, matrixX);
return matrixX;
}
/// <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"/>, <b>B</b>.</param>
/// <param name="result">The left hand side <see cref="Matrix"/>, <b>X</b>.</param>
public abstract void Solve(Matrix input, Matrix result);
/// <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>
/// <returns>The left hand side <see cref="Vector"/>, <b>x</b>.</returns>
public virtual Vector Solve(Vector input)
{
// Check for proper arguments.
if (input == null)
{
throw new ArgumentNullException("input");
}
var x = input.CreateVector(MatrixR.ColumnCount);
Solve(input, x);
return x;
}
/// <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"/>, <b>x</b>.</param>
public abstract void Solve(Vector input, Vector result);
}
}

64
src/Numerics/LinearAlgebra/Double/ISolver.cs

@ -0,0 +1,64 @@
// <copyright file="ISolver.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.Double
{
/// <summary>
/// Classes that solves a system of linear equations, <c>AX = B</c>.
/// </summary>
public interface ISolver
{
/// <summary>
/// Solves a system of linear equations, <c>AX = B</c>.
/// </summary>
/// <param name="input">The right hand side <see cref="Matrix"/>, <c>B</c>.</param>
/// <returns>The left hand side <see cref="Matrix"/>, <c>X</c>.</returns>
Matrix Solve(Matrix input);
/// <summary>
/// Solves a system of linear equations, <c>AX = B</c>.
/// </summary>
/// <param name="input">The right hand side <see cref="Matrix"/>, <c>B</c>.</param>
/// <param name="result">The left hand side <see cref="Matrix"/>, <c>X</c>.</param>
void Solve(Matrix input, Matrix result);
/// <summary>
/// Solves a system of linear equations, <c>Ax = b</c>
/// </summary>
/// <param name="input">The right hand side vector, <c>b</c>.</param>
/// <returns>The left hand side <see cref="Vector"/>, <c>x</c>.</returns>
Vector Solve(Vector input);
/// <summary>
/// Solves a system of linear equations, <c>Ax = b</c>.
/// </summary>
/// <param name="input">The right hand side vector, <c>b</c>.</param>
/// <param name="result">The left hand side <see cref="Matrix"/>, <c>x</c>.</param>
void Solve(Vector input, Vector result);
}
}

3
src/Numerics/Numerics.csproj

@ -101,6 +101,9 @@
<Compile Include="Constants.cs" />
<Compile Include="Control.cs" />
<Compile Include="Complex32.cs" />
<Compile Include="LinearAlgebra\Double\Factorization\DenseQR.cs" />
<Compile Include="LinearAlgebra\Double\ISolver.cs" />
<Compile Include="LinearAlgebra\Double\Factorization\QR.cs" />
<Compile Include="LinearAlgebra\Double\SparseMatrix.cs" />
<Compile Include="Permutation.cs" />
<Compile Include="Distributions\Continuous\Beta.cs" />

9
src/Silverlight/Silverlight.csproj

@ -230,12 +230,21 @@
<Compile Include="..\Numerics\LinearAlgebra\Double\Factorization\DenseLU.cs">
<Link>LinearAlgebra\Double\Factorization\DenseLU.cs</Link>
</Compile>
<Compile Include="..\Numerics\LinearAlgebra\Double\Factorization\DenseQR.cs">
<Link>LinearAlgebra\Double\Factorization\DenseQR.cs</Link>
</Compile>
<Compile Include="..\Numerics\LinearAlgebra\Double\Factorization\ExtensionMethods.cs">
<Link>LinearAlgebra\Double\Factorization\ExtensionMethods.cs</Link>
</Compile>
<Compile Include="..\Numerics\LinearAlgebra\Double\Factorization\LU.cs">
<Link>LinearAlgebra\Double\Factorization\LU.cs</Link>
</Compile>
<Compile Include="..\Numerics\LinearAlgebra\Double\Factorization\QR.cs">
<Link>LinearAlgebra\Double\Factorization\QR.cs</Link>
</Compile>
<Compile Include="..\Numerics\LinearAlgebra\Double\ISolver.cs">
<Link>LinearAlgebra\Double\ISolver.cs</Link>
</Compile>
<Compile Include="..\Numerics\LinearAlgebra\Double\Matrix.Arithmetic.cs">
<Link>LinearAlgebra\Double\Matrix.Arithmetic.cs</Link>
</Compile>

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

@ -0,0 +1,317 @@
// <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.Double.Factorization
{
using MbUnit.Framework;
using LinearAlgebra.Double;
using LinearAlgebra.Double.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.GenerateRandomMatrix(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], 1.0e-11);
}
}
}
[Test]
[Row(1)]
[Row(2)]
[Row(5)]
[Row(10)]
[Row(50)]
[Row(100)]
[MultipleAsserts]
public void CanSolveForRandomVector(int order)
{
var matrixA = MatrixLoader.GenerateRandomMatrix(order, order);
var matrixACopy = matrixA.Clone();
var factorQR = matrixA.QR();
var vectorb = MatrixLoader.GenerateRandomVector(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], 1.0e-11);
}
// Make sure A didn't change.
for (var i = 0; i < matrixA.RowCount; i++)
{
for (var j = 0; j < matrixA.ColumnCount; j++)
{
Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]);
}
}
}
[Test]
[Row(1)]
[Row(4)]
[Row(8)]
[Row(10)]
[Row(50)]
[Row(100)]
[MultipleAsserts]
public void CanSolveForRandomMatrix(int order)
{
var matrixA = MatrixLoader.GenerateRandomMatrix(order, order);
var matrixACopy = matrixA.Clone();
var factorQR = matrixA.QR();
var matrixB = MatrixLoader.GenerateRandomMatrix(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], 1.0e-11);
}
}
// Make sure A didn't change.
for (var i = 0; i < matrixA.RowCount; i++)
{
for (var j = 0; j < matrixA.ColumnCount; j++)
{
Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]);
}
}
}
[Test]
[Row(1)]
[Row(2)]
[Row(5)]
[Row(10)]
[Row(50)]
[Row(100)]
[MultipleAsserts]
public void CanSolveForRandomVectorWhenResultVectorGiven(int order)
{
var matrixA = MatrixLoader.GenerateRandomMatrix(order, order);
var matrixACopy = matrixA.Clone();
var factorQR = matrixA.QR();
var vectorb = MatrixLoader.GenerateRandomVector(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], 1.0e-11);
}
// Make sure A didn't change.
for (var i = 0; i < matrixA.RowCount; i++)
{
for (var j = 0; j < matrixA.ColumnCount; j++)
{
Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]);
}
}
// Make sure b didn't change.
for (var i = 0; i < vectorb.Count; i++)
{
Assert.AreEqual(vectorbCopy[i], vectorb[i]);
}
}
[Test]
[Row(1)]
[Row(4)]
[Row(8)]
[Row(10)]
[Row(50)]
[Row(100)]
[MultipleAsserts]
public void CanSolveForRandomMatrixWhenResultMatrixGiven(int order)
{
var matrixA = MatrixLoader.GenerateRandomMatrix(order, order);
var matrixACopy = matrixA.Clone();
var factorQR = matrixA.QR();
var matrixB = MatrixLoader.GenerateRandomMatrix(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], 1.0e-11);
}
}
// Make sure A didn't change.
for (var i = 0; i < matrixA.RowCount; i++)
{
for (var j = 0; j < matrixA.ColumnCount; j++)
{
Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]);
}
}
// Make sure B didn't change.
for (var i = 0; i < matrixB.RowCount; i++)
{
for (var j = 0; j < matrixB.ColumnCount; j++)
{
Assert.AreEqual(matrixBCopy[i, j], matrixB[i, j]);
}
}
}
}
}

1
src/UnitTests/UnitTests.csproj

@ -90,6 +90,7 @@
<Compile Include="ComplexTests\ComplexTest.cs" />
<Compile Include="ComplexTests\Complex32Test.TextHandling.cs" />
<Compile Include="ComplexTests\Complex32Test.cs" />
<Compile Include="LinearAlgebraTests\Double\Factorization\QRTests.cs" />
<Compile Include="LinearAlgebraTests\Double\SparseMatrixTests.cs" />
<Compile Include="PermutationTest.cs" />
<Compile Include="DistributionTests\CommonDistributionTests.cs" />

Loading…
Cancel
Save