diff --git a/src/Numerics/Algorithms/LinearAlgebra/ManagedLinearAlgebraProvider.cs b/src/Numerics/Algorithms/LinearAlgebra/ManagedLinearAlgebraProvider.cs
index 43692fdf..260d38d1 100644
--- a/src/Numerics/Algorithms/LinearAlgebra/ManagedLinearAlgebraProvider.cs
+++ b/src/Numerics/Algorithms/LinearAlgebra/ManagedLinearAlgebraProvider.cs
@@ -742,6 +742,11 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra
/// This is equivalent to the POTRF LAPACK routine.
public void CholeskyFactor(double[] a, int order)
{
+ if (a == null)
+ {
+ throw new ArgumentNullException("a");
+ }
+
for (var j = 0; j < order; j++)
{
var d = 0.0;
@@ -793,15 +798,35 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra
///
/// Solves A*X=B for X using a previously factored A matrix.
///
- /// The square, positive definite matrix A.
+ /// The square, positive definite matrix A. Has to be different than .
/// The number of rows and columns in A.
- /// The B matrix.
+ /// The B matrix. Has to be different than .
/// The number of rows in the B matrix.
/// The number of columns in the B matrix.
/// This is equivalent to the POTRS LAPACK routine.
public void CholeskySolveFactored(double[] a, int aOrder, double[] b, int bRows, int bColumns)
{
- for (int c = 0; c < bColumns; c++)
+ if (a == null)
+ {
+ throw new ArgumentNullException("a");
+ }
+
+ if (b == null)
+ {
+ throw new ArgumentNullException("b");
+ }
+
+ if (aOrder != bRows)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixDimensions);
+ }
+
+ if (Object.ReferenceEquals(a, b))
+ {
+ throw new ArgumentException(Resources.ArgumentReferenceDifferent);
+ }
+
+ CommonParallel.For(0, bColumns, c =>
{
int cindex = c * aOrder;
@@ -809,7 +834,7 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra
double sum;
for (int i = 0; i < aOrder; i++)
{
- sum = b[c * aOrder + i];
+ sum = b[cindex + i];
for (int k = i - 1; k >= 0; k--)
{
sum -= a[k * aOrder + i] * b[cindex + k];
@@ -828,7 +853,7 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra
}
b[cindex + i] = sum / a[iindex + i];
}
- }
+ });
}
public void QRFactor(double[] r, double[] q)
diff --git a/src/Numerics/LinearAlgebra/Double/Factorization/DenseCholesky.cs b/src/Numerics/LinearAlgebra/Double/Factorization/DenseCholesky.cs
index eafbbc83..67ad3bb5 100644
--- a/src/Numerics/LinearAlgebra/Double/Factorization/DenseCholesky.cs
+++ b/src/Numerics/LinearAlgebra/Double/Factorization/DenseCholesky.cs
@@ -128,7 +128,7 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Factorization
throw new NotImplementedException("Can only do Cholesky factorization for dense matrices at the moment.");
}
- var dresult = input as DenseMatrix;
+ var dresult = result as DenseMatrix;
if (dresult == null)
{
throw new NotImplementedException("Can only do Cholesky factorization for dense matrices at the moment.");
@@ -195,7 +195,7 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Factorization
throw new NotImplementedException("Can only do Cholesky factorization for dense vectors at the moment.");
}
- var dresult = input as DenseVector;
+ var dresult = result as DenseVector;
if (dresult == null)
{
throw new NotImplementedException("Can only do Cholesky factorization for dense vectors at the moment.");
diff --git a/src/Numerics/Properties/Resources.Designer.cs b/src/Numerics/Properties/Resources.Designer.cs
index d5b8053e..4d595b4d 100644
--- a/src/Numerics/Properties/Resources.Designer.cs
+++ b/src/Numerics/Properties/Resources.Designer.cs
@@ -384,6 +384,15 @@ namespace MathNet.Numerics.Properties {
}
}
+ ///
+ /// Looks up a localized string similar to Arguments must be different objects..
+ ///
+ internal static string ArgumentReferenceDifferent {
+ get {
+ return ResourceManager.GetString("ArgumentReferenceDifferent", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Array must have exactly one dimension (and not be null)..
///
diff --git a/src/Numerics/Properties/Resources.resx b/src/Numerics/Properties/Resources.resx
index d7002782..0e04d29e 100644
--- a/src/Numerics/Properties/Resources.resx
+++ b/src/Numerics/Properties/Resources.resx
@@ -112,10 +112,10 @@
2.0
- System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
- System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
The histogram does not contains the value.
@@ -297,4 +297,7 @@
The moment of the distribution is undefined.
+
+ Arguments must be different objects.
+
\ No newline at end of file
diff --git a/src/UnitTests/LinearAlgebraTests/Double/Factorization/CholeskyTests.cs b/src/UnitTests/LinearAlgebraTests/Double/Factorization/CholeskyTests.cs
index bfeb81ca..d78c7cb4 100644
--- a/src/UnitTests/LinearAlgebraTests/Double/Factorization/CholeskyTests.cs
+++ b/src/UnitTests/LinearAlgebraTests/Double/Factorization/CholeskyTests.cs
@@ -103,20 +103,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double.Factorization
[MultipleAsserts]
public void CanFactorizeRandomMatrix(int order)
{
- // Fill a matrix with standard random numbers.
- var normal = new Distributions.Normal();
- normal.RandomSource = new Random.MersenneTwister(1);
- var A = new DenseMatrix(order);
- for (int i = 0; i < order; i++)
- {
- for (int j = 0; j < order; j++)
- {
- A[i, j] = normal.Sample();
- }
- }
-
- // Generate a matrix which is positive definite.
- var X = A.Transpose() * A;
+ var X = MatrixLoader.GenerateRandomPositiveDefiniteMatrix(order);
var chol = X.Cholesky();
var C = chol.Factor;
@@ -139,7 +126,178 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double.Factorization
{
for (int j = 0; j < XfromC.ColumnCount; j++)
{
- Assert.AreApproximatelyEqual(X[i,j], XfromC[i, j], 1.0e-13);
+ Assert.AreApproximatelyEqual(X[i,j], XfromC[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 A = MatrixLoader.GenerateRandomPositiveDefiniteMatrix(order);
+ var ACopy = A.Clone();
+ var chol = A.Cholesky();
+ var b = MatrixLoader.GenerateRandomVector(order);
+ var x = chol.Solve(b);
+
+ Assert.AreEqual(b.Count, x.Count);
+
+ var bReconstruct = A * x;
+
+ // Check the reconstruction.
+ for (int i = 0; i < order; i++)
+ {
+ Assert.AreApproximatelyEqual(b[i], bReconstruct[i], 1.0e-11);
+ }
+
+ // Make sure A didn't change.
+ for (int i = 0; i < A.RowCount; i++)
+ {
+ for (int j = 0; j < A.ColumnCount; j++)
+ {
+ Assert.AreEqual(ACopy[i, j], A[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 A = MatrixLoader.GenerateRandomPositiveDefiniteMatrix(row);
+ var ACopy = A.Clone();
+ var chol = A.Cholesky();
+ var B = MatrixLoader.GenerateRandomMatrix(row, col);
+ var X = chol.Solve(B);
+
+ Assert.AreEqual(B.RowCount, X.RowCount);
+ Assert.AreEqual(B.ColumnCount, X.ColumnCount);
+
+ var BReconstruct = A * X;
+
+ // Check the reconstruction.
+ for (int i = 0; i < B.RowCount; i++)
+ {
+ for (int j = 0; j < B.ColumnCount; j++)
+ {
+ Assert.AreApproximatelyEqual(B[i, j], BReconstruct[i, j], 1.0e-11);
+ }
+ }
+
+ // Make sure A didn't change.
+ for (int i = 0; i < A.RowCount; i++)
+ {
+ for (int j = 0; j < A.ColumnCount; j++)
+ {
+ Assert.AreEqual(ACopy[i, j], A[i, j]);
+ }
+ }
+ }
+
+ [Test]
+ [Row(1)]
+ [Row(2)]
+ [Row(5)]
+ [Row(10)]
+ [Row(50)]
+ [Row(100)]
+ [MultipleAsserts]
+ public void CanSolveForRandomVectorWhenResultVectorGiven(int order)
+ {
+ var A = MatrixLoader.GenerateRandomPositiveDefiniteMatrix(order);
+ var ACopy = A.Clone();
+ var chol = A.Cholesky();
+ var b = MatrixLoader.GenerateRandomVector(order);
+ var bCopy = b.Clone();
+ var x = new DenseVector(order);
+ chol.Solve(b, x);
+
+ Assert.AreEqual(b.Count, x.Count);
+
+ var bReconstruct = A * x;
+
+ // Check the reconstruction.
+ for (int i = 0; i < order; i++)
+ {
+ Assert.AreApproximatelyEqual(b[i], bReconstruct[i], 1.0e-11);
+ }
+
+ // Make sure A didn't change.
+ for (int i = 0; i < A.RowCount; i++)
+ {
+ for (int j = 0; j < A.ColumnCount; j++)
+ {
+ Assert.AreEqual(ACopy[i, j], A[i, j]);
+ }
+ }
+
+ // Make sure b didn't change.
+ for (int 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 A = MatrixLoader.GenerateRandomPositiveDefiniteMatrix(row);
+ var ACopy = A.Clone();
+ var chol = A.Cholesky();
+ var B = MatrixLoader.GenerateRandomMatrix(row, col);
+ var BCopy = B.Clone();
+ var X = new DenseMatrix(row, col);
+ chol.Solve(B, X);
+
+ Assert.AreEqual(B.RowCount, X.RowCount);
+ Assert.AreEqual(B.ColumnCount, X.ColumnCount);
+
+ var BReconstruct = A * X;
+
+ // Check the reconstruction.
+ for (int i = 0; i < B.RowCount; i++)
+ {
+ for (int j = 0; j < B.ColumnCount; j++)
+ {
+ Assert.AreApproximatelyEqual(B[i, j], BReconstruct[i, j], 1.0e-11);
+ }
+ }
+
+ // Make sure A didn't change.
+ for (int i = 0; i < A.RowCount; i++)
+ {
+ for (int j = 0; j < A.ColumnCount; j++)
+ {
+ Assert.AreEqual(ACopy[i, j], A[i, j]);
+ }
+ }
+
+ // Make sure B didn't change.
+ for (int i = 0; i < B.RowCount; i++)
+ {
+ for (int j = 0; j < B.ColumnCount; j++)
+ {
+ Assert.AreEqual(BCopy[i, j], B[i, j]);
}
}
}
diff --git a/src/UnitTests/LinearAlgebraTests/Double/MatrixLoader.cs b/src/UnitTests/LinearAlgebraTests/Double/MatrixLoader.cs
index 21a99a5b..56884526 100644
--- a/src/UnitTests/LinearAlgebraTests/Double/MatrixLoader.cs
+++ b/src/UnitTests/LinearAlgebraTests/Double/MatrixLoader.cs
@@ -63,5 +63,55 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
}
}
+ public static Matrix GenerateRandomMatrix(int row, int col)
+ {
+ // Fill a matrix with standard random numbers.
+ var normal = new Distributions.Normal();
+ normal.RandomSource = new Random.MersenneTwister(1);
+ var A = new DenseMatrix(row, col);
+ for (int i = 0; i < row; i++)
+ {
+ for (int j = 0; j < col; j++)
+ {
+ A[i, j] = normal.Sample();
+ }
+ }
+
+ // Generate a matrix which is positive definite.
+ return A;
+ }
+
+ public static Matrix GenerateRandomPositiveDefiniteMatrix(int order)
+ {
+ // Fill a matrix with standard random numbers.
+ var normal = new Distributions.Normal();
+ normal.RandomSource = new Random.MersenneTwister(1);
+ var A = new DenseMatrix(order);
+ for (int i = 0; i < order; i++)
+ {
+ for (int j = 0; j < order; j++)
+ {
+ A[i, j] = normal.Sample();
+ }
+ }
+
+ // Generate a matrix which is positive definite.
+ return A.Transpose() * A;
+ }
+
+ public static Vector GenerateRandomVector(int order)
+ {
+ // Fill a matrix with standard random numbers.
+ var normal = new Distributions.Normal();
+ normal.RandomSource = new Random.MersenneTwister(1);
+ var v = new DenseVector(order);
+ for (int i = 0; i < order; i++)
+ {
+ v[i] = normal.Sample();
+ }
+
+ // Generate a matrix which is positive definite.
+ return v;
+ }
}
}