// // 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. // using System; using MathNet.Numerics.LinearAlgebra; using MathNet.Numerics.LinearAlgebra.Factorization; using MathNet.Numerics.LinearAlgebra.Single.Factorization; using NUnit.Framework; namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single.Factorization { /// /// QR factorization tests for a user matrix. /// public class UserQRTests { /// /// Constructor with wide matrix throws ArgumentException. /// [Test] public void ConstructorWideMatrixThrowsInvalidMatrixOperationException() { Assert.Throws(() => UserQR.Create(new UserDefinedMatrix(3, 4))); } /// /// Can factorize identity matrix. /// /// Matrix order. [TestCase(1)] [TestCase(10)] [TestCase(100)] public void CanFactorizeIdentity(int order) { var matrixI = UserDefinedMatrix.Identity(order); var factorQR = matrixI.QR(); var r = factorQR.R; Assert.AreEqual(matrixI.RowCount, r.RowCount); Assert.AreEqual(matrixI.ColumnCount, r.ColumnCount); for (var i = 0; i < r.RowCount; i++) { for (var j = 0; j < r.ColumnCount; j++) { if (i == j) { Assert.AreEqual(-1.0, r[i, j]); } else { Assert.AreEqual(0.0, r[i, j]); } } } } /// /// Can factorize identity matrix using thin QR. /// /// Matrix order. [TestCase(1)] [TestCase(10)] [TestCase(100)] public void CanFactorizeIdentityUsingThinQR(int order) { var matrixI = UserDefinedMatrix.Identity(order); var factorQR = matrixI.QR(QRMethod.Thin); var r = factorQR.R; Assert.AreEqual(matrixI.RowCount, r.RowCount); Assert.AreEqual(matrixI.ColumnCount, r.ColumnCount); for (var i = 0; i < r.RowCount; i++) { for (var j = 0; j < r.ColumnCount; j++) { if (i == j) { Assert.AreEqual(-1.0, r[i, j]); } else { Assert.AreEqual(0.0, r[i, j]); } } } } /// /// Identity determinant is one. /// /// Matrix order. [TestCase(1)] [TestCase(10)] [TestCase(100)] public void IdentityDeterminantIsOne(int order) { var matrixI = UserDefinedMatrix.Identity(order); var factorQR = matrixI.QR(); Assert.AreEqual(1.0, factorQR.Determinant); } /// /// Can factorize a random matrix. /// /// Matrix row number. /// Matrix column number. [TestCase(1, 1)] [TestCase(2, 2)] [TestCase(5, 5)] [TestCase(10, 6)] [TestCase(50, 48)] [TestCase(100, 98)] public void CanFactorizeRandomMatrix(int row, int column) { var matrixA = new UserDefinedMatrix(Matrix.Build.Random(row, column, 1).ToArray()); var factorQR = matrixA.QR(QRMethod.Full); var q = factorQR.Q; var r = factorQR.R; // Make sure the R has the right dimensions. Assert.AreEqual(row, r.RowCount); Assert.AreEqual(column, r.ColumnCount); // Make sure the Q has the right dimensions. Assert.AreEqual(row, q.RowCount); Assert.AreEqual(row, q.ColumnCount); // Make sure the R factor is upper triangular. for (var i = 0; i < r.RowCount; i++) { for (var j = 0; j < r.ColumnCount; j++) { if (i > j) { Assert.AreEqual(0.0, r[i, j]); } } } // Make sure the Q*R is the original matrix. var matrixQfromR = q * r; for (var i = 0; i < matrixQfromR.RowCount; i++) { for (var j = 0; j < matrixQfromR.ColumnCount; j++) { Assert.AreEqual(matrixA[i, j], matrixQfromR[i, j], 1e-4); } } } /// /// Can factorize a random matrix using thin QR. /// /// Matrix row number. /// Matrix column number. [TestCase(1, 1)] [TestCase(2, 2)] [TestCase(5, 5)] [TestCase(10, 6)] [TestCase(50, 48)] [TestCase(100, 98)] public void CanFactorizeRandomMatrixUsingThinQR(int row, int column) { var matrixA = new UserDefinedMatrix(Matrix.Build.Random(row, column, 1).ToArray()); var factorQR = matrixA.QR(QRMethod.Thin); var q = factorQR.Q; var r = factorQR.R; // Make sure the R has the right dimensions. Assert.AreEqual(column, r.RowCount); Assert.AreEqual(column, r.ColumnCount); // Make sure the Q has the right dimensions. Assert.AreEqual(row, q.RowCount); Assert.AreEqual(column, q.ColumnCount); // Make sure the R factor is upper triangular. for (var i = 0; i < r.RowCount; i++) { for (var j = 0; j < r.ColumnCount; j++) { if (i > j) { Assert.AreEqual(0.0, r[i, j]); } } } // Make sure the Q*R is the original matrix. var matrixQfromR = q * r; for (var i = 0; i < matrixQfromR.RowCount; i++) { for (var j = 0; j < matrixQfromR.ColumnCount; j++) { Assert.AreEqual(matrixA[i, j], matrixQfromR[i, j], 1.0e-4); } } } /// /// Can solve a system of linear equations for a random vector (Ax=b). /// /// Matrix order. [TestCase(1)] [TestCase(2)] [TestCase(5)] [TestCase(10)] [TestCase(50)] [TestCase(100)] public void CanSolveForRandomVector(int order) { var matrixA = new UserDefinedMatrix(Matrix.Build.Random(order, order, 1).ToArray()); var matrixACopy = matrixA.Clone(); var factorQR = matrixA.QR(); var vectorb = new UserDefinedVector(Vector.Build.Random(order, 1).ToArray()); var resultx = factorQR.Solve(vectorb); Assert.AreEqual(matrixA.ColumnCount, resultx.Count); var matrixBReconstruct = matrixA * resultx; // Check the reconstruction. for (var i = 0; i < order; i++) { Assert.AreEqual(vectorb[i], matrixBReconstruct[i], 1e-4); } // 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]); } } } /// /// Can solve a system of linear equations for a random matrix (AX=B). /// /// Matrix order. [TestCase(1)] [TestCase(2)] [TestCase(5)] [TestCase(10)] [TestCase(50)] [TestCase(100)] public void CanSolveForRandomMatrix(int order) { var matrixA = new UserDefinedMatrix(Matrix.Build.Random(order, order, 1).ToArray()); var matrixACopy = matrixA.Clone(); var factorQR = matrixA.QR(); var matrixB = new UserDefinedMatrix(Matrix.Build.Random(order, order, 1).ToArray()); 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.AreEqual(matrixB[i, j], matrixBReconstruct[i, j], 1e-4); } } // 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]); } } } /// /// Can solve for a random vector into a result vector. /// /// Matrix order. [TestCase(1)] [TestCase(2)] [TestCase(5)] [TestCase(10)] [TestCase(50)] [TestCase(100)] public void CanSolveForRandomVectorWhenResultVectorGiven(int order) { var matrixA = new UserDefinedMatrix(Matrix.Build.Random(order, order, 1).ToArray()); var matrixACopy = matrixA.Clone(); var factorQR = matrixA.QR(); var vectorb = new UserDefinedVector(Vector.Build.Random(order, 1).ToArray()); var vectorbCopy = vectorb.Clone(); var resultx = new UserDefinedVector(order); factorQR.Solve(vectorb, resultx); Assert.AreEqual(vectorb.Count, resultx.Count); var matrixBReconstruct = matrixA * resultx; // Check the reconstruction. for (var i = 0; i < vectorb.Count; i++) { Assert.AreEqual(vectorb[i], matrixBReconstruct[i], 1e-4); } // 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]); } } /// /// Can solve a system of linear equations for a random matrix (AX=B) into a result matrix. /// /// Matrix order. [TestCase(1)] [TestCase(2)] [TestCase(5)] [TestCase(10)] [TestCase(50)] [TestCase(100)] public void CanSolveForRandomMatrixWhenResultMatrixGiven(int order) { var matrixA = new UserDefinedMatrix(Matrix.Build.Random(order, order, 1).ToArray()); var matrixACopy = matrixA.Clone(); var factorQR = matrixA.QR(); var matrixB = new UserDefinedMatrix(Matrix.Build.Random(order, order, 1).ToArray()); var matrixBCopy = matrixB.Clone(); var matrixX = new UserDefinedMatrix(order, order); factorQR.Solve(matrixB, matrixX); // The solution X row dimension is equal to the column dimension of A Assert.AreEqual(matrixA.ColumnCount, matrixX.RowCount); // The solution X has the same number of columns as B Assert.AreEqual(matrixB.ColumnCount, matrixX.ColumnCount); var matrixBReconstruct = matrixA * matrixX; // Check the reconstruction. for (var i = 0; i < matrixB.RowCount; i++) { for (var j = 0; j < matrixB.ColumnCount; j++) { Assert.AreEqual(matrixB[i, j], matrixBReconstruct[i, j], 1e-4); } } // 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]); } } } /// /// Can solve a system of linear equations for a random vector (Ax=b). /// /// Matrix order. [TestCase(1)] [TestCase(2)] [TestCase(5)] [TestCase(10)] [TestCase(50)] [TestCase(100)] public void CanSolveForRandomVectorUsingThinQR(int order) { var matrixA = new UserDefinedMatrix(Matrix.Build.Random(order, order, 1).ToArray()); var matrixACopy = matrixA.Clone(); var factorQR = matrixA.QR(QRMethod.Thin); var vectorb = new UserDefinedVector(Vector.Build.Random(order, 1).ToArray()); var resultx = factorQR.Solve(vectorb); Assert.AreEqual(matrixA.ColumnCount, resultx.Count); var matrixBReconstruct = matrixA * resultx; // Check the reconstruction. for (var i = 0; i < order; i++) { Assert.AreEqual(vectorb[i], matrixBReconstruct[i], 1e-4); } // 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]); } } } /// /// Can solve a system of linear equations for a random matrix (AX=B). /// /// Matrix order. [TestCase(1)] [TestCase(2)] [TestCase(5)] [TestCase(10)] [TestCase(50)] [TestCase(100)] public void CanSolveForRandomMatrixUsingThinQR(int order) { var matrixA = new UserDefinedMatrix(Matrix.Build.Random(order, order, 1).ToArray()); var matrixACopy = matrixA.Clone(); var factorQR = matrixA.QR(QRMethod.Thin); var matrixB = new UserDefinedMatrix(Matrix.Build.Random(order, order, 1).ToArray()); 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.AreEqual(matrixB[i, j], matrixBReconstruct[i, j], 1e-4); } } // 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]); } } } /// /// Can solve for a random vector into a result vector. /// /// Matrix order. [TestCase(1)] [TestCase(2)] [TestCase(5)] [TestCase(10)] [TestCase(50)] [TestCase(100)] public void CanSolveForRandomVectorWhenResultVectorGivenUsingThinQR(int order) { var matrixA = new UserDefinedMatrix(Matrix.Build.Random(order, order, 1).ToArray()); var matrixACopy = matrixA.Clone(); var factorQR = matrixA.QR(QRMethod.Thin); var vectorb = new UserDefinedVector(Vector.Build.Random(order, 1).ToArray()); var vectorbCopy = vectorb.Clone(); var resultx = new UserDefinedVector(order); factorQR.Solve(vectorb, resultx); Assert.AreEqual(vectorb.Count, resultx.Count); var matrixBReconstruct = matrixA * resultx; // Check the reconstruction. for (var i = 0; i < vectorb.Count; i++) { Assert.AreEqual(vectorb[i], matrixBReconstruct[i], 1e-4); } // 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]); } } /// /// Can solve a system of linear equations for a random matrix (AX=B) into a result matrix. /// /// Matrix order. [TestCase(1)] [TestCase(2)] [TestCase(5)] [TestCase(10)] [TestCase(50)] [TestCase(100)] public void CanSolveForRandomMatrixWhenResultMatrixGivenUsingThinAR(int order) { var matrixA = new UserDefinedMatrix(Matrix.Build.Random(order, order, 1).ToArray()); var matrixACopy = matrixA.Clone(); var factorQR = matrixA.QR(QRMethod.Thin); var matrixB = new UserDefinedMatrix(Matrix.Build.Random(order, order, 1).ToArray()); var matrixBCopy = matrixB.Clone(); var matrixX = new UserDefinedMatrix(order, order); factorQR.Solve(matrixB, matrixX); // The solution X row dimension is equal to the column dimension of A Assert.AreEqual(matrixA.ColumnCount, matrixX.RowCount); // The solution X has the same number of columns as B Assert.AreEqual(matrixB.ColumnCount, matrixX.ColumnCount); var matrixBReconstruct = matrixA * matrixX; // Check the reconstruction. for (var i = 0; i < matrixB.RowCount; i++) { for (var j = 0; j < matrixB.ColumnCount; j++) { Assert.AreEqual(matrixB[i, j], matrixBReconstruct[i, j], 1e-4); } } // 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]); } } } } }