// // 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-2013 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 System.Collections.Generic; using MathNet.Numerics.Distributions; using MathNet.Numerics.LinearAlgebra; using MathNet.Numerics.LinearAlgebra.Single; using MathNet.Numerics.Random; using NUnit.Framework; namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single { /// /// Diagonal matrix tests. /// public class DiagonalMatrixTests : MatrixTests { /// /// Setup test matrices. /// [SetUp] public override void SetupMatrices() { TestData2D = new Dictionary { {"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>(); foreach (var name in TestData2D.Keys) { TestMatrices.Add(name, CreateMatrix(TestData2D[name])); } } /// /// Creates a matrix for the given number of rows and columns. /// /// The number of rows. /// The number of columns. /// A matrix with the given dimensions. protected override Matrix CreateMatrix(int rows, int columns) { return new DiagonalMatrix(rows, columns); } /// /// Creates a matrix from a 2D array. /// /// The 2D array to create this matrix from. /// A matrix with the given values. protected override Matrix CreateMatrix(float[,] data) { return DiagonalMatrix.OfArray(data); } /// /// Creates a vector of the given size. /// /// The size of the vector to create. /// /// The new vector. protected override Vector CreateVector(int size) { return new DenseVector(size); } /// /// Creates a vector from an array. /// /// The array to create this vector from. /// The new vector. protected override Vector CreateVector(float[] data) { return new DenseVector(data); } /// /// Can create a matrix from a diagonal array. /// [Test] public void CanCreateMatrixFromDiagonalArray() { var testData = new Dictionary> { {"Singular3x3", new DiagonalMatrix(3, 3, new[] {1.0f, 0.0f, 3.0f})}, {"Square3x3", new DiagonalMatrix(3, 3, 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.That(testData[name], Is.EqualTo(TestMatrices[name])); } } /// /// Matrix from array is a reference. /// [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.0f, data[0]); } /// /// Can create a matrix from two-dimensional array. /// /// Matrix name. [TestCase("Singular3x3")] [TestCase("Singular4x4")] [TestCase("Square3x3")] [TestCase("Square4x4")] [TestCase("Tall3x2")] [TestCase("Wide2x3")] public void CanCreateMatrixFrom2DArray(string name) { var matrix = DiagonalMatrix.OfArray(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]); } } } /// /// Can create a matrix with uniform values. /// [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.0f); } } /// /// Can create an identity matrix. /// [Test] public void CanCreateIdentity() { var matrix = DiagonalMatrix.CreateIdentity(5); for (var i = 0; i < matrix.RowCount; i++) { for (var j = 0; j < matrix.ColumnCount; j++) { Assert.AreEqual(i == j ? 1.0f : 0.0f, matrix[i, j]); } } } /// /// Identity with wrong order throws ArgumentOutOfRangeException. /// /// The size of the square matrix [TestCase(0)] [TestCase(-1)] public void IdentityWithWrongOrderThrowsArgumentOutOfRangeException(int order) { Assert.Throws(() => DiagonalMatrix.CreateIdentity(order)); } /// /// Can multiply a matrix with matrix. /// /// Matrix A name. /// Matrix B name. 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.AlmostEqualRelative(matrixA.Row(i)*matrixB.Column(j), matrixC[i, j], 15); } } } /// /// Permute matrix rows throws InvalidOperationException. /// [Test] public void PermuteMatrixRowsThrowsInvalidOperationException() { var matrixp = CreateMatrix(TestData2D["Singular3x3"]); var permutation = new Permutation(new[] {2, 0, 1}); Assert.Throws(() => matrixp.PermuteRows(permutation)); } /// /// Permute matrix columns throws InvalidOperationException. /// [Test] public void PermuteMatrixColumnsThrowsInvalidOperationException() { var matrixp = CreateMatrix(TestData2D["Singular3x3"]); var permutation = new Permutation(new[] {2, 0, 1}); Assert.Throws(() => matrixp.PermuteColumns(permutation)); } /// /// Can pointwise divide matrices into a result matrix. /// public override void CanPointwiseDivideIntoResult() { 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]); } } } /// /// Can compute Frobenius norm. /// public override void CanComputeFrobeniusNorm() { var matrix = TestMatrices["Square3x3"]; var denseMatrix = DenseMatrix.OfArray(TestData2D["Square3x3"]); AssertHelpers.AlmostEqualRelative(denseMatrix.FrobeniusNorm(), matrix.FrobeniusNorm(), 7); matrix = TestMatrices["Wide2x3"]; denseMatrix = DenseMatrix.OfArray(TestData2D["Wide2x3"]); AssertHelpers.AlmostEqualRelative(denseMatrix.FrobeniusNorm(), matrix.FrobeniusNorm(), 7); matrix = TestMatrices["Tall3x2"]; denseMatrix = DenseMatrix.OfArray(TestData2D["Tall3x2"]); AssertHelpers.AlmostEqualRelative(denseMatrix.FrobeniusNorm(), matrix.FrobeniusNorm(), 7); } /// /// Can compute Infinity norm. /// public override void CanComputeInfinityNorm() { var matrix = TestMatrices["Square3x3"]; var denseMatrix = DenseMatrix.OfArray(TestData2D["Square3x3"]); AssertHelpers.AlmostEqualRelative(denseMatrix.InfinityNorm(), matrix.InfinityNorm(), 7); matrix = TestMatrices["Wide2x3"]; denseMatrix = DenseMatrix.OfArray(TestData2D["Wide2x3"]); AssertHelpers.AlmostEqualRelative(denseMatrix.InfinityNorm(), matrix.InfinityNorm(), 7); matrix = TestMatrices["Tall3x2"]; denseMatrix = DenseMatrix.OfArray(TestData2D["Tall3x2"]); AssertHelpers.AlmostEqualRelative(denseMatrix.InfinityNorm(), matrix.InfinityNorm(), 7); } /// /// Can compute L1 norm. /// public override void CanComputeL1Norm() { var matrix = TestMatrices["Square3x3"]; var denseMatrix = DenseMatrix.OfArray(TestData2D["Square3x3"]); AssertHelpers.AlmostEqualRelative(denseMatrix.L1Norm(), matrix.L1Norm(), 7); matrix = TestMatrices["Wide2x3"]; denseMatrix = DenseMatrix.OfArray(TestData2D["Wide2x3"]); AssertHelpers.AlmostEqualRelative(denseMatrix.L1Norm(), matrix.L1Norm(), 7); matrix = TestMatrices["Tall3x2"]; denseMatrix = DenseMatrix.OfArray(TestData2D["Tall3x2"]); AssertHelpers.AlmostEqualRelative(denseMatrix.L1Norm(), matrix.L1Norm(), 7); } /// /// Can compute L2 norm. /// public override void CanComputeL2Norm() { var matrix = TestMatrices["Square3x3"]; var denseMatrix = DenseMatrix.OfArray(TestData2D["Square3x3"]); AssertHelpers.AlmostEqualRelative(denseMatrix.L2Norm(), matrix.L2Norm(), 7); matrix = TestMatrices["Wide2x3"]; denseMatrix = DenseMatrix.OfArray(TestData2D["Wide2x3"]); AssertHelpers.AlmostEqualRelative(denseMatrix.L2Norm(), matrix.L2Norm(), 7); matrix = TestMatrices["Tall3x2"]; denseMatrix = DenseMatrix.OfArray(TestData2D["Tall3x2"]); AssertHelpers.AlmostEqualRelative(denseMatrix.L2Norm(), matrix.L2Norm(), 7); } /// /// Can compute determinant. /// [Test] public void CanComputeDeterminant() { var matrix = TestMatrices["Square3x3"]; var denseMatrix = DenseMatrix.OfArray(TestData2D["Square3x3"]); AssertHelpers.AlmostEqualRelative(denseMatrix.Determinant(), matrix.Determinant(), 7); matrix = TestMatrices["Square4x4"]; denseMatrix = DenseMatrix.OfArray(TestData2D["Square4x4"]); AssertHelpers.AlmostEqualRelative(denseMatrix.Determinant(), matrix.Determinant(), 7); } /// /// Determinant of non-square matrix throws ArgumentException. /// [Test] public void DeterminantNotSquareMatrixThrowsArgumentException() { var matrix = TestMatrices["Tall3x2"]; Assert.Throws(() => matrix.Determinant()); } /// /// Can check if a matrix is symmetric. /// [Test] public override void CanCheckIfMatrixIsSymmetric() { var matrix = TestMatrices["Square3x3"]; Assert.IsTrue(matrix.IsSymmetric); } [Test] public void DenseDiagonalMatrixMultiplication() { var dist = new ContinuousUniform(-1.0, 1.0, new MersenneTwister()); Assert.IsInstanceOf(Matrix.Build.DiagonalIdentity(3, 3)); var tall = Matrix.Build.Random(8, 3, dist); Assert.IsTrue((tall*Matrix.Build.DiagonalIdentity(3).Multiply(2f)).Equals(tall.Multiply(2f))); Assert.IsTrue((tall*Matrix.Build.Diagonal(3, 5, 2f)).Equals(tall.Multiply(2f).Append(Matrix.Build.Dense(8, 2)))); Assert.IsTrue((tall*Matrix.Build.Diagonal(3, 2, 2f)).Equals(tall.Multiply(2f).SubMatrix(0, 8, 0, 2))); var wide = Matrix.Build.Random(3, 8, dist); Assert.IsTrue((wide*Matrix.Build.DiagonalIdentity(8).Multiply(2f)).Equals(wide.Multiply(2f))); Assert.IsTrue((wide*Matrix.Build.Diagonal(8, 10, 2f)).Equals(wide.Multiply(2f).Append(Matrix.Build.Dense(3, 2)))); Assert.IsTrue((wide*Matrix.Build.Diagonal(8, 2, 2f)).Equals(wide.Multiply(2f).SubMatrix(0, 3, 0, 2))); } } }