From 17d5941ea286dab526151ef2da5632bb7e7c5181 Mon Sep 17 00:00:00 2001 From: Marcus Cuda Date: Sat, 28 Aug 2010 20:48:38 +0800 Subject: [PATCH] added andriy's single LA --- .../Atlas/AtlasLinearAlgebraProvider.cs | 480 +- .../ILinearAlgebraProviderOfT.cs | 108 +- .../ManagedLinearAlgebraProvider.cs | 6711 ++++++++++------- .../Mkl/MklLinearAlgebraProvider.cs | 480 +- .../LinearAlgebra/Complex/DenseMatrix.cs | 10 - .../LinearAlgebra/Complex/DenseVector.cs | 26 +- .../LinearAlgebra/Complex/DiagonalMatrix.cs | 34 +- .../Complex/Factorization/DenseCholesky.cs | 9 - .../Complex/Factorization/DenseLU.cs | 18 - .../Complex/Factorization/DenseQR.cs | 8 - .../Complex/Factorization/DenseSvd.cs | 10 - .../Complex/Factorization/GramSchmidt.cs | 8 - .../Complex/Factorization/UserCholesky.cs | 9 - .../Complex/Factorization/UserLU.cs | 18 - .../Complex/Factorization/UserQR.cs | 9 - .../Complex/Factorization/UserSvd.cs | 9 - .../LinearAlgebra/Complex/SparseMatrix.cs | 10 - .../LinearAlgebra/Complex/SparseVector.cs | 15 +- .../LinearAlgebra/Double/DenseMatrix.cs | 19 - .../LinearAlgebra/Double/DenseVector.cs | 19 - .../LinearAlgebra/Double/DiagonalMatrix.cs | 43 +- .../Double/Factorization/DenseCholesky.cs | 9 - .../Double/Factorization/DenseLU.cs | 18 - .../Double/Factorization/DenseQR.cs | 8 - .../Double/Factorization/DenseSvd.cs | 10 - .../Double/Factorization/GramSchmidt.cs | 9 - .../Double/Factorization/SparseCholesky.cs | 10 - .../Double/Factorization/SparseLU.cs | 17 - .../Double/Factorization/SparseQR.cs | 9 - .../Double/Factorization/SparseSvd.cs | 9 - .../Double/Factorization/UserCholesky.cs | 44 - .../Double/Factorization/UserLU.cs | 17 - .../Double/Factorization/UserQR.cs | 9 - .../Double/Factorization/UserSvd.cs | 9 - .../LinearAlgebra/Double/SparseMatrix.cs | 19 - .../LinearAlgebra/Double/SparseVector.cs | 19 - .../Generic/Factorization/Cholesky.cs | 42 +- .../Generic/Factorization/ExtensionMethods.cs | 5 + .../LinearAlgebra/Generic/Factorization/LU.cs | 73 +- .../LinearAlgebra/Generic/Factorization/QR.cs | 46 +- .../Generic/Factorization/Svd.cs | 59 +- src/Numerics/LinearAlgebra/Generic/Matrix.cs | 91 +- src/Numerics/LinearAlgebra/Generic/Vector.cs | 103 +- .../LinearAlgebra/Single/DenseMatrix.cs | 710 ++ .../LinearAlgebra/Single/DenseVector.cs | 1557 ++++ .../LinearAlgebra/Single/DiagonalMatrix.cs | 1723 +++++ .../Single/Factorization/DenseCholesky.cs | 212 + .../Single/Factorization/DenseLU.cs | 204 + .../Single/Factorization/DenseQR.cs | 194 + .../Single/Factorization/DenseSvd.cs | 206 + .../Single/Factorization/GramSchmidt.cs | 311 + .../Single/Factorization/UserCholesky.cs | 259 + .../Single/Factorization/UserLU.cs | 316 + .../Single/Factorization/UserQR.cs | 357 + .../Single/Factorization/UserSvd.cs | 950 +++ .../Single/IO/DelimitedReader.cs | 197 + .../Single/IO/DelimitedWriter.cs | 168 + .../Single/IO/Matlab/MatlabFile.cs | 219 + .../Single/IO/Matlab/MatlabParser.cs | 536 ++ .../LinearAlgebra/Single/IO/MatlabReader.cs | 190 + .../LinearAlgebra/Single/IO/MatrixReader.cs | 84 + .../LinearAlgebra/Single/IO/MatrixWriter.cs | 197 + .../Single/Solvers/Iterative/BiCgStab.cs | 520 ++ .../Solvers/Iterative/CompositeSolver.cs | 629 ++ .../Single/Solvers/Iterative/GpBiCg.cs | 624 ++ .../Single/Solvers/Iterative/MlkBiCgStab.cs | 779 ++ .../Single/Solvers/Iterative/TFQMR.cs | 528 ++ .../LinearAlgebra/Single/Solvers/Iterator.cs | 328 + .../Solvers/Preconditioners/Diagonal.cs | 150 + .../Single/Solvers/Preconditioners/Ilutp.cs | 729 ++ .../Preconditioners/IlutpElementSorter.cs | 227 + .../Solvers/Preconditioners/IncompleteLU.cs | 260 + .../Preconditioners/UnitPreconditioner.cs | 139 + .../StopCriterium/DivergenceStopCriterium.cs | 393 + .../StopCriterium/FailureStopCriterium.cs | 201 + .../IterationCountStopCriterium.cs | 227 + .../StopCriterium/ResidualStopCriterium.cs | 409 + .../LinearAlgebra/Single/SparseMatrix.cs | 1707 +++++ .../LinearAlgebra/Single/SparseVector.cs | 1683 +++++ src/Numerics/Numerics.csproj | 36 + src/Numerics/Precision.cs | 182 +- src/Numerics/SpecialFunctions/Stability.cs | 24 + src/Numerics/Threading/CommonParallel.cs | 61 + src/Silverlight/Silverlight.csproj | 87 + src/UnitTests/AssertHelpers.cs | 42 +- .../Preconditioners/IluptElementSorterTest.cs | 18 +- .../Complex/UserDefinedMatrixTests.cs | 5 - .../Complex/UserDefinedVectorTests.cs | 5 - .../Preconditioners/IluptElementSorterTest.cs | 17 +- .../Double/UserDefinedMatrixTests.cs | 10 - .../Double/UserDefinedVectorTests.cs | 10 - .../Single/DenseMatrixTests.cs | 156 + .../Single/DenseVectorTest.TextHandling.cs | 133 + .../Single/DenseVectorTests.cs | 290 + .../Single/DiagonalMatrixTests.cs | 450 ++ .../Single/Factorization/CholeskyTests.cs | 300 + .../Single/Factorization/GramSchmidtTests.cs | 331 + .../Single/Factorization/LUTests.cs | 364 + .../Single/Factorization/QRTests.cs | 318 + .../Single/Factorization/SvdTests.cs | 371 + .../Single/Factorization/UserCholeskyTests.cs | 299 + .../Single/Factorization/UserLUTests.cs | 363 + .../Single/Factorization/UserQRTests.cs | 317 + .../Single/Factorization/UserSvdTests.cs | 370 + .../Single/IO/DelimitedReaderTests.cs | 121 + .../Single/IO/DelimitedWriterTests.cs | 90 + .../Single/IO/MatlabReaderTests.cs | 70 + .../Single/LinearAlgebraProviderTests.cs | 367 + .../ManagedLinearAlgebraProviderTests.cs | 43 + .../LinearAlgebraTests/Single/MatrixLoader.cs | 171 + .../Single/MatrixTests.Arithmetic.cs | 888 +++ .../LinearAlgebraTests/Single/MatrixTests.cs | 1482 ++++ .../Single/Solvers/Iterative/BiCgStabTest.cs | 289 + .../Single/Solvers/Iterative/GpBiCgTest.cs | 291 + .../Solvers/Iterative/MlkBiCgStabTest.cs | 309 + .../Single/Solvers/Iterative/TFQMRTest.cs | 289 + .../Single/Solvers/IteratorTest.cs | 354 + .../Solvers/Preconditioners/DiagonalTest.cs | 32 + .../Preconditioners/IluptElementSorterTest.cs | 503 ++ .../Solvers/Preconditioners/IlutpTest.cs | 250 + .../Preconditioners/IncompleteLUTest.cs | 84 + .../Preconditioners/PreConditionerTest.cs | 127 + .../Preconditioners/UnitPreconditionerTest.cs | 29 + .../DivergenceStopCriteriumTest.cs | 233 + .../StopCriterium/FailureStopCriteriumTest.cs | 133 + .../IterationCountStopCriteriumTest.cs | 96 + .../ResidualStopCriteriumTest.cs | 261 + .../Single/SparseMatrixTests.cs | 173 + .../Single/SparseVectorTest.TextHandling.cs | 132 + .../Single/SparseVectorTest.cs | 414 + .../Single/UserDefinedMatrixTests.cs | 196 + .../Single/UserDefinedVectorTests.cs | 255 + .../Single/VectorTests.Arithmetic.cs | 901 +++ .../Single/VectorTests.Norm.cs | 90 + .../LinearAlgebraTests/Single/VectorTests.cs | 439 ++ src/UnitTests/UnitTests.csproj | 48 +- 136 files changed, 36718 insertions(+), 3812 deletions(-) create mode 100644 src/Numerics/LinearAlgebra/Single/DenseMatrix.cs create mode 100644 src/Numerics/LinearAlgebra/Single/DenseVector.cs create mode 100644 src/Numerics/LinearAlgebra/Single/DiagonalMatrix.cs create mode 100644 src/Numerics/LinearAlgebra/Single/Factorization/DenseCholesky.cs create mode 100644 src/Numerics/LinearAlgebra/Single/Factorization/DenseLU.cs create mode 100644 src/Numerics/LinearAlgebra/Single/Factorization/DenseQR.cs create mode 100644 src/Numerics/LinearAlgebra/Single/Factorization/DenseSvd.cs create mode 100644 src/Numerics/LinearAlgebra/Single/Factorization/GramSchmidt.cs create mode 100644 src/Numerics/LinearAlgebra/Single/Factorization/UserCholesky.cs create mode 100644 src/Numerics/LinearAlgebra/Single/Factorization/UserLU.cs create mode 100644 src/Numerics/LinearAlgebra/Single/Factorization/UserQR.cs create mode 100644 src/Numerics/LinearAlgebra/Single/Factorization/UserSvd.cs create mode 100644 src/Numerics/LinearAlgebra/Single/IO/DelimitedReader.cs create mode 100644 src/Numerics/LinearAlgebra/Single/IO/DelimitedWriter.cs create mode 100644 src/Numerics/LinearAlgebra/Single/IO/Matlab/MatlabFile.cs create mode 100644 src/Numerics/LinearAlgebra/Single/IO/Matlab/MatlabParser.cs create mode 100644 src/Numerics/LinearAlgebra/Single/IO/MatlabReader.cs create mode 100644 src/Numerics/LinearAlgebra/Single/IO/MatrixReader.cs create mode 100644 src/Numerics/LinearAlgebra/Single/IO/MatrixWriter.cs create mode 100644 src/Numerics/LinearAlgebra/Single/Solvers/Iterative/BiCgStab.cs create mode 100644 src/Numerics/LinearAlgebra/Single/Solvers/Iterative/CompositeSolver.cs create mode 100644 src/Numerics/LinearAlgebra/Single/Solvers/Iterative/GpBiCg.cs create mode 100644 src/Numerics/LinearAlgebra/Single/Solvers/Iterative/MlkBiCgStab.cs create mode 100644 src/Numerics/LinearAlgebra/Single/Solvers/Iterative/TFQMR.cs create mode 100644 src/Numerics/LinearAlgebra/Single/Solvers/Iterator.cs create mode 100644 src/Numerics/LinearAlgebra/Single/Solvers/Preconditioners/Diagonal.cs create mode 100644 src/Numerics/LinearAlgebra/Single/Solvers/Preconditioners/Ilutp.cs create mode 100644 src/Numerics/LinearAlgebra/Single/Solvers/Preconditioners/IlutpElementSorter.cs create mode 100644 src/Numerics/LinearAlgebra/Single/Solvers/Preconditioners/IncompleteLU.cs create mode 100644 src/Numerics/LinearAlgebra/Single/Solvers/Preconditioners/UnitPreconditioner.cs create mode 100644 src/Numerics/LinearAlgebra/Single/Solvers/StopCriterium/DivergenceStopCriterium.cs create mode 100644 src/Numerics/LinearAlgebra/Single/Solvers/StopCriterium/FailureStopCriterium.cs create mode 100644 src/Numerics/LinearAlgebra/Single/Solvers/StopCriterium/IterationCountStopCriterium.cs create mode 100644 src/Numerics/LinearAlgebra/Single/Solvers/StopCriterium/ResidualStopCriterium.cs create mode 100644 src/Numerics/LinearAlgebra/Single/SparseMatrix.cs create mode 100644 src/Numerics/LinearAlgebra/Single/SparseVector.cs create mode 100644 src/UnitTests/LinearAlgebraTests/Single/DenseMatrixTests.cs create mode 100644 src/UnitTests/LinearAlgebraTests/Single/DenseVectorTest.TextHandling.cs create mode 100644 src/UnitTests/LinearAlgebraTests/Single/DenseVectorTests.cs create mode 100644 src/UnitTests/LinearAlgebraTests/Single/DiagonalMatrixTests.cs create mode 100644 src/UnitTests/LinearAlgebraTests/Single/Factorization/CholeskyTests.cs create mode 100644 src/UnitTests/LinearAlgebraTests/Single/Factorization/GramSchmidtTests.cs create mode 100644 src/UnitTests/LinearAlgebraTests/Single/Factorization/LUTests.cs create mode 100644 src/UnitTests/LinearAlgebraTests/Single/Factorization/QRTests.cs create mode 100644 src/UnitTests/LinearAlgebraTests/Single/Factorization/SvdTests.cs create mode 100644 src/UnitTests/LinearAlgebraTests/Single/Factorization/UserCholeskyTests.cs create mode 100644 src/UnitTests/LinearAlgebraTests/Single/Factorization/UserLUTests.cs create mode 100644 src/UnitTests/LinearAlgebraTests/Single/Factorization/UserQRTests.cs create mode 100644 src/UnitTests/LinearAlgebraTests/Single/Factorization/UserSvdTests.cs create mode 100644 src/UnitTests/LinearAlgebraTests/Single/IO/DelimitedReaderTests.cs create mode 100644 src/UnitTests/LinearAlgebraTests/Single/IO/DelimitedWriterTests.cs create mode 100644 src/UnitTests/LinearAlgebraTests/Single/IO/MatlabReaderTests.cs create mode 100644 src/UnitTests/LinearAlgebraTests/Single/LinearAlgebraProviderTests.cs create mode 100644 src/UnitTests/LinearAlgebraTests/Single/ManagedLinearAlgebraProviderTests.cs create mode 100644 src/UnitTests/LinearAlgebraTests/Single/MatrixLoader.cs create mode 100644 src/UnitTests/LinearAlgebraTests/Single/MatrixTests.Arithmetic.cs create mode 100644 src/UnitTests/LinearAlgebraTests/Single/MatrixTests.cs create mode 100644 src/UnitTests/LinearAlgebraTests/Single/Solvers/Iterative/BiCgStabTest.cs create mode 100644 src/UnitTests/LinearAlgebraTests/Single/Solvers/Iterative/GpBiCgTest.cs create mode 100644 src/UnitTests/LinearAlgebraTests/Single/Solvers/Iterative/MlkBiCgStabTest.cs create mode 100644 src/UnitTests/LinearAlgebraTests/Single/Solvers/Iterative/TFQMRTest.cs create mode 100644 src/UnitTests/LinearAlgebraTests/Single/Solvers/IteratorTest.cs create mode 100644 src/UnitTests/LinearAlgebraTests/Single/Solvers/Preconditioners/DiagonalTest.cs create mode 100644 src/UnitTests/LinearAlgebraTests/Single/Solvers/Preconditioners/IluptElementSorterTest.cs create mode 100644 src/UnitTests/LinearAlgebraTests/Single/Solvers/Preconditioners/IlutpTest.cs create mode 100644 src/UnitTests/LinearAlgebraTests/Single/Solvers/Preconditioners/IncompleteLUTest.cs create mode 100644 src/UnitTests/LinearAlgebraTests/Single/Solvers/Preconditioners/PreConditionerTest.cs create mode 100644 src/UnitTests/LinearAlgebraTests/Single/Solvers/Preconditioners/UnitPreconditionerTest.cs create mode 100644 src/UnitTests/LinearAlgebraTests/Single/Solvers/StopCriterium/DivergenceStopCriteriumTest.cs create mode 100644 src/UnitTests/LinearAlgebraTests/Single/Solvers/StopCriterium/FailureStopCriteriumTest.cs create mode 100644 src/UnitTests/LinearAlgebraTests/Single/Solvers/StopCriterium/IterationCountStopCriteriumTest.cs create mode 100644 src/UnitTests/LinearAlgebraTests/Single/Solvers/StopCriterium/ResidualStopCriteriumTest.cs create mode 100644 src/UnitTests/LinearAlgebraTests/Single/SparseMatrixTests.cs create mode 100644 src/UnitTests/LinearAlgebraTests/Single/SparseVectorTest.TextHandling.cs create mode 100644 src/UnitTests/LinearAlgebraTests/Single/SparseVectorTest.cs create mode 100644 src/UnitTests/LinearAlgebraTests/Single/UserDefinedMatrixTests.cs create mode 100644 src/UnitTests/LinearAlgebraTests/Single/UserDefinedVectorTests.cs create mode 100644 src/UnitTests/LinearAlgebraTests/Single/VectorTests.Arithmetic.cs create mode 100644 src/UnitTests/LinearAlgebraTests/Single/VectorTests.Norm.cs create mode 100644 src/UnitTests/LinearAlgebraTests/Single/VectorTests.cs diff --git a/src/Numerics/Algorithms/LinearAlgebra/Atlas/AtlasLinearAlgebraProvider.cs b/src/Numerics/Algorithms/LinearAlgebra/Atlas/AtlasLinearAlgebraProvider.cs index 04424c55..dd3e9a4e 100644 --- a/src/Numerics/Algorithms/LinearAlgebra/Atlas/AtlasLinearAlgebraProvider.cs +++ b/src/Numerics/Algorithms/LinearAlgebra/Atlas/AtlasLinearAlgebraProvider.cs @@ -267,17 +267,17 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Atlas /// Multiples two matrices. result = x * y /// /// The x matrix. - /// The number of rows in the x matrix. - /// The number of columns in the x matrix. + /// The number of rows in the x matrix. + /// The number of columns in the x matrix. /// The y matrix. - /// The number of rows in the y matrix. - /// The number of columns in the y matrix. + /// The number of rows in the y matrix. + /// The number of columns in the y matrix. /// Where to store the result of the multiplication. /// This is a simplified version of the BLAS GEMM routine with alpha /// set to 1.0 and beta set to 0.0, and x and y are not transposed. - public void MatrixMultiply(double[] x, int xRows, int xColumns, double[] y, int yRows, int yColumns, double[] result) + public void MatrixMultiply(double[] x, int rowsX, int columnsX, double[] y, int rowsY, int columnsY, double[] result) { - MatrixMultiplyWithUpdate(Transpose.DontTranspose, Transpose.DontTranspose, 1.0, x, xRows, xColumns, y, yRows, yColumns, 0.0, result); + MatrixMultiplyWithUpdate(Transpose.DontTranspose, Transpose.DontTranspose, 1.0, x, rowsX, columnsX, y, rowsY, columnsY, 0.0, result); } /// @@ -287,15 +287,15 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Atlas /// How to transpose the matrix. /// The value to scale matrix. /// The a matrix. - /// The number of rows in the matrix. - /// The number of columns in the matrix. + /// The number of rows in the matrix. + /// The number of columns in the matrix. /// The b matrix - /// The number of rows in the matrix. - /// The number of columns in the matrix. + /// The number of rows in the matrix. + /// The number of columns in the matrix. /// The value to scale the matrix. /// The c matrix. public void MatrixMultiplyWithUpdate(Transpose transposeA, Transpose transposeB, double alpha, double[] a, - int aRows, int aColumns, double[] b, int bRows, int bColumns, double beta, double[] c) + int rowsA, int columnsA, double[] b, int rowsB, int columnsB, double beta, double[] c) { if (a == null) { @@ -312,16 +312,16 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Atlas throw new ArgumentNullException("c"); } - var m = transposeA == Transpose.DontTranspose ? aRows : aColumns; - var n = transposeB == Transpose.DontTranspose ? bColumns : bRows; - var k = transposeA == Transpose.DontTranspose ? aColumns : aRows; + var m = transposeA == Transpose.DontTranspose ? rowsA : columnsA; + var n = transposeB == Transpose.DontTranspose ? columnsB : rowsB; + var k = transposeA == Transpose.DontTranspose ? columnsA : rowsA; - if( c.Length != aRows * bColumns) + if( c.Length != rowsA * columnsB) { throw new ArgumentException(Resources.ArgumentMatrixDimensions); } - if (aColumns != bRows) + if (columnsA != rowsB) { throw new ArgumentException(Resources.ArgumentMatrixDimensions); } @@ -477,13 +477,13 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Atlas /// Solves A*X=B for X using Cholesky factorization. /// /// The square, positive definite matrix A. - /// The number of rows and columns in A. + /// The number of rows and columns in A. /// The B matrix. - /// The number of rows in the B matrix. - /// The number of columns in the B matrix. + /// The number of rows in the B matrix. + /// The number of columns in the B matrix. /// This is equivalent to the POTRF add POTRS LAPACK routines. /// - public void CholeskySolve(double[] a, int aOrder, double[] b, int bRows, int bColumns) + public void CholeskySolve(double[] a, int orderA, double[] b, int rowsB, int columnsB) { throw new NotImplementedException(); } @@ -492,12 +492,12 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Atlas /// Solves A*X=B for X using a previously factored A matrix. /// /// The square, positive definite matrix A. - /// The number of rows and columns in A. + /// The number of rows and columns in A. /// The B matrix. - /// The number of rows in the B matrix. - /// The number of columns in the B matrix. + /// 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) + public void CholeskySolveFactored(double[] a, int orderA, double[] b, int rowsB, int columnsB) { throw new NotImplementedException(); } @@ -507,12 +507,12 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Atlas /// /// On entry, it is the M by N A matrix to factor. On exit, /// it is overwritten with the R matrix of the QR factorization. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// On exit, A M by M matrix that holds the Q matrix of the /// QR factorization. /// This is similar to the GEQRF and ORGQR LAPACK routines. - public void QRFactor(double[] r, int rRows, int rColumns, double[] q) + public void QRFactor(double[] r, int rowsR, int columnsR, double[] q) { throw new NotImplementedException(); } @@ -522,15 +522,15 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Atlas /// /// On entry, it is the M by N A matrix to factor. On exit, /// it is overwritten with the R matrix of the QR factorization. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// On exit, A M by M matrix that holds the Q matrix of the /// QR factorization. /// The work array. The array must have a length of at least N, /// but should be N*blocksize. The blocksize is machine dependent. On exit, work[0] contains the optimal /// work size value. /// This is similar to the GEQRF and ORGQR LAPACK routines. - public void QRFactor(double[] r, int rRows, int rColumns, double[] q, double[] work) + public void QRFactor(double[] r, int rowsR, int columnsR, double[] q, double[] work) { throw new NotImplementedException(); } @@ -540,14 +540,14 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Atlas /// /// On entry, it is the M by N A matrix to factor. On exit, /// it is overwritten with the R matrix of the QR factorization. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// On exit, A M by M matrix that holds the Q matrix of the /// QR factorization. /// The B matrix. - /// The number of columns of B. + /// The number of columns of B. /// On exit, the solution matrix. - public void QRSolve(double[] r, int rRows, int rColumns, double[] q, double[] b, int bColumns, double[] x) + public void QRSolve(double[] r, int rowsR, int columnsR, double[] q, double[] b, int columnsB, double[] x) { throw new NotImplementedException(); } @@ -557,17 +557,17 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Atlas /// /// On entry, it is the M by N A matrix to factor. On exit, /// it is overwritten with the R matrix of the QR factorization. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// On exit, A M by M matrix that holds the Q matrix of the /// QR factorization. /// The B matrix. - /// The number of columns of B. + /// The number of columns of B. /// On exit, the solution matrix. /// The work array. The array must have a length of at least N, /// but should be N*blocksize. The blocksize is machine dependent. On exit, work[0] contains the optimal /// work size value. - public void QRSolve(double[] r, int rRows, int rColumns, double[] q, double[] b, int bColumns, double[] x, double[] work) + public void QRSolve(double[] r, int rowsR, int columnsR, double[] q, double[] b, int columnsB, double[] x, double[] work) { throw new NotImplementedException(); } @@ -577,12 +577,12 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Atlas /// /// The Q matrix obtained by calling . /// The R matrix obtained by calling . - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// The B matrix. - /// The number of columns of B. + /// The number of columns of B. /// On exit, the solution matrix. - public void QRSolveFactored(double[] q, double[] r, int rRows, int rColumns, double[] b, int bColumns, double[] x) + public void QRSolveFactored(double[] q, double[] r, int rowsR, int columnsR, double[] b, int columnsB, double[] x) { throw new NotImplementedException(); } @@ -592,15 +592,15 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Atlas /// /// Compute the singular U and VT vectors or not. /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// The singular values of A in ascending value. /// If is true, on exit U contains the left /// singular vectors. /// If is true, on exit VT contains the transposed /// right singular vectors. /// This is equivalent to the GESVD LAPACK routine. - public void SingularValueDecomposition(bool computeVectors, double[] a, int aRows, int aColumns, double[] s, double[] u, double[] vt) + public void SingularValueDecomposition(bool computeVectors, double[] a, int rowsA, int columnsA, double[] s, double[] u, double[] vt) { throw new NotImplementedException(); } @@ -610,8 +610,8 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Atlas /// /// Compute the singular U and VT vectors or not. /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// The singular values of A in ascending value. /// If is true, on exit U contains the left /// singular vectors. @@ -621,7 +621,7 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Atlas /// Max(3*Min(M, N) + Max(M, N), 5*Min(M,N)). For complex matrices, 2*Min(M, N) + Max(M, N). /// On exit, work[0] contains the optimal work size value. /// This is equivalent to the GESVD LAPACK routine. - public void SingularValueDecomposition(bool computeVectors, double[] a, int aRows, int aColumns, double[] s, double[] u, double[] vt, double[] work) + public void SingularValueDecomposition(bool computeVectors, double[] a, int rowsA, int columnsA, double[] s, double[] u, double[] vt, double[] work) { throw new NotImplementedException(); } @@ -630,15 +630,15 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Atlas /// Solves A*X=B for X using the singular value decomposition of A. /// /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// The singular values of A in ascending value. /// On exit U contains the left singular vectors. /// On exit VT contains the transposed right singular vectors. /// The B matrix. - /// The number of columns of B. + /// The number of columns of B. /// On exit, the solution matrix. - public void SvdSolve(double[] a, int aRows, int aColumns, double[] s, double[] u, double[] vt, double[] b, int bColumns, double[] x) + public void SvdSolve(double[] a, int rowsA, int columnsA, double[] s, double[] u, double[] vt, double[] b, int columnsB, double[] x) { throw new NotImplementedException(); } @@ -647,18 +647,18 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Atlas /// Solves A*X=B for X using the singular value decomposition of A. /// /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// The singular values of A in ascending value. /// On exit U contains the left singular vectors. /// On exit VT contains the transposed right singular vectors. /// The B matrix. - /// The number of columns of B. + /// The number of columns of B. /// On exit, the solution matrix. /// The work array. For real matrices, the work array should be at least /// Max(3*Min(M, N) + Max(M, N), 5*Min(M,N)). For complex matrices, 2*Min(M, N) + Max(M, N). /// On exit, work[0] contains the optimal work size value. - public void SvdSolve(double[] a, int aRows, int aColumns, double[] s, double[] u, double[] vt, double[] b, int bColumns, double[] x, double[] work) + public void SvdSolve(double[] a, int rowsA, int columnsA, double[] s, double[] u, double[] vt, double[] b, int columnsB, double[] x, double[] work) { throw new NotImplementedException(); } @@ -666,15 +666,15 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Atlas /// /// Solves A*X=B for X using a previously SVD decomposed matrix. /// - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// The s values returned by . /// The left singular vectors returned by . /// The right singular vectors returned by . /// The B matrix. - /// The number of columns of B. + /// The number of columns of B. /// On exit, the solution matrix. - public void SvdSolveFactored(int aRows, int aColumns, double[] s, double[] u, double[] vt, double[] b, int bColumns, double[] x) + public void SvdSolveFactored(int rowsA, int columnsA, double[] s, double[] u, double[] vt, double[] b, int columnsB, double[] x) { throw new NotImplementedException(); } @@ -904,17 +904,17 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Atlas /// Multiples two matrices. result = x * y /// /// The x matrix. - /// The number of rows in the x matrix. - /// The number of columns in the x matrix. + /// The number of rows in the x matrix. + /// The number of columns in the x matrix. /// The y matrix. - /// The number of rows in the y matrix. - /// The number of columns in the y matrix. + /// The number of rows in the y matrix. + /// The number of columns in the y matrix. /// Where to store the result of the multiplication. /// This is a simplified version of the BLAS GEMM routine with alpha /// set to 1.0 and beta set to 0.0, and x and y are not transposed. - public void MatrixMultiply(float[] x, int xRows, int xColumns, float[] y, int yRows, int yColumns, float[] result) + public void MatrixMultiply(float[] x, int rowsX, int columnsX, float[] y, int rowsY, int columnsY, float[] result) { - MatrixMultiplyWithUpdate(Transpose.DontTranspose, Transpose.DontTranspose, 1.0f, x, xRows, xColumns, y, yRows, yColumns, 0.0f, result); + MatrixMultiplyWithUpdate(Transpose.DontTranspose, Transpose.DontTranspose, 1.0f, x, rowsX, columnsX, y, rowsY, columnsY, 0.0f, result); } /// @@ -924,15 +924,15 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Atlas /// How to transpose the matrix. /// The value to scale matrix. /// The a matrix. - /// The number of rows in the matrix. - /// The number of columns in the matrix. + /// The number of rows in the matrix. + /// The number of columns in the matrix. /// The b matrix - /// The number of rows in the matrix. - /// The number of columns in the matrix. + /// The number of rows in the matrix. + /// The number of columns in the matrix. /// The value to scale the matrix. /// The c matrix. public void MatrixMultiplyWithUpdate(Transpose transposeA, Transpose transposeB, float alpha, float[] a, - int aRows, int aColumns, float[] b, int bRows, int bColumns, float beta, float[] c) + int rowsA, int columnsA, float[] b, int rowsB, int columnsB, float beta, float[] c) { if (a == null) { @@ -949,16 +949,16 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Atlas throw new ArgumentNullException("c"); } - var m = transposeA == Transpose.DontTranspose ? aRows : aColumns; - var n = transposeB == Transpose.DontTranspose ? bColumns : bRows; - var k = transposeA == Transpose.DontTranspose ? aColumns : aRows; + var m = transposeA == Transpose.DontTranspose ? rowsA : columnsA; + var n = transposeB == Transpose.DontTranspose ? columnsB : rowsB; + var k = transposeA == Transpose.DontTranspose ? columnsA : rowsA; - if( c.Length != aRows * bColumns) + if( c.Length != rowsA * columnsB) { throw new ArgumentException(Resources.ArgumentMatrixDimensions); } - if (aColumns != bRows) + if (columnsA != rowsB) { throw new ArgumentException(Resources.ArgumentMatrixDimensions); } @@ -1114,12 +1114,12 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Atlas /// Solves A*X=B for X using Cholesky factorization. /// /// The square, positive definite matrix A. - /// The number of rows and columns in A. + /// The number of rows and columns in A. /// The B matrix. - /// The number of rows in the B matrix. - /// The number of columns in the B matrix. + /// The number of rows in the B matrix. + /// The number of columns in the B matrix. /// This is equivalent to the POTRF add POTRS LAPACK routines. - public void CholeskySolve(float[] a, int aOrder, float[] b, int bRows, int bColumns) + public void CholeskySolve(float[] a, int orderA, float[] b, int rowsB, int columnsB) { throw new NotImplementedException(); } @@ -1128,12 +1128,12 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Atlas /// Solves A*X=B for X using a previously factored A matrix. /// /// The square, positive definite matrix A. - /// The number of rows and columns in A. + /// The number of rows and columns in A. /// The B matrix. - /// The number of rows in the B matrix. - /// The number of columns in the B matrix. + /// 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(float[] a, int aOrder, float[] b, int bRows, int bColumns) + public void CholeskySolveFactored(float[] a, int orderA, float[] b, int rowsB, int columnsB) { throw new NotImplementedException(); } @@ -1143,12 +1143,12 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Atlas /// /// On entry, it is the M by N A matrix to factor. On exit, /// it is overwritten with the R matrix of the QR factorization. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// On exit, A M by M matrix that holds the Q matrix of the /// QR factorization. /// This is similar to the GEQRF and ORGQR LAPACK routines. - public void QRFactor(float[] r, int rRows, int rColumns, float[] q) + public void QRFactor(float[] r, int rowsR, int columnsR, float[] q) { throw new NotImplementedException(); } @@ -1158,15 +1158,15 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Atlas /// /// On entry, it is the M by N A matrix to factor. On exit, /// it is overwritten with the R matrix of the QR factorization. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// On exit, A M by M matrix that holds the Q matrix of the /// QR factorization. /// The work array. The array must have a length of at least N, /// but should be N*blocksize. The blocksize is machine dependent. On exit, work[0] contains the optimal /// work size value. /// This is similar to the GEQRF and ORGQR LAPACK routines. - public void QRFactor(float[] r, int rRows, int rColumns, float[] q, float[] work) + public void QRFactor(float[] r, int rowsR, int columnsR, float[] q, float[] work) { throw new NotImplementedException(); } @@ -1176,14 +1176,14 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Atlas /// /// On entry, it is the M by N A matrix to factor. On exit, /// it is overwritten with the R matrix of the QR factorization. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// On exit, A M by M matrix that holds the Q matrix of the /// QR factorization. /// The B matrix. - /// The number of columns of B. + /// The number of columns of B. /// On exit, the solution matrix. - public void QRSolve(float[] r, int rRows, int rColumns, float[] q, float[] b, int bColumns, float[] x) + public void QRSolve(float[] r, int rowsR, int columnsR, float[] q, float[] b, int columnsB, float[] x) { throw new NotImplementedException(); } @@ -1193,17 +1193,17 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Atlas /// /// On entry, it is the M by N A matrix to factor. On exit, /// it is overwritten with the R matrix of the QR factorization. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// On exit, A M by M matrix that holds the Q matrix of the /// QR factorization. /// The B matrix. - /// The number of columns of B. + /// The number of columns of B. /// On exit, the solution matrix. /// The work array. The array must have a length of at least N, /// but should be N*blocksize. The blocksize is machine dependent. On exit, work[0] contains the optimal /// work size value. - public void QRSolve(float[] r, int rRows, int rColumns, float[] q, float[] b, int bColumns, float[] x, float[] work) + public void QRSolve(float[] r, int rowsR, int columnsR, float[] q, float[] b, int columnsB, float[] x, float[] work) { throw new NotImplementedException(); } @@ -1213,12 +1213,12 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Atlas /// /// The Q matrix obtained by calling . /// The R matrix obtained by calling . - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// The B matrix. - /// The number of columns of B. + /// The number of columns of B. /// On exit, the solution matrix. - public void QRSolveFactored(float[] q, float[] r, int rRows, int rColumns, float[] b, int bColumns, float[] x) + public void QRSolveFactored(float[] q, float[] r, int rowsR, int columnsR, float[] b, int columnsB, float[] x) { throw new NotImplementedException(); } @@ -1228,15 +1228,15 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Atlas /// /// Compute the singular U and VT vectors or not. /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// The singular values of A in ascending value. /// If is true, on exit U contains the left /// singular vectors. /// If is true, on exit VT contains the transposed /// right singular vectors. /// This is equivalent to the GESVD LAPACK routine. - public void SingularValueDecomposition(bool computeVectors, float[] a, int aRows, int aColumns, float[] s, float[] u, float[] vt) + public void SingularValueDecomposition(bool computeVectors, float[] a, int rowsA, int columnsA, float[] s, float[] u, float[] vt) { throw new NotImplementedException(); } @@ -1246,8 +1246,8 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Atlas /// /// Compute the singular U and VT vectors or not. /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// The singular values of A in ascending value. /// If is true, on exit U contains the left /// singular vectors. @@ -1257,7 +1257,7 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Atlas /// Max(3*Min(M, N) + Max(M, N), 5*Min(M,N)). For complex matrices, 2*Min(M, N) + Max(M, N). /// On exit, work[0] contains the optimal work size value. /// This is equivalent to the GESVD LAPACK routine. - public void SingularValueDecomposition(bool computeVectors, float[] a, int aRows, int aColumns, float[] s, float[] u, float[] vt, float[] work) + public void SingularValueDecomposition(bool computeVectors, float[] a, int rowsA, int columnsA, float[] s, float[] u, float[] vt, float[] work) { throw new NotImplementedException(); } @@ -1266,15 +1266,15 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Atlas /// Solves A*X=B for X using the singular value decomposition of A. /// /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// The singular values of A in ascending value. /// On exit U contains the left singular vectors. /// On exit VT contains the transposed right singular vectors. /// The B matrix. - /// The number of columns of B. + /// The number of columns of B. /// On exit, the solution matrix. - public void SvdSolve(float[] a, int aRows, int aColumns, float[] s, float[] u, float[] vt, float[] b, int bColumns, float[] x) + public void SvdSolve(float[] a, int rowsA, int columnsA, float[] s, float[] u, float[] vt, float[] b, int columnsB, float[] x) { throw new NotImplementedException(); } @@ -1283,18 +1283,18 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Atlas /// Solves A*X=B for X using the singular value decomposition of A. /// /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// The singular values of A in ascending value. /// On exit U contains the left singular vectors. /// On exit VT contains the transposed right singular vectors. /// The B matrix. - /// The number of columns of B. + /// The number of columns of B. /// On exit, the solution matrix. /// The work array. For real matrices, the work array should be at least /// Max(3*Min(M, N) + Max(M, N), 5*Min(M,N)). For complex matrices, 2*Min(M, N) + Max(M, N). /// On exit, work[0] contains the optimal work size value. - public void SvdSolve(float[] a, int aRows, int aColumns, float[] s, float[] u, float[] vt, float[] b, int bColumns, float[] x, float[] work) + public void SvdSolve(float[] a, int rowsA, int columnsA, float[] s, float[] u, float[] vt, float[] b, int columnsB, float[] x, float[] work) { throw new NotImplementedException(); } @@ -1302,15 +1302,15 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Atlas /// /// Solves A*X=B for X using a previously SVD decomposed matrix. /// - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// The s values returned by . /// The left singular vectors returned by . /// The right singular vectors returned by . /// The B matrix. - /// The number of columns of B. + /// The number of columns of B. /// On exit, the solution matrix. - public void SvdSolveFactored(int aRows, int aColumns, float[] s, float[] u, float[] vt, float[] b, int bColumns, float[] x) + public void SvdSolveFactored(int rowsA, int columnsA, float[] s, float[] u, float[] vt, float[] b, int columnsB, float[] x) { throw new NotImplementedException(); } @@ -1540,17 +1540,17 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Atlas /// Multiples two matrices. result = x * y /// /// The x matrix. - /// The number of rows in the x matrix. - /// The number of columns in the x matrix. + /// The number of rows in the x matrix. + /// The number of columns in the x matrix. /// The y matrix. - /// The number of rows in the y matrix. - /// The number of columns in the y matrix. + /// The number of rows in the y matrix. + /// The number of columns in the y matrix. /// Where to store the result of the multiplication. /// This is a simplified version of the BLAS GEMM routine with alpha /// set to 1.0 and beta set to 0.0, and x and y are not transposed. - public void MatrixMultiply(Complex[] x, int xRows, int xColumns, Complex[] y, int yRows, int yColumns, Complex[] result) + public void MatrixMultiply(Complex[] x, int rowsX, int columnsX, Complex[] y, int rowsY, int columnsY, Complex[] result) { - MatrixMultiplyWithUpdate(Transpose.DontTranspose, Transpose.DontTranspose, Complex.One, x, xRows, xColumns, y, yRows, yColumns, Complex.Zero, result); + MatrixMultiplyWithUpdate(Transpose.DontTranspose, Transpose.DontTranspose, Complex.One, x, rowsX, columnsX, y, rowsY, columnsY, Complex.Zero, result); } /// @@ -1560,15 +1560,15 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Atlas /// How to transpose the matrix. /// The value to scale matrix. /// The a matrix. - /// The number of rows in the matrix. - /// The number of columns in the matrix. + /// The number of rows in the matrix. + /// The number of columns in the matrix. /// The b matrix - /// The number of rows in the matrix. - /// The number of columns in the matrix. + /// The number of rows in the matrix. + /// The number of columns in the matrix. /// The value to scale the matrix. /// The c matrix. public void MatrixMultiplyWithUpdate(Transpose transposeA, Transpose transposeB, Complex alpha, Complex[] a, - int aRows, int aColumns, Complex[] b, int bRows, int bColumns, Complex beta, Complex[] c) + int rowsA, int columnsA, Complex[] b, int rowsB, int columnsB, Complex beta, Complex[] c) { if (a == null) { @@ -1585,16 +1585,16 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Atlas throw new ArgumentNullException("c"); } - var m = transposeA == Transpose.DontTranspose ? aRows : aColumns; - var n = transposeB == Transpose.DontTranspose ? bColumns : bRows; - var k = transposeA == Transpose.DontTranspose ? aColumns : aRows; + var m = transposeA == Transpose.DontTranspose ? rowsA : columnsA; + var n = transposeB == Transpose.DontTranspose ? columnsB : rowsB; + var k = transposeA == Transpose.DontTranspose ? columnsA : rowsA; - if( c.Length != aRows * bColumns) + if( c.Length != rowsA * columnsB) { throw new ArgumentException(Resources.ArgumentMatrixDimensions); } - if (aColumns != bRows) + if (columnsA != rowsB) { throw new ArgumentException(Resources.ArgumentMatrixDimensions); } @@ -1750,12 +1750,12 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Atlas /// Solves A*X=B for X using Cholesky factorization. /// /// The square, positive definite matrix A. - /// The number of rows and columns in A. + /// The number of rows and columns in A. /// The B matrix. - /// The number of rows in the B matrix. - /// The number of columns in the B matrix. + /// The number of rows in the B matrix. + /// The number of columns in the B matrix. /// This is equivalent to the POTRF add POTRS LAPACK routines. - public void CholeskySolve(Complex[] a, int aOrder, Complex[] b, int bRows, int bColumns) + public void CholeskySolve(Complex[] a, int orderA, Complex[] b, int rowsB, int columnsB) { throw new NotImplementedException(); } @@ -1764,12 +1764,12 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Atlas /// Solves A*X=B for X using a previously factored A matrix. /// /// The square, positive definite matrix A. - /// The number of rows and columns in A. + /// The number of rows and columns in A. /// The B matrix. - /// The number of rows in the B matrix. - /// The number of columns in the B matrix. + /// 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(Complex[] a, int aOrder, Complex[] b, int bRows, int bColumns) + public void CholeskySolveFactored(Complex[] a, int orderA, Complex[] b, int rowsB, int columnsB) { throw new NotImplementedException(); } @@ -1779,12 +1779,12 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Atlas /// /// On entry, it is the M by N A matrix to factor. On exit, /// it is overwritten with the R matrix of the QR factorization. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// On exit, A M by M matrix that holds the Q matrix of the /// QR factorization. /// This is similar to the GEQRF and ORGQR LAPACK routines. - public void QRFactor(Complex[] r, int rRows, int rColumns, Complex[] q) + public void QRFactor(Complex[] r, int rowsR, int columnsR, Complex[] q) { throw new NotImplementedException(); } @@ -1794,15 +1794,15 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Atlas /// /// On entry, it is the M by N A matrix to factor. On exit, /// it is overwritten with the R matrix of the QR factorization. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// On exit, A M by M matrix that holds the Q matrix of the /// QR factorization. /// The work array. The array must have a length of at least N, /// but should be N*blocksize. The blocksize is machine dependent. On exit, work[0] contains the optimal /// work size value. /// This is similar to the GEQRF and ORGQR LAPACK routines. - public void QRFactor(Complex[] r, int rRows, int rColumns, Complex[] q, Complex[] work) + public void QRFactor(Complex[] r, int rowsR, int columnsR, Complex[] q, Complex[] work) { throw new NotImplementedException(); } @@ -1812,14 +1812,14 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Atlas /// /// On entry, it is the M by N A matrix to factor. On exit, /// it is overwritten with the R matrix of the QR factorization. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// On exit, A M by M matrix that holds the Q matrix of the /// QR factorization. /// The B matrix. - /// The number of columns of B. + /// The number of columns of B. /// On exit, the solution matrix. - public void QRSolve(Complex[] r, int rRows, int rColumns, Complex[] q, Complex[] b, int bColumns, Complex[] x) + public void QRSolve(Complex[] r, int rowsR, int columnsR, Complex[] q, Complex[] b, int columnsB, Complex[] x) { throw new NotImplementedException(); } @@ -1829,17 +1829,17 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Atlas /// /// On entry, it is the M by N A matrix to factor. On exit, /// it is overwritten with the R matrix of the QR factorization. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// On exit, A M by M matrix that holds the Q matrix of the /// QR factorization. /// The B matrix. - /// The number of columns of B. + /// The number of columns of B. /// On exit, the solution matrix. /// The work array. The array must have a length of at least N, /// but should be N*blocksize. The blocksize is machine dependent. On exit, work[0] contains the optimal /// work size value. - public void QRSolve(Complex[] r, int rRows, int rColumns, Complex[] q, Complex[] b, int bColumns, Complex[] x, Complex[] work) + public void QRSolve(Complex[] r, int rowsR, int columnsR, Complex[] q, Complex[] b, int columnsB, Complex[] x, Complex[] work) { throw new NotImplementedException(); } @@ -1849,12 +1849,12 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Atlas /// /// The Q matrix obtained by calling . /// The R matrix obtained by calling . - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// The B matrix. - /// The number of columns of B. + /// The number of columns of B. /// On exit, the solution matrix. - public void QRSolveFactored(Complex[] q, Complex[] r, int rRows, int rColumns, Complex[] b, int bColumns, Complex[] x) + public void QRSolveFactored(Complex[] q, Complex[] r, int rowsR, int columnsR, Complex[] b, int columnsB, Complex[] x) { throw new NotImplementedException(); } @@ -1864,15 +1864,15 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Atlas /// /// Compute the singular U and VT vectors or not. /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// The singular values of A in ascending value. /// If is true, on exit U contains the left /// singular vectors. /// If is true, on exit VT contains the transposed /// right singular vectors. /// This is equivalent to the GESVD LAPACK routine. - public void SingularValueDecomposition(bool computeVectors, Complex[] a, int aRows, int aColumns, Complex[] s, Complex[] u, Complex[] vt) + public void SingularValueDecomposition(bool computeVectors, Complex[] a, int rowsA, int columnsA, Complex[] s, Complex[] u, Complex[] vt) { throw new NotImplementedException(); } @@ -1882,8 +1882,8 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Atlas /// /// Compute the singular U and VT vectors or not. /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// The singular values of A in ascending value. /// If is true, on exit U contains the left /// singular vectors. @@ -1893,7 +1893,7 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Atlas /// Max(3*Min(M, N) + Max(M, N), 5*Min(M,N)). For complex matrices, 2*Min(M, N) + Max(M, N). /// On exit, work[0] contains the optimal work size value. /// This is equivalent to the GESVD LAPACK routine. - public void SingularValueDecomposition(bool computeVectors, Complex[] a, int aRows, int aColumns, Complex[] s, Complex[] u, Complex[] vt, Complex[] work) + public void SingularValueDecomposition(bool computeVectors, Complex[] a, int rowsA, int columnsA, Complex[] s, Complex[] u, Complex[] vt, Complex[] work) { throw new NotImplementedException(); } @@ -1902,15 +1902,15 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Atlas /// Solves A*X=B for X using the singular value decomposition of A. /// /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// The singular values of A in ascending value. /// On exit U contains the left singular vectors. /// On exit VT contains the transposed right singular vectors. /// The B matrix. - /// The number of columns of B. + /// The number of columns of B. /// On exit, the solution matrix. - public void SvdSolve(Complex[] a, int aRows, int aColumns, Complex[] s, Complex[] u, Complex[] vt, Complex[] b, int bColumns, Complex[] x) + public void SvdSolve(Complex[] a, int rowsA, int columnsA, Complex[] s, Complex[] u, Complex[] vt, Complex[] b, int columnsB, Complex[] x) { throw new NotImplementedException(); } @@ -1919,18 +1919,18 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Atlas /// Solves A*X=B for X using the singular value decomposition of A. /// /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// The singular values of A in ascending value. /// On exit U contains the left singular vectors. /// On exit VT contains the transposed right singular vectors. /// The B matrix. - /// The number of columns of B. + /// The number of columns of B. /// On exit, the solution matrix. /// The work array. For real matrices, the work array should be at least /// Max(3*Min(M, N) + Max(M, N), 5*Min(M,N)). For complex matrices, 2*Min(M, N) + Max(M, N). /// On exit, work[0] contains the optimal work size value. - public void SvdSolve(Complex[] a, int aRows, int aColumns, Complex[] s, Complex[] u, Complex[] vt, Complex[] b, int bColumns, Complex[] x, Complex[] work) + public void SvdSolve(Complex[] a, int rowsA, int columnsA, Complex[] s, Complex[] u, Complex[] vt, Complex[] b, int columnsB, Complex[] x, Complex[] work) { throw new NotImplementedException(); } @@ -1938,15 +1938,15 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Atlas /// /// Solves A*X=B for X using a previously SVD decomposed matrix. /// - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// The s values returned by . /// The left singular vectors returned by . /// The right singular vectors returned by . /// The B matrix. - /// The number of columns of B. + /// The number of columns of B. /// On exit, the solution matrix. - public void SvdSolveFactored(int aRows, int aColumns, Complex[] s, Complex[] u, Complex[] vt, Complex[] b, int bColumns, Complex[] x) + public void SvdSolveFactored(int rowsA, int columnsA, Complex[] s, Complex[] u, Complex[] vt, Complex[] b, int columnsB, Complex[] x) { throw new NotImplementedException(); } @@ -2176,17 +2176,17 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Atlas /// Multiples two matrices. result = x * y /// /// The x matrix. - /// The number of rows in the x matrix. - /// The number of columns in the x matrix. + /// The number of rows in the x matrix. + /// The number of columns in the x matrix. /// The y matrix. - /// The number of rows in the y matrix. - /// The number of columns in the y matrix. + /// The number of rows in the y matrix. + /// The number of columns in the y matrix. /// Where to store the result of the multiplication. /// This is a simplified version of the BLAS GEMM routine with alpha /// set to 1.0 and beta set to 0.0, and x and y are not transposed. - public void MatrixMultiply(Complex32[] x, int xRows, int xColumns, Complex32[] y, int yRows, int yColumns, Complex32[] result) + public void MatrixMultiply(Complex32[] x, int rowsX, int columnsX, Complex32[] y, int rowsY, int columnsY, Complex32[] result) { - MatrixMultiplyWithUpdate(Transpose.DontTranspose, Transpose.DontTranspose, Complex32.One, x, xRows, xColumns, y, yRows, yColumns, Complex32.Zero, result); + MatrixMultiplyWithUpdate(Transpose.DontTranspose, Transpose.DontTranspose, Complex32.One, x, rowsX, columnsX, y, rowsY, columnsY, Complex32.Zero, result); } /// @@ -2196,15 +2196,15 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Atlas /// How to transpose the matrix. /// The value to scale matrix. /// The a matrix. - /// The number of rows in the matrix. - /// The number of columns in the matrix. + /// The number of rows in the matrix. + /// The number of columns in the matrix. /// The b matrix - /// The number of rows in the matrix. - /// The number of columns in the matrix. + /// The number of rows in the matrix. + /// The number of columns in the matrix. /// The value to scale the matrix. /// The c matrix. public void MatrixMultiplyWithUpdate(Transpose transposeA, Transpose transposeB, Complex32 alpha, Complex32[] a, - int aRows, int aColumns, Complex32[] b, int bRows, int bColumns, Complex32 beta, Complex32[] c) + int rowsA, int columnsA, Complex32[] b, int rowsB, int columnsB, Complex32 beta, Complex32[] c) { if (a == null) { @@ -2221,16 +2221,16 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Atlas throw new ArgumentNullException("c"); } - var m = transposeA == Transpose.DontTranspose ? aRows : aColumns; - var n = transposeB == Transpose.DontTranspose ? bColumns : bRows; - var k = transposeA == Transpose.DontTranspose ? aColumns : aRows; + var m = transposeA == Transpose.DontTranspose ? rowsA : columnsA; + var n = transposeB == Transpose.DontTranspose ? columnsB : rowsB; + var k = transposeA == Transpose.DontTranspose ? columnsA : rowsA; - if( c.Length != aRows * bColumns) + if( c.Length != rowsA * columnsB) { throw new ArgumentException(Resources.ArgumentMatrixDimensions); } - if (aColumns != bRows) + if (columnsA != rowsB) { throw new ArgumentException(Resources.ArgumentMatrixDimensions); } @@ -2386,12 +2386,12 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Atlas /// Solves A*X=B for X using Cholesky factorization. /// /// The square, positive definite matrix A. - /// The number of rows and columns in A. + /// The number of rows and columns in A. /// The B matrix. - /// The number of rows in the B matrix. - /// The number of columns in the B matrix. + /// The number of rows in the B matrix. + /// The number of columns in the B matrix. /// This is equivalent to the POTRF add POTRS LAPACK routines. - public void CholeskySolve(Complex32[] a, int aOrder, Complex32[] b, int bRows, int bColumns) + public void CholeskySolve(Complex32[] a, int orderA, Complex32[] b, int rowsB, int columnsB) { throw new NotImplementedException(); } @@ -2400,12 +2400,12 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Atlas /// Solves A*X=B for X using a previously factored A matrix. /// /// The square, positive definite matrix A. - /// The number of rows and columns in A. + /// The number of rows and columns in A. /// The B matrix. - /// The number of rows in the B matrix. - /// The number of columns in the B matrix. + /// 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(Complex32[] a, int aOrder, Complex32[] b, int bRows, int bColumns) + public void CholeskySolveFactored(Complex32[] a, int orderA, Complex32[] b, int rowsB, int columnsB) { throw new NotImplementedException(); } @@ -2415,12 +2415,12 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Atlas /// /// On entry, it is the M by N A matrix to factor. On exit, /// it is overwritten with the R matrix of the QR factorization. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// On exit, A M by M matrix that holds the Q matrix of the /// QR factorization. /// This is similar to the GEQRF and ORGQR LAPACK routines. - public void QRFactor(Complex32[] r, int rRows, int rColumns, Complex32[] q) + public void QRFactor(Complex32[] r, int rowsR, int columnsR, Complex32[] q) { throw new NotImplementedException(); } @@ -2430,15 +2430,15 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Atlas /// /// On entry, it is the M by N A matrix to factor. On exit, /// it is overwritten with the R matrix of the QR factorization. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// On exit, A M by M matrix that holds the Q matrix of the /// QR factorization. /// The work array. The array must have a length of at least N, /// but should be N*blocksize. The blocksize is machine dependent. On exit, work[0] contains the optimal /// work size value. /// This is similar to the GEQRF and ORGQR LAPACK routines. - public void QRFactor(Complex32[] r, int rRows, int rColumns, Complex32[] q, Complex32[] work) + public void QRFactor(Complex32[] r, int rowsR, int columnsR, Complex32[] q, Complex32[] work) { throw new NotImplementedException(); } @@ -2448,14 +2448,14 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Atlas /// /// On entry, it is the M by N A matrix to factor. On exit, /// it is overwritten with the R matrix of the QR factorization. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// On exit, A M by M matrix that holds the Q matrix of the /// QR factorization. /// The B matrix. - /// The number of columns of B. + /// The number of columns of B. /// On exit, the solution matrix. - public void QRSolve(Complex32[] r, int rRows, int rColumns, Complex32[] q, Complex32[] b, int bColumns, Complex32[] x) + public void QRSolve(Complex32[] r, int rowsR, int columnsR, Complex32[] q, Complex32[] b, int columnsB, Complex32[] x) { throw new NotImplementedException(); } @@ -2465,17 +2465,17 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Atlas /// /// On entry, it is the M by N A matrix to factor. On exit, /// it is overwritten with the R matrix of the QR factorization. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// On exit, A M by M matrix that holds the Q matrix of the /// QR factorization. /// The B matrix. - /// The number of columns of B. + /// The number of columns of B. /// On exit, the solution matrix. /// The work array. The array must have a length of at least N, /// but should be N*blocksize. The blocksize is machine dependent. On exit, work[0] contains the optimal /// work size value. - public void QRSolve(Complex32[] r, int rRows, int rColumns, Complex32[] q, Complex32[] b, int bColumns, Complex32[] x, Complex32[] work) + public void QRSolve(Complex32[] r, int rowsR, int columnsR, Complex32[] q, Complex32[] b, int columnsB, Complex32[] x, Complex32[] work) { throw new NotImplementedException(); } @@ -2485,12 +2485,12 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Atlas /// /// The Q matrix obtained by calling . /// The R matrix obtained by calling . - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// The B matrix. - /// The number of columns of B. + /// The number of columns of B. /// On exit, the solution matrix. - public void QRSolveFactored(Complex32[] q, Complex32[] r, int rRows, int rColumns, Complex32[] b, int bColumns, Complex32[] x) + public void QRSolveFactored(Complex32[] q, Complex32[] r, int rowsR, int columnsR, Complex32[] b, int columnsB, Complex32[] x) { throw new NotImplementedException(); } @@ -2500,15 +2500,15 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Atlas /// /// Compute the singular U and VT vectors or not. /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// The singular values of A in ascending value. /// If is true, on exit U contains the left /// singular vectors. /// If is true, on exit VT contains the transposed /// right singular vectors. /// This is equivalent to the GESVD LAPACK routine. - public void SingularValueDecomposition(bool computeVectors, Complex32[] a, int aRows, int aColumns, Complex32[] s, Complex32[] u, Complex32[] vt) + public void SingularValueDecomposition(bool computeVectors, Complex32[] a, int rowsA, int columnsA, Complex32[] s, Complex32[] u, Complex32[] vt) { throw new NotImplementedException(); } @@ -2518,8 +2518,8 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Atlas /// /// Compute the singular U and VT vectors or not. /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// The singular values of A in ascending value. /// If is true, on exit U contains the left /// singular vectors. @@ -2529,7 +2529,7 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Atlas /// Max(3*Min(M, N) + Max(M, N), 5*Min(M,N)). For complex matrices, 2*Min(M, N) + Max(M, N). /// On exit, work[0] contains the optimal work size value. /// This is equivalent to the GESVD LAPACK routine. - public void SingularValueDecomposition(bool computeVectors, Complex32[] a, int aRows, int aColumns, Complex32[] s, Complex32[] u, Complex32[] vt, Complex32[] work) + public void SingularValueDecomposition(bool computeVectors, Complex32[] a, int rowsA, int columnsA, Complex32[] s, Complex32[] u, Complex32[] vt, Complex32[] work) { throw new NotImplementedException(); } @@ -2538,15 +2538,15 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Atlas /// Solves A*X=B for X using the singular value decomposition of A. /// /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// The singular values of A in ascending value. /// On exit U contains the left singular vectors. /// On exit VT contains the transposed right singular vectors. /// The B matrix. - /// The number of columns of B. + /// The number of columns of B. /// On exit, the solution matrix. - public void SvdSolve(Complex32[] a, int aRows, int aColumns, Complex32[] s, Complex32[] u, Complex32[] vt, Complex32[] b, int bColumns, Complex32[] x) + public void SvdSolve(Complex32[] a, int rowsA, int columnsA, Complex32[] s, Complex32[] u, Complex32[] vt, Complex32[] b, int columnsB, Complex32[] x) { throw new NotImplementedException(); } @@ -2555,18 +2555,18 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Atlas /// Solves A*X=B for X using the singular value decomposition of A. /// /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// The singular values of A in ascending value. /// On exit U contains the left singular vectors. /// On exit VT contains the transposed right singular vectors. /// The B matrix. - /// The number of columns of B. + /// The number of columns of B. /// On exit, the solution matrix. /// The work array. For real matrices, the work array should be at least /// Max(3*Min(M, N) + Max(M, N), 5*Min(M,N)). For complex matrices, 2*Min(M, N) + Max(M, N). /// On exit, work[0] contains the optimal work size value. - public void SvdSolve(Complex32[] a, int aRows, int aColumns, Complex32[] s, Complex32[] u, Complex32[] vt, Complex32[] b, int bColumns, Complex32[] x, Complex32[] work) + public void SvdSolve(Complex32[] a, int rowsA, int columnsA, Complex32[] s, Complex32[] u, Complex32[] vt, Complex32[] b, int columnsB, Complex32[] x, Complex32[] work) { throw new NotImplementedException(); } @@ -2574,15 +2574,15 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Atlas /// /// Solves A*X=B for X using a previously SVD decomposed matrix. /// - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// The s values returned by . /// The left singular vectors returned by . /// The right singular vectors returned by . /// The B matrix. - /// The number of columns of B. + /// The number of columns of B. /// On exit, the solution matrix. - public void SvdSolveFactored(int aRows, int aColumns, Complex32[] s, Complex32[] u, Complex32[] vt, Complex32[] b, int bColumns, Complex32[] x) + public void SvdSolveFactored(int rowsA, int columnsA, Complex32[] s, Complex32[] u, Complex32[] vt, Complex32[] b, int columnsB, Complex32[] x) { throw new NotImplementedException(); } diff --git a/src/Numerics/Algorithms/LinearAlgebra/ILinearAlgebraProviderOfT.cs b/src/Numerics/Algorithms/LinearAlgebra/ILinearAlgebraProviderOfT.cs index bf1a7532..e96d496e 100644 --- a/src/Numerics/Algorithms/LinearAlgebra/ILinearAlgebraProviderOfT.cs +++ b/src/Numerics/Algorithms/LinearAlgebra/ILinearAlgebraProviderOfT.cs @@ -185,15 +185,15 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra /// Multiples two matrices. result = x * y /// /// The x matrix. - /// The number of rows in the x matrix. - /// The number of columns in the x matrix. + /// The number of rows in the x matrix. + /// The number of columns in the x matrix. /// The y matrix. - /// The number of rows in the y matrix. - /// The number of columns in the y matrix. + /// The number of rows in the y matrix. + /// The number of columns in the y matrix. /// Where to store the result of the multiplication. /// This is a simplified version of the BLAS GEMM routine with alpha /// set to 1.0 and beta set to 0.0, and x and y are not transposed. - void MatrixMultiply(T[] x, int xRows, int xColumns, T[] y, int yRows, int yColumns, T[] result); + void MatrixMultiply(T[] x, int rowsX, int columnsX, T[] y, int rowsY, int columnsY, T[] result); /// /// Multiplies two matrices and updates another with the result. c = alpha*op(a)*op(b) + beta*c @@ -202,14 +202,14 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra /// How to transpose the matrix. /// The value to scale matrix. /// The a matrix. - /// The number of rows in the matrix. - /// The number of columns in the matrix. + /// The number of rows in the matrix. + /// The number of columns in the matrix. /// The b matrix - /// The number of rows in the matrix. - /// The number of columns in the matrix. + /// The number of rows in the matrix. + /// The number of columns in the matrix. /// The value to scale the matrix. /// The c matrix. - void MatrixMultiplyWithUpdate(Transpose transposeA, Transpose transposeB, T alpha, T[] a, int aRows, int aColumns, T[] b, int bRows, int bColumns, T beta, T[] c); + void MatrixMultiplyWithUpdate(Transpose transposeA, Transpose transposeB, T alpha, T[] a, int rowsA, int columnsA, T[] b, int rowsB, int columnsB, T beta, T[] c); /// /// Computes the LUP factorization of A. P*A = L*U. @@ -319,116 +319,116 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra /// Solves A*X=B for X using Cholesky factorization. /// /// The square, positive definite matrix A. - /// The number of rows and columns in A. + /// The number of rows and columns in A. /// The B matrix. - /// The number of rows in the B matrix. - /// The number of columns in the B matrix. + /// The number of rows in the B matrix. + /// The number of columns in the B matrix. /// This is equivalent to the POTRF add POTRS LAPACK routines. - void CholeskySolve(T[] a, int aOrder, T[] b, int bRows, int bColumns); + void CholeskySolve(T[] a, int orderA, T[] b, int rowsB, int columnsB); /// /// Solves A*X=B for X using a previously factored A matrix. /// /// The square, positive definite matrix A. - /// The number of rows and columns in A. + /// The number of rows and columns in A. /// The B matrix. - /// The number of rows in the B matrix. - /// The number of columns in the B matrix. + /// The number of rows in the B matrix. + /// The number of columns in the B matrix. /// This is equivalent to the POTRS LAPACK routine. - void CholeskySolveFactored(T[] a, int aOrder, T[] b, int bRows, int bColumns); + void CholeskySolveFactored(T[] a, int orderA, T[] b, int rowsB, int columnsB); /// /// Computes the QR factorization of A. /// /// On entry, it is the M by N A matrix to factor. On exit, /// it is overwritten with the R matrix of the QR factorization. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// On exit, A M by M matrix that holds the Q matrix of the /// QR factorization. /// This is similar to the GEQRF and ORGQR LAPACK routines. - void QRFactor(T[] r, int rRows, int rColumns, T[] q); + void QRFactor(T[] r, int rowsR, int columnsR, T[] q); /// /// Computes the QR factorization of A. /// /// On entry, it is the M by N A matrix to factor. On exit, /// it is overwritten with the R matrix of the QR factorization. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// On exit, A M by M matrix that holds the Q matrix of the /// QR factorization. /// The work array. The array must have a length of at least N, /// but should be N*blocksize. The blocksize is machine dependent. On exit, work[0] contains the optimal /// work size value. /// This is similar to the GEQRF and ORGQR LAPACK routines. - void QRFactor(T[] r, int rRows, int rColumns, T[] q, T[] work); + void QRFactor(T[] r, int rowsR, int columnsR, T[] q, T[] work); /// /// Solves A*X=B for X using QR factorization of A. /// /// On entry, it is the M by N A matrix to factor. On exit, /// it is overwritten with the R matrix of the QR factorization. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// On exit, A M by M matrix that holds the Q matrix of the /// QR factorization. /// The B matrix. - /// The number of columns of B. + /// The number of columns of B. /// On exit, the solution matrix. - void QRSolve(T[] r, int rRows, int rColumns, T[] q, T[] b, int bColumns, T[] x); + void QRSolve(T[] r, int rowsR, int columnsR, T[] q, T[] b, int columnsB, T[] x); /// /// Solves A*X=B for X using QR factorization of A. /// /// On entry, it is the M by N A matrix to factor. On exit, /// it is overwritten with the R matrix of the QR factorization. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// On exit, A M by M matrix that holds the Q matrix of the /// QR factorization. /// The B matrix. - /// The number of columns of B. + /// The number of columns of B. /// On exit, the solution matrix. /// The work array. The array must have a length of at least N, /// but should be N*blocksize. The blocksize is machine dependent. On exit, work[0] contains the optimal /// work size value. - void QRSolve(T[] r, int rRows, int rColumns, T[] q, T[] b, int bColumns, T[] x, T[] work); + void QRSolve(T[] r, int rowsR, int columnsR, T[] q, T[] b, int columnsB, T[] x, T[] work); /// /// Solves A*X=B for X using a previously QR factored matrix. /// /// The Q matrix obtained by calling . /// The R matrix obtained by calling . - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// The B matrix. - /// The number of columns of B. + /// The number of columns of B. /// On exit, the solution matrix. - void QRSolveFactored(T[] q, T[] r, int rRows, int rColumns, T[] b, int bColumns, T[] x); + void QRSolveFactored(T[] q, T[] r, int rowsR, int columnsR, T[] b, int columnsB, T[] x); /// /// Computes the singular value decomposition of A. /// /// Compute the singular U and VT vectors or not. /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// The singular values of A in ascending value. /// If is true, on exit U contains the left /// singular vectors. /// If is true, on exit VT contains the transposed /// right singular vectors. /// This is equivalent to the GESVD LAPACK routine. - void SingularValueDecomposition(bool computeVectors, T[] a, int aRows, int aColumns, T[] s, T[] u, T[] vt); + void SingularValueDecomposition(bool computeVectors, T[] a, int rowsA, int columnsA, T[] s, T[] u, T[] vt); /// /// Computes the singular value decomposition of A. /// /// Compute the singular U and VT vectors or not. /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// The singular values of A in ascending value. /// If is true, on exit U contains the left /// singular vectors. @@ -439,51 +439,51 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra /// On exit, work[0] contains the optimal work size value. /// /// This is equivalent to the GESVD LAPACK routine. - void SingularValueDecomposition(bool computeVectors, T[] a, int aRows, int aColumns, T[] s, T[] u, T[] vt, T[] work); + void SingularValueDecomposition(bool computeVectors, T[] a, int rowsA, int columnsA, T[] s, T[] u, T[] vt, T[] work); /// /// Solves A*X=B for X using the singular value decomposition of A. /// /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// The singular values of A in ascending value. /// On exit U contains the left singular vectors. /// On exit VT contains the transposed right singular vectors. /// The B matrix. - /// The number of columns of B. + /// The number of columns of B. /// On exit, the solution matrix. - void SvdSolve(T[] a, int aRows, int aColumns, T[] s, T[] u, T[] vt, T[] b, int bColumns, T[] x); + void SvdSolve(T[] a, int rowsA, int columnsA, T[] s, T[] u, T[] vt, T[] b, int columnsB, T[] x); /// /// Solves A*X=B for X using the singular value decomposition of A. /// /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// The singular values of A in ascending value. /// On exit U contains the left singular vectors. /// On exit VT contains the transposed right singular vectors. /// The B matrix. - /// The number of columns of B. + /// The number of columns of B. /// On exit, the solution matrix. /// The work array. For real matrices, the work array should be at least /// Max(3*Min(M, N) + Max(M, N), 5*Min(M,N)). For complex matrices, 2*Min(M, N) + Max(M, N). /// On exit, work[0] contains the optimal work size value. /// - void SvdSolve(T[] a, int aRows, int aColumns, T[] s, T[] u, T[] vt, T[] b, int bColumns, T[] x, T[] work); + void SvdSolve(T[] a, int rowsA, int columnsA, T[] s, T[] u, T[] vt, T[] b, int columnsB, T[] x, T[] work); /// /// Solves A*X=B for X using a previously SVD decomposed matrix. /// - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// The s values returned by . /// The left singular vectors returned by . /// The right singular vectors returned by . /// The B matrix. - /// The number of columns of B. + /// The number of columns of B. /// On exit, the solution matrix. - void SvdSolveFactored(int aRows, int aColumns, T[] s, T[] u, T[] vt, T[] b, int bColumns, T[] x); + void SvdSolveFactored(int rowsA, int columnsA, T[] s, T[] u, T[] vt, T[] b, int columnsB, T[] x); } } diff --git a/src/Numerics/Algorithms/LinearAlgebra/ManagedLinearAlgebraProvider.cs b/src/Numerics/Algorithms/LinearAlgebra/ManagedLinearAlgebraProvider.cs index ac071b4d..43290034 100644 --- a/src/Numerics/Algorithms/LinearAlgebra/ManagedLinearAlgebraProvider.cs +++ b/src/Numerics/Algorithms/LinearAlgebra/ManagedLinearAlgebraProvider.cs @@ -26,7 +26,6 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra { using System; - using System.Linq; using System.Numerics; using Properties; using Threading; @@ -278,15 +277,15 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra /// Multiples two matrices. result = x * y /// /// The x matrix. - /// The number of rows in the x matrix. - /// The number of columns in the x matrix. + /// The number of rows in the x matrix. + /// The number of columns in the x matrix. /// The y matrix. - /// The number of rows in the y matrix. - /// The number of columns in the y matrix. + /// The number of rows in the y matrix. + /// The number of columns in the y matrix. /// Where to store the result of the multiplication. /// This is a simplified version of the BLAS GEMM routine with alpha /// set to 1.0 and beta set to 0.0, and x and y are not transposed. - public void MatrixMultiply(double[] x, int xRows, int xColumns, double[] y, int yRows, int yColumns, double[] result) + public void MatrixMultiply(double[] x, int rowsX, int columnsX, double[] y, int rowsY, int columnsY, double[] result) { // First check some basic requirement on the parameters of the matrix multiplication. if (x == null) @@ -304,22 +303,22 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra throw new ArgumentNullException("result"); } - if (xRows * xColumns != x.Length) + if (rowsX * columnsX != x.Length) { throw new ArgumentException("x.Length != xRows * xColumns"); } - if (yRows * yColumns != y.Length) + if (rowsY * columnsY != y.Length) { throw new ArgumentException("y.Length != yRows * yColumns"); } - if (xColumns != yRows) + if (columnsX != rowsY) { throw new ArgumentException("xColumns != yRows"); } - if (xRows * yColumns != result.Length) + if (rowsX * columnsY != result.Length) { throw new ArgumentException("xRows * yColumns != result.Length"); } @@ -351,7 +350,7 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra // TODO - For small matrices we should get rid of the parallelism because of startup costs. // Perhaps the following implementations would be a good one // http://blog.feradz.com/2009/01/cache-efficient-matrix-multiplication/ - MatrixMultiplyWithUpdate(Transpose.DontTranspose, Transpose.DontTranspose, 1.0, xdata, xRows, xColumns, ydata, yRows, yColumns, 0.0, result); + MatrixMultiplyWithUpdate(Transpose.DontTranspose, Transpose.DontTranspose, 1.0, xdata, rowsX, columnsX, ydata, rowsY, columnsY, 0.0, result); } /// @@ -361,18 +360,18 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra /// How to transpose the matrix. /// The value to scale matrix. /// The a matrix. - /// The number of rows in the matrix. - /// The number of columns in the matrix. + /// The number of rows in the matrix. + /// The number of columns in the matrix. /// The b matrix - /// The number of rows in the matrix. - /// The number of columns in the matrix. + /// The number of rows in the matrix. + /// The number of columns in the matrix. /// The value to scale the matrix. /// The c matrix. - public void MatrixMultiplyWithUpdate(Transpose transposeA, Transpose transposeB, double alpha, double[] a, int aRows, int aColumns, double[] b, int bRows, int bColumns, double beta, double[] c) + public void MatrixMultiplyWithUpdate(Transpose transposeA, Transpose transposeB, double alpha, double[] a, int rowsA, int columnsA, double[] b, int rowsB, int columnsB, double beta, double[] c) { // Choose nonsensical values for the number of rows in c; fill them in depending // on the operations on a and b. - int cRows; + int rowsC; // First check some basic requirement on the parameters of the matrix multiplication. if (a == null) @@ -387,59 +386,59 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra if ((int)transposeA > 111 && (int)transposeB > 111) { - if (aRows != bColumns) + if (rowsA != columnsB) { throw new ArgumentOutOfRangeException(); } - if (aColumns * bRows != c.Length) + if (columnsA * rowsB != c.Length) { throw new ArgumentOutOfRangeException(); } - cRows = aColumns; + rowsC = columnsA; } else if ((int)transposeA > 111) { - if (aRows != bRows) + if (rowsA != rowsB) { throw new ArgumentOutOfRangeException(); } - if (aColumns * bColumns != c.Length) + if (columnsA * columnsB != c.Length) { throw new ArgumentOutOfRangeException(); } - cRows = aColumns; + rowsC = columnsA; } else if ((int)transposeB > 111) { - if (aColumns != bColumns) + if (columnsA != columnsB) { throw new ArgumentOutOfRangeException(); } - if (aRows * bRows != c.Length) + if (rowsA * rowsB != c.Length) { throw new ArgumentOutOfRangeException(); } - cRows = aRows; + rowsC = rowsA; } else { - if (aColumns != bRows) + if (columnsA != rowsB) { throw new ArgumentOutOfRangeException(); } - if (aRows * bColumns != c.Length) + if (rowsA * columnsB != c.Length) { throw new ArgumentOutOfRangeException(); } - cRows = aRows; + rowsC = rowsA; } if (alpha == 0.0 && beta == 0.0) @@ -477,35 +476,39 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra { if ((int)transposeA > 111 && (int)transposeB > 111) { - CommonParallel.For(0, aColumns, - j => - { - var jIndex = j * cRows; - for (var i = 0; i != bRows; i++) - { - var iIndex = i * aRows; - double s = 0; - for (var l = 0; l != bColumns; l++) - { - s += adata[iIndex + l] * bdata[l * bRows + j]; - } - - c[jIndex + i] = s; - } - }); + CommonParallel.For( + 0, + columnsA, + j => + { + var jIndex = j * rowsC; + for (var i = 0; i != rowsB; i++) + { + var iIndex = i * rowsA; + double s = 0; + for (var l = 0; l != columnsB; l++) + { + s += adata[iIndex + l] * bdata[(l * rowsB) + j]; + } + + c[jIndex + i] = s; + } + }); } else if ((int)transposeA > 111) { - CommonParallel.For(0, bColumns, + CommonParallel.For( + 0, + columnsB, j => { - var jcIndex = j * cRows; - var jbIndex = j * bRows; - for (var i = 0; i != aColumns; i++) + var jcIndex = j * rowsC; + var jbIndex = j * rowsB; + for (var i = 0; i != columnsA; i++) { - var iIndex = i * aRows; + var iIndex = i * rowsA; double s = 0; - for (var l = 0; l != aRows; l++) + for (var l = 0; l != rowsA; l++) { s += adata[iIndex + l] * bdata[jbIndex + l]; } @@ -516,119 +519,131 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra } else if ((int)transposeB > 111) { - CommonParallel.For(0, bRows, - j => - { - var jIndex = j * cRows; - for (var i = 0; i != aRows; i++) - { - double s = 0; - for (var l = 0; l != aColumns; l++) - { - s += adata[l * aRows + i] * bdata[l * bRows + j]; - } + CommonParallel.For( + 0, + rowsB, + j => + { + var jIndex = j * rowsC; + for (var i = 0; i != rowsA; i++) + { + double s = 0; + for (var l = 0; l != columnsA; l++) + { + s += adata[(l * rowsA) + i] * bdata[(l * rowsB) + j]; + } - c[jIndex + i] = s; - } - }); + c[jIndex + i] = s; + } + }); } else { - CommonParallel.For(0, bColumns, - j => - { - var jcIndex = j * cRows; - var jbIndex = j * bRows; - for (var i = 0; i != aRows; i++) - { - double s = 0; - for (var l = 0; l != aColumns; l++) - { - s += adata[l * aRows + i] * bdata[jbIndex + l]; - } + CommonParallel.For( + 0, + columnsB, + j => + { + var jcIndex = j * rowsC; + var jbIndex = j * rowsB; + for (var i = 0; i != rowsA; i++) + { + double s = 0; + for (var l = 0; l != columnsA; l++) + { + s += adata[(l * rowsA) + i] * bdata[jbIndex + l]; + } - c[jcIndex + i] = s; - } - }); + c[jcIndex + i] = s; + } + }); } } else { if ((int)transposeA > 111 && (int)transposeB > 111) { - CommonParallel.For(0, aColumns, - j => - { - var jIndex = j * cRows; - for (var i = 0; i != bRows; i++) - { - var iIndex = i * aRows; - double s = 0; - for (var l = 0; l != bColumns; l++) - { - s += adata[iIndex + l] * bdata[l * bRows + j]; - } - - c[jIndex + i] = c[jIndex + i] * beta + s; - } - }); + CommonParallel.For( + 0, + columnsA, + j => + { + var jIndex = j * rowsC; + for (var i = 0; i != rowsB; i++) + { + var iIndex = i * rowsA; + double s = 0; + for (var l = 0; l != columnsB; l++) + { + s += adata[iIndex + l] * bdata[(l * rowsB) + j]; + } + + c[jIndex + i] = (c[jIndex + i] * beta) + s; + } + }); } else if ((int)transposeA > 111) { - CommonParallel.For(0, bColumns, - j => - { - var jcIndex = j * cRows; - var jbIndex = j * bRows; - for (var i = 0; i != aColumns; i++) - { - var iIndex = i * aRows; - double s = 0; - for (var l = 0; l != aRows; l++) - { - s += adata[iIndex + l] * bdata[jbIndex + l]; - } - - c[jcIndex + i] = s + c[jcIndex + i] * beta; - } - }); + CommonParallel.For( + 0, + columnsB, + j => + { + var jcIndex = j * rowsC; + var jbIndex = j * rowsB; + for (var i = 0; i != columnsA; i++) + { + var iIndex = i * rowsA; + double s = 0; + for (var l = 0; l != rowsA; l++) + { + s += adata[iIndex + l] * bdata[jbIndex + l]; + } + + c[jcIndex + i] = s + (c[jcIndex + i] * beta); + } + }); } else if ((int)transposeB > 111) { - CommonParallel.For(0, bRows, - j => - { - var jIndex = j * cRows; - for (var i = 0; i != aRows; i++) - { - double s = 0; - for (var l = 0; l != aColumns; l++) - { - s += adata[l * aRows + i] * bdata[l * bRows + j]; - } + CommonParallel.For( + 0, + rowsB, + j => + { + var jIndex = j * rowsC; + for (var i = 0; i != rowsA; i++) + { + double s = 0; + for (var l = 0; l != columnsA; l++) + { + s += adata[(l * rowsA) + i] * bdata[(l * rowsB) + j]; + } - c[jIndex + i] = s + c[jIndex + i] * beta; - } - }); + c[jIndex + i] = s + (c[jIndex + i] * beta); + } + }); } else { - CommonParallel.For(0, bColumns, - j => - { - var jcIndex = j * cRows; - var jbIndex = j * bRows; - for (var i = 0; i != aRows; i++) - { - double s = 0; - for (var l = 0; l != aColumns; l++) - { - s += adata[l * aRows + i] * bdata[jbIndex + l]; - } + CommonParallel.For( + 0, + columnsB, + j => + { + var jcIndex = j * rowsC; + var jbIndex = j * rowsB; + for (var i = 0; i != rowsA; i++) + { + double s = 0; + for (var l = 0; l != columnsA; l++) + { + s += adata[(l * rowsA) + i] * bdata[jbIndex + l]; + } - c[jcIndex + i] = s + c[jcIndex + i] * beta; - } - }); + c[jcIndex + i] = s + (c[jcIndex + i] * beta); + } + }); } } } @@ -636,79 +651,87 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra { if ((int)transposeA > 111 && (int)transposeB > 111) { - CommonParallel.For(0, aColumns, - j => - { - var jIndex = j * cRows; - for (var i = 0; i != bRows; i++) - { - var iIndex = i * aRows; - double s = 0; - for (var l = 0; l != bColumns; l++) - { - s += adata[iIndex + l] * bdata[l * bRows + j]; - } + CommonParallel.For( + 0, + columnsA, + j => + { + var jIndex = j * rowsC; + for (var i = 0; i != rowsB; i++) + { + var iIndex = i * rowsA; + double s = 0; + for (var l = 0; l != columnsB; l++) + { + s += adata[iIndex + l] * bdata[(l * rowsB) + j]; + } - c[jIndex + i] = c[jIndex + i] * beta + alpha * s; - } - }); + c[jIndex + i] = (c[jIndex + i] * beta) + (alpha * s); + } + }); } else if ((int)transposeA > 111) { - CommonParallel.For(0, bColumns, - j => - { - var jcIndex = j * cRows; - var jbIndex = j * bRows; - for (var i = 0; i != aColumns; i++) - { - var iIndex = i * aRows; - double s = 0; - for (var l = 0; l != aRows; l++) - { - s += adata[iIndex + l] * bdata[jbIndex + l]; - } + CommonParallel.For( + 0, + columnsB, + j => + { + var jcIndex = j * rowsC; + var jbIndex = j * rowsB; + for (var i = 0; i != columnsA; i++) + { + var iIndex = i * rowsA; + double s = 0; + for (var l = 0; l != rowsA; l++) + { + s += adata[iIndex + l] * bdata[jbIndex + l]; + } - c[jcIndex + i] = alpha * s + c[jcIndex + i] * beta; - } - }); + c[jcIndex + i] = (alpha * s) + (c[jcIndex + i] * beta); + } + }); } else if ((int)transposeB > 111) { - CommonParallel.For(0, bRows, - j => - { - var jIndex = j * cRows; - for (var i = 0; i != aRows; i++) - { - double s = 0; - for (var l = 0; l != aColumns; l++) - { - s += adata[l * aRows + i] * bdata[l * bRows + j]; - } + CommonParallel.For( + 0, + rowsB, + j => + { + var jIndex = j * rowsC; + for (var i = 0; i != rowsA; i++) + { + double s = 0; + for (var l = 0; l != columnsA; l++) + { + s += adata[(l * rowsA) + i] * bdata[(l * rowsB) + j]; + } - c[jIndex + i] = alpha * s + c[jIndex + i] * beta; - } - }); + c[jIndex + i] = (alpha * s) + (c[jIndex + i] * beta); + } + }); } else { - CommonParallel.For(0, bColumns, - j => - { - var jcIndex = j * cRows; - var jbIndex = j * bRows; - for (var i = 0; i != aRows; i++) - { - double s = 0; - for (var l = 0; l != aColumns; l++) - { - s += adata[l * aRows + i] * bdata[jbIndex + l]; - } + CommonParallel.For( + 0, + columnsB, + j => + { + var jcIndex = j * rowsC; + var jbIndex = j * rowsB; + for (var i = 0; i != rowsA; i++) + { + double s = 0; + for (var l = 0; l != columnsA; l++) + { + s += adata[(l * rowsA) + i] * bdata[jbIndex + l]; + } - c[jcIndex + i] = alpha * s + c[jcIndex + i] * beta; - } - }); + c[jcIndex + i] = (alpha * s) + (c[jcIndex + i] * beta); + } + }); } } } @@ -750,7 +773,7 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra ipiv[i] = i; } - var vLUcolj = new double[order]; + var vecLUcolj = new double[order]; // Outer loop. for (var j = 0; j < order; j++) @@ -761,7 +784,7 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra // Make a copy of the j-th column to localize references. for (var i = 0; i < order; i++) { - vLUcolj[i] = data[indexj + i]; + vecLUcolj[i] = data[indexj + i]; } // Apply previous transformations. @@ -772,17 +795,17 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra var s = 0.0; for (var k = 0; k < kmax; k++) { - s += data[k * order + i] * vLUcolj[k]; + s += data[(k * order) + i] * vecLUcolj[k]; } - data[indexj + i] = vLUcolj[i] -= s; + data[indexj + i] = vecLUcolj[i] -= s; } // Find pivot and exchange if necessary. var p = j; for (var i = j + 1; i < order; i++) { - if (Math.Abs(vLUcolj[i]) > Math.Abs(vLUcolj[p])) + if (Math.Abs(vecLUcolj[i]) > Math.Abs(vecLUcolj[p])) { p = i; } @@ -869,7 +892,7 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra var inverse = new double[a.Length]; for (var i = 0; i < order; i++) { - inverse[i + order * i] = 1.0; + inverse[i + (order * i)] = 1.0; } LUSolveFactored(order, a, order, ipiv, inverse); @@ -992,7 +1015,7 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra var p = ipiv[i]; for (var j = 0; j < columnsOfB; j++) { - var indexk = j*order; + var indexk = j * order; var indexkp = indexk + p; var indexkj = indexk + i; var temp = b[indexkp]; @@ -1018,10 +1041,10 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra // Solve U*X = Y; for (var k = order - 1; k >= 0; k--) { - var korder = k + k * order; + var korder = k + (k * order); for (var j = 0; j < columnsOfB; j++) { - b[k + j * order] /= a[korder]; + b[k + (j * order)] /= a[korder]; } korder = k * order; @@ -1124,6 +1147,7 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra aT[(j * order) + i] = a[(i * order) + j]; } } + LUSolveFactored(columnsOfB, aT, order, ipiv, b); } else @@ -1156,7 +1180,7 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra int i; for (i = 0; i < k; i++) { - s += a[i * order + k] * a[i * order + j]; + s += a[(i * order) + k] * a[(i * order) + j]; } var tmp = k * order; @@ -1165,7 +1189,7 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra d += s * s; } - index = j * order + j; + index = (j * order) + j; d = a[index] - d; if (d <= 0.0) { @@ -1175,7 +1199,7 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra a[index] = Math.Sqrt(d); for (var k = j + 1; k < order; k++) { - a[k * order + j] = 0.0; + a[(k * order) + j] = 0.0; } } } @@ -1184,12 +1208,12 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra /// Solves A*X=B for X using Cholesky factorization. /// /// The square, positive definite matrix A. - /// The number of rows and columns in A. + /// The number of rows and columns in A. /// The B matrix. - /// The number of rows in the B matrix. - /// The number of columns in the B matrix. + /// The number of rows in the B matrix. + /// The number of columns in the B matrix. /// This is equivalent to the POTRF add POTRS LAPACK routines. - public void CholeskySolve(double[] a, int aOrder, double[] b, int bRows, int bColumns) + public void CholeskySolve(double[] a, int orderA, double[] b, int rowsB, int columnsB) { if (a == null) { @@ -1201,7 +1225,7 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra throw new ArgumentNullException("b"); } - if (aOrder != bRows) + if (orderA != rowsB) { throw new ArgumentException(Resources.ArgumentMatrixDimensions); } @@ -1211,20 +1235,20 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra throw new ArgumentException(Resources.ArgumentReferenceDifferent); } - CholeskyFactor(a, aOrder); - CholeskySolveFactored(a, aOrder, b, bRows, bColumns); + CholeskyFactor(a, orderA); + CholeskySolveFactored(a, orderA, b, rowsB, columnsB); } /// /// Solves A*X=B for X using a previously factored A matrix. /// /// The square, positive definite matrix A. Has to be different than . - /// The number of rows and columns in A. + /// The number of rows and columns in A. /// The B matrix. Has to be different than . - /// The number of rows in the B matrix. - /// The number of columns in the B matrix. + /// 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) + public void CholeskySolveFactored(double[] a, int orderA, double[] b, int rowsB, int columnsB) { if (a == null) { @@ -1236,7 +1260,7 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra throw new ArgumentNullException("b"); } - if (aOrder != bRows) + if (orderA != rowsB) { throw new ArgumentException(Resources.ArgumentMatrixDimensions); } @@ -1246,36 +1270,39 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra throw new ArgumentException(Resources.ArgumentReferenceDifferent); } - CommonParallel.For(0, bColumns, c => - { - var cindex = c * aOrder; + CommonParallel.For( + 0, + columnsB, + c => + { + var cindex = c * orderA; - // Solve L*Y = B; - double sum; - for (var i = 0; i < aOrder; i++) - { - sum = b[cindex + i]; - for (var k = i - 1; k >= 0; k--) - { - sum -= a[k * aOrder + i] * b[cindex + k]; - } + // Solve L*Y = B; + double sum; + for (var i = 0; i < orderA; i++) + { + sum = b[cindex + i]; + for (var k = i - 1; k >= 0; k--) + { + sum -= a[(k * orderA) + i] * b[cindex + k]; + } - b[cindex + i] = sum / a[i * aOrder + i]; - } + b[cindex + i] = sum / a[(i * orderA) + i]; + } - // Solve L'*X = Y; - for (var i = aOrder - 1; i >= 0; i--) - { - sum = b[cindex + i]; - var iindex = i * aOrder; - for (var k = i + 1; k < aOrder; k++) - { - sum -= a[iindex + k] * b[cindex + k]; - } + // Solve L'*X = Y; + for (var i = orderA - 1; i >= 0; i--) + { + sum = b[cindex + i]; + var iindex = i * orderA; + for (var k = i + 1; k < orderA; k++) + { + sum -= a[iindex + k] * b[cindex + k]; + } - b[cindex + i] = sum / a[iindex + i]; - } - }); + b[cindex + i] = sum / a[iindex + i]; + } + }); } /// @@ -1283,12 +1310,12 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra /// /// On entry, it is the M by N A matrix to factor. On exit, /// it is overwritten with the R matrix of the QR factorization. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// On exit, A M by M matrix that holds the Q matrix of the /// QR factorization. /// This is similar to the GEQRF and ORGQR LAPACK routines. - public void QRFactor(double[] r, int rRows, int rColumns, double[] q) + public void QRFactor(double[] r, int rowsR, int columnsR, double[] q) { if (r == null) { @@ -1300,18 +1327,18 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra throw new ArgumentNullException("q"); } - if (r.Length != rRows * rColumns) + if (r.Length != rowsR * columnsR) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "r"); } - if (q.Length != rRows * rRows) + if (q.Length != rowsR * rowsR) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "q"); } - var work = new double[rRows * rRows]; - QRFactor(r, rRows, rColumns, q, work); + var work = new double[rowsR * rowsR]; + QRFactor(r, rowsR, columnsR, q, work); } /// @@ -1319,15 +1346,15 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra /// /// On entry, it is the M by N A matrix to factor. On exit, /// it is overwritten with the R matrix of the QR factorization. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// On exit, A M by M matrix that holds the Q matrix of the /// QR factorization. /// The work array. The array must have a length of at least N, /// but should be N*blocksize. The blocksize is machine dependent. On exit, work[0] contains the optimal /// work size value. /// This is similar to the GEQRF and ORGQR LAPACK routines. - public void QRFactor(double[] r, int rRows, int rColumns, double[] q, double[] work) + public void QRFactor(double[] r, int rowsR, int columnsR, double[] q, double[] work) { if (r == null) { @@ -1344,37 +1371,37 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra throw new ArgumentNullException("q"); } - if (r.Length != rRows * rColumns) + if (r.Length != rowsR * columnsR) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "r"); } - if (q.Length != rRows * rRows) + if (q.Length != rowsR * rowsR) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "q"); } - if (work.Length < rRows * rRows) + if (work.Length < rowsR * rowsR) { - work[0] = rRows * rRows; + work[0] = rowsR * rowsR; throw new ArgumentException(Resources.WorkArrayTooSmall, "work"); } - CommonParallel.For(0, rRows, i => q[(i * rRows) + i] = 1.0); + CommonParallel.For(0, rowsR, i => q[(i * rowsR) + i] = 1.0); - var minmn = Math.Min(rRows, rColumns); + var minmn = Math.Min(rowsR, columnsR); for (var i = 0; i < minmn; i++) { - GenerateColumn(work, r, rRows, i, rRows - 1, i); - ComputeQR(work, i, r, rRows, i, rRows - 1, i + 1, rColumns - 1); + GenerateColumn(work, r, rowsR, i, rowsR - 1, i); + ComputeQR(work, i, r, rowsR, i, rowsR - 1, i + 1, columnsR - 1); } for (var i = minmn - 1; i >= 0; i--) { - ComputeQR(work, i, q, rRows, i, rRows - 1, i, rRows - 1); + ComputeQR(work, i, q, rowsR, i, rowsR - 1, i, rowsR - 1); } - work[0] = rRows * rRows; + work[0] = rowsR * rowsR; } #region QR Factor Helper functions @@ -1442,8 +1469,8 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra var norm = 0.0; for (var i = 0; i < rowEnd - rowStart + 1; ++i) { - var iIndex = tmp + i; - norm += work[iIndex] * work[iIndex]; + var iindex = tmp + i; + norm += work[iindex] * work[iindex]; } norm = Math.Sqrt(norm); @@ -1475,14 +1502,14 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra /// /// On entry, it is the M by N A matrix to factor. On exit, /// it is overwritten with the R matrix of the QR factorization. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// On exit, A M by M matrix that holds the Q matrix of the /// QR factorization. /// The B matrix. - /// The number of columns of B. + /// The number of columns of B. /// On exit, the solution matrix. - public void QRSolve(double[] r, int rRows, int rColumns, double[] q, double[] b, int bColumns, double[] x) + public void QRSolve(double[] r, int rowsR, int columnsR, double[] q, double[] b, int columnsB, double[] x) { if (r == null) { @@ -1504,28 +1531,28 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra throw new ArgumentNullException("q"); } - if (r.Length != rRows * rColumns) + if (r.Length != rowsR * columnsR) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "r"); } - if (q.Length != rRows * rRows) + if (q.Length != rowsR * rowsR) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "q"); } - if (b.Length != rRows * bColumns) + if (b.Length != rowsR * columnsB) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); } - if (x.Length != rColumns * bColumns) + if (x.Length != columnsR * columnsB) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "x"); } - var work = new double[rRows * rRows]; - QRSolve(r, rRows, rColumns, q, b, bColumns, x, work); + var work = new double[rowsR * rowsR]; + QRSolve(r, rowsR, columnsR, q, b, columnsB, x, work); } /// @@ -1533,17 +1560,17 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra /// /// On entry, it is the M by N A matrix to factor. On exit, /// it is overwritten with the R matrix of the QR factorization. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// On exit, A M by M matrix that holds the Q matrix of the /// QR factorization. /// The B matrix. - /// The number of columns of B. + /// The number of columns of B. /// On exit, the solution matrix. /// The work array. The array must have a length of at least N, /// but should be N*blocksize. The blocksize is machine dependent. On exit, work[0] contains the optimal /// work size value. - public void QRSolve(double[] r, int rRows, int rColumns, double[] q, double[] b, int bColumns, double[] x, double[] work) + public void QRSolve(double[] r, int rowsR, int columnsR, double[] q, double[] b, int columnsB, double[] x, double[] work) { if (r == null) { @@ -1565,36 +1592,36 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra throw new ArgumentNullException("q"); } - if (r.Length != rRows * rColumns) + if (r.Length != rowsR * columnsR) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "r"); } - if (q.Length != rRows * rRows) + if (q.Length != rowsR * rowsR) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "q"); } - if (b.Length != rRows * bColumns) + if (b.Length != rowsR * columnsB) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); } - if (x.Length != rColumns * bColumns) + if (x.Length != columnsR * columnsB) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "x"); } - if (work.Length < rRows * rRows) + if (work.Length < rowsR * rowsR) { - work[0] = rRows * rRows; + work[0] = rowsR * rowsR; throw new ArgumentException(Resources.WorkArrayTooSmall, "work"); } - QRFactor(r, rRows, rColumns, q, work); - QRSolveFactored(q, r, rRows, rColumns, b, bColumns, x); + QRFactor(r, rowsR, columnsR, q, work); + QRSolveFactored(q, r, rowsR, columnsR, b, columnsB, x); - work[0] = rRows * rRows; + work[0] = rowsR * rowsR; } /// @@ -1602,12 +1629,12 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra /// /// The Q matrix obtained by calling . /// The R matrix obtained by calling . - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// The B matrix. - /// The number of columns of B. + /// The number of columns of B. /// On exit, the solution matrix. - public void QRSolveFactored(double[] q, double[] r, int rRows, int rColumns, double[] b, int bColumns, double[] x) + public void QRSolveFactored(double[] q, double[] r, int rowsR, int columnsR, double[] b, int columnsB, double[] x) { if (r == null) { @@ -1629,22 +1656,22 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra throw new ArgumentNullException("q"); } - if (r.Length != rRows * rColumns) + if (r.Length != rowsR * columnsR) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "r"); } - if (q.Length != rRows * rRows) + if (q.Length != rowsR * rowsR) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "q"); } - if (b.Length != rRows * bColumns) + if (b.Length != rowsR * columnsB) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); } - if (x.Length != rColumns * bColumns) + if (x.Length != columnsR * columnsB) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "x"); } @@ -1655,35 +1682,35 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra Buffer.BlockCopy(b, 0, sol, 0, b.Length * Constants.SizeOfDouble); // Compute Y = transpose(Q)*B - var column = new double[rRows]; - for (var j = 0; j < bColumns; j++) + var column = new double[rowsR]; + for (var j = 0; j < columnsB; j++) { - var jm = j * rRows; - CommonParallel.For(0, rRows, k => column[k] = sol[jm + k]); + var jm = j * rowsR; + CommonParallel.For(0, rowsR, k => column[k] = sol[jm + k]); CommonParallel.For( 0, - rRows, + rowsR, i => { - var im = i * rRows; - sol[jm + i] = CommonParallel.Aggregate(0, rRows, k => q[im + k] * column[k]); + var im = i * rowsR; + sol[jm + i] = CommonParallel.Aggregate(0, rowsR, k => q[im + k] * column[k]); }); } // Solve R*X = Y; - for (var k = rColumns - 1; k >= 0; k--) + for (var k = columnsR - 1; k >= 0; k--) { - var km = k * rRows; - for (var j = 0; j < bColumns; j++) + var km = k * rowsR; + for (var j = 0; j < columnsB; j++) { - sol[(j * rRows) + k] /= r[km + k]; + sol[(j * rowsR) + k] /= r[km + k]; } for (var i = 0; i < k; i++) { - for (var j = 0; j < bColumns; j++) + for (var j = 0; j < columnsB; j++) { - var jm = j * rRows; + var jm = j * rowsR; sol[jm + i] -= sol[jm + k] * r[km + i]; } } @@ -1692,12 +1719,12 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra // Fill result matrix CommonParallel.For( 0, - rColumns, + columnsR, row => { - for (var col = 0; col < bColumns; col++) + for (var col = 0; col < columnsB; col++) { - x[(col * rColumns) + row] = sol[row + (col * rRows)]; + x[(col * columnsR) + row] = sol[row + (col * rowsR)]; } }); } @@ -1707,15 +1734,15 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra /// /// Compute the singular U and VT vectors or not. /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// The singular values of A in ascending value. /// If is true, on exit U contains the left /// singular vectors. /// If is true, on exit VT contains the transposed /// right singular vectors. /// This is equivalent to the GESVD LAPACK routine. - public void SingularValueDecomposition(bool computeVectors, double[] a, int aRows, int aColumns, double[] s, double[] u, double[] vt) + public void SingularValueDecomposition(bool computeVectors, double[] a, int rowsA, int columnsA, double[] s, double[] u, double[] vt) { if (a == null) { @@ -1737,24 +1764,24 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra throw new ArgumentNullException("vt"); } - if (u.Length != aRows * aRows) + if (u.Length != rowsA * rowsA) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "u"); } - if (vt.Length != aColumns * aColumns) + if (vt.Length != columnsA * columnsA) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "vt"); } - if (s.Length != Math.Min(aRows, aColumns)) + if (s.Length != Math.Min(rowsA, columnsA)) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "s"); } // TODO: Actually "work = new double[aRows]" is acceptable size of work array. I set size proposed in method description - var work = new double[Math.Max((3 * Math.Min(aRows, aColumns)) + Math.Max(aRows, aColumns), 5 * Math.Min(aRows, aColumns))]; - SingularValueDecomposition(computeVectors, a, aRows, aColumns, s, u, vt, work); + var work = new double[Math.Max((3 * Math.Min(rowsA, columnsA)) + Math.Max(rowsA, columnsA), 5 * Math.Min(rowsA, columnsA))]; + SingularValueDecomposition(computeVectors, a, rowsA, columnsA, s, u, vt, work); } /// @@ -1762,8 +1789,8 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra /// /// Compute the singular U and VT vectors or not. /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// The singular values of A in ascending value. /// If is true, on exit U contains the left /// singular vectors. @@ -1773,7 +1800,7 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra /// Max(3*Min(M, N) + Max(M, N), 5*Min(M,N)). For complex matrices, 2*Min(M, N) + Max(M, N). /// On exit, work[0] contains the optimal work size value. /// This is equivalent to the GESVD LAPACK routine. - public void SingularValueDecomposition(bool computeVectors, double[] a, int aRows, int aColumns, double[] s, double[] u, double[] vt, double[] work) + public void SingularValueDecomposition(bool computeVectors, double[] a, int rowsA, int columnsA, double[] s, double[] u, double[] vt, double[] work) { if (a == null) { @@ -1800,17 +1827,17 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra throw new ArgumentNullException("work"); } - if (u.Length != aRows * aRows) + if (u.Length != rowsA * rowsA) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "u"); } - if (vt.Length != aColumns * aColumns) + if (vt.Length != columnsA * columnsA) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "vt"); } - if (s.Length != Math.Min(aRows, aColumns)) + if (s.Length != Math.Min(rowsA, columnsA)) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "s"); } @@ -1820,17 +1847,17 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra throw new ArgumentException(Resources.ArgumentSingleDimensionArray, "work"); } - if (work.Length < aRows) + if (work.Length < rowsA) { - work[0] = aRows; + work[0] = rowsA; throw new ArgumentException(Resources.WorkArrayTooSmall, "work"); } const int Maxiter = 1000; - var e = new double[aColumns]; + var e = new double[columnsA]; var v = new double[vt.Length]; - var stemp = new double[Math.Min(aRows + 1, aColumns)]; + var stemp = new double[Math.Min(rowsA + 1, columnsA)]; int i, j, l, lp1; @@ -1838,12 +1865,12 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra var sn = 0.0; double t; - var ncu = aRows; + var ncu = rowsA; // Reduce matrix to bidiagonal form, storing the diagonal elements // in "s" and the super-diagonal elements in "e". - var nct = Math.Min(aRows - 1, aColumns); - var nrt = Math.Max(0, Math.Min(aColumns - 2, aRows)); + var nct = Math.Min(rowsA - 1, columnsA); + var nrt = Math.Max(0, Math.Min(columnsA - 2, rowsA)); var lu = Math.Max(nct, nrt); for (l = 0; l < lu; l++) @@ -1854,28 +1881,28 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra // Compute the transformation for the l-th column and // place the l-th diagonal in vector s[l]. var l1 = l; - stemp[l] = Math.Sqrt(CommonParallel.Aggregate(l, aRows, i1 => (a[(l1 * aRows) + i1] * a[(l1 * aRows) + i1]))); + stemp[l] = Math.Sqrt(CommonParallel.Aggregate(l, rowsA, i1 => (a[(l1 * rowsA) + i1] * a[(l1 * rowsA) + i1]))); if (stemp[l] != 0.0) { - if (a[(l * aRows) + l] != 0.0) + if (a[(l * rowsA) + l] != 0.0) { - stemp[l] = Math.Abs(stemp[l]) * (a[(l * aRows) + l] / Math.Abs(a[(l * aRows) + l])); + stemp[l] = Math.Abs(stemp[l]) * (a[(l * rowsA) + l] / Math.Abs(a[(l * rowsA) + l])); } // A part of column "l" of Matrix A from row "l" to end multiply by 1.0 / s[l] - for (i = l; i < aRows; i++) + for (i = l; i < rowsA; i++) { - a[(l * aRows) + i] = a[(l * aRows) + i] * (1.0 / stemp[l]); + a[(l * rowsA) + i] = a[(l * rowsA) + i] * (1.0 / stemp[l]); } - a[(l * aRows) + l] = 1.0 + a[(l * aRows) + l]; + a[(l * rowsA) + l] = 1.0 + a[(l * rowsA) + l]; } stemp[l] = -stemp[l]; } - for (j = lp1; j < aColumns; j++) + for (j = lp1; j < columnsA; j++) { if (l < nct) { @@ -1883,31 +1910,31 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra { // Apply the transformation. t = 0.0; - for (i = l; i < aRows; i++) + for (i = l; i < rowsA; i++) { - t += a[(j * aRows) + i] * a[(l * aRows) + i]; + t += a[(j * rowsA) + i] * a[(l * rowsA) + i]; } - t = -t / a[(l * aRows) + l]; + t = -t / a[(l * rowsA) + l]; - for (var ii = l; ii < aRows; ii++) + for (var ii = l; ii < rowsA; ii++) { - a[(j * aRows) + ii] += t * a[(l * aRows) + ii]; + a[(j * rowsA) + ii] += t * a[(l * rowsA) + ii]; } } } // Place the l-th row of matrix into "e" for the // subsequent calculation of the row transformation. - e[j] = a[(j * aRows) + l]; + e[j] = a[(j * rowsA) + l]; } if (computeVectors && l < nct) { // Place the transformation in "u" for subsequent back multiplication. - for (i = l; i < aRows; i++) + for (i = l; i < rowsA; i++) { - u[(l * aRows) + i] = a[(l * aRows) + i]; + u[(l * rowsA) + i] = a[(l * rowsA) + i]; } } @@ -1942,28 +1969,28 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra e[l] = -e[l]; - if (lp1 < aRows && e[l] != 0.0) + if (lp1 < rowsA && e[l] != 0.0) { // Apply the transformation. - for (i = lp1; i < aRows; i++) + for (i = lp1; i < rowsA; i++) { work[i] = 0.0; } - for (j = lp1; j < aColumns; j++) + for (j = lp1; j < columnsA; j++) { - for (var ii = lp1; ii < aRows; ii++) + for (var ii = lp1; ii < rowsA; ii++) { - work[ii] += e[j] * a[(j * aRows) + ii]; + work[ii] += e[j] * a[(j * rowsA) + ii]; } } - for (j = lp1; j < aColumns; j++) + for (j = lp1; j < columnsA; j++) { var ww = -e[j] / e[lp1]; - for (var ii = lp1; ii < aRows; ii++) + for (var ii = lp1; ii < rowsA; ii++) { - a[(j * aRows) + ii] += ww * work[ii]; + a[(j * rowsA) + ii] += ww * work[ii]; } } } @@ -1974,29 +2001,29 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra } // Place the transformation in v for subsequent back multiplication. - for (i = lp1; i < aColumns; i++) + for (i = lp1; i < columnsA; i++) { - v[(l * aColumns) + i] = e[i]; + v[(l * columnsA) + i] = e[i]; } } // Set up the final bidiagonal matrix or order m. - var m = Math.Min(aColumns, aRows + 1); + var m = Math.Min(columnsA, rowsA + 1); var nctp1 = nct + 1; var nrtp1 = nrt + 1; - if (nct < aColumns) + if (nct < columnsA) { - stemp[nctp1 - 1] = a[((nctp1 - 1) * aRows) + (nctp1 - 1)]; + stemp[nctp1 - 1] = a[((nctp1 - 1) * rowsA) + (nctp1 - 1)]; } - if (aRows < m) + if (rowsA < m) { stemp[m - 1] = 0.0; } if (nrtp1 < m) { - e[nrtp1 - 1] = a[((m - 1) * aRows) + (nrtp1 - 1)]; + e[nrtp1 - 1] = a[((m - 1) * rowsA) + (nrtp1 - 1)]; } e[m - 1] = 0.0; @@ -2006,12 +2033,12 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra { for (j = nctp1 - 1; j < ncu; j++) { - for (i = 0; i < aRows; i++) + for (i = 0; i < rowsA; i++) { - u[(j * aRows) + i] = 0.0; + u[(j * rowsA) + i] = 0.0; } - u[(j * aRows) + j] = 1.0; + u[(j * rowsA) + j] = 1.0; } for (l = nct - 1; l >= 0; l--) @@ -2021,39 +2048,39 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra for (j = l + 1; j < ncu; j++) { t = 0.0; - for (i = l; i < aRows; i++) + for (i = l; i < rowsA; i++) { - t += u[(j * aRows) + i] * u[(l * aRows) + i]; + t += u[(j * rowsA) + i] * u[(l * rowsA) + i]; } - t = -t / u[(l * aRows) + l]; + t = -t / u[(l * rowsA) + l]; - for (var ii = l; ii < aRows; ii++) + for (var ii = l; ii < rowsA; ii++) { - u[(j * aRows) + ii] += t * u[(l * aRows) + ii]; + u[(j * rowsA) + ii] += t * u[(l * rowsA) + ii]; } } // A part of column "l" of matrix A from row "l" to end multiply by -1.0 - for (i = l; i < aRows; i++) + for (i = l; i < rowsA; i++) { - u[(l * aRows) + i] = u[(l * aRows) + i] * -1.0; + u[(l * rowsA) + i] = u[(l * rowsA) + i] * -1.0; } - u[(l * aRows) + l] = 1.0 + u[(l * aRows) + l]; + u[(l * rowsA) + l] = 1.0 + u[(l * rowsA) + l]; for (i = 0; i < l; i++) { - u[(l * aRows) + i] = 0.0; + u[(l * rowsA) + i] = 0.0; } } else { - for (i = 0; i < aRows; i++) + for (i = 0; i < rowsA; i++) { - u[(l * aRows) + i] = 0.0; + u[(l * rowsA) + i] = 0.0; } - u[(l * aRows) + l] = 1.0; + u[(l * rowsA) + l] = 1.0; } } } @@ -2061,36 +2088,36 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra // If it is required, generate v. if (computeVectors) { - for (l = aColumns - 1; l >= 0; l--) + for (l = columnsA - 1; l >= 0; l--) { lp1 = l + 1; if (l < nrt) { if (e[l] != 0.0) { - for (j = lp1; j < aColumns; j++) + for (j = lp1; j < columnsA; j++) { t = 0.0; - for (i = lp1; i < aColumns; i++) + for (i = lp1; i < columnsA; i++) { - t += v[(j * aColumns) + i] * v[(l * aColumns) + i]; + t += v[(j * columnsA) + i] * v[(l * columnsA) + i]; } - t = -t / v[(l * aColumns) + lp1]; - for (var ii = l; ii < aColumns; ii++) + t = -t / v[(l * columnsA) + lp1]; + for (var ii = l; ii < columnsA; ii++) { - v[(j * aColumns) + ii] += t * v[(l * aColumns) + ii]; + v[(j * columnsA) + ii] += t * v[(l * columnsA) + ii]; } } } } - for (i = 0; i < aColumns; i++) + for (i = 0; i < columnsA; i++) { - v[(l * aColumns) + i] = 0.0; + v[(l * columnsA) + i] = 0.0; } - v[(l * aColumns) + l] = 1.0; + v[(l * columnsA) + l] = 1.0; } } @@ -2111,9 +2138,9 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra if (computeVectors) { // A part of column "i" of matrix U from row 0 to end multiply by r - for (j = 0; j < aRows; j++) + for (j = 0; j < rowsA; j++) { - u[(i * aRows) + j] = u[(i * aRows) + j] * r; + u[(i * rowsA) + j] = u[(i * rowsA) + j] * r; } } } @@ -2139,9 +2166,9 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra } // A part of column "i+1" of matrix VT from row 0 to end multiply by r - for (j = 0; j < aColumns; j++) + for (j = 0; j < columnsA; j++) { - v[((i + 1) * aColumns) + j] = v[((i + 1) * aColumns) + j] * r; + v[((i + 1) * columnsA) + j] = v[((i + 1) * columnsA) + j] * r; } } @@ -2249,11 +2276,11 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra if (computeVectors) { // Rotate - for (i = 0; i < aColumns; i++) + for (i = 0; i < columnsA; i++) { - var z = (cs * v[(k * aColumns) + i]) + (sn * v[((m - 1) * aColumns) + i]); - v[((m - 1) * aColumns) + i] = (cs * v[((m - 1) * aColumns) + i]) - (sn * v[(k * aColumns) + i]); - v[(k * aColumns) + i] = z; + var z = (cs * v[(k * columnsA) + i]) + (sn * v[((m - 1) * columnsA) + i]); + v[((m - 1) * columnsA) + i] = (cs * v[((m - 1) * columnsA) + i]) - (sn * v[(k * columnsA) + i]); + v[(k * columnsA) + i] = z; } } } @@ -2274,11 +2301,11 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra if (computeVectors) { // Rotate - for (i = 0; i < aRows; i++) + for (i = 0; i < rowsA; i++) { - var z = (cs * u[(k * aRows) + i]) + (sn * u[((l - 1) * aRows) + i]); - u[((l - 1) * aRows) + i] = (cs * u[((l - 1) * aRows) + i]) - (sn * u[(k * aRows) + i]); - u[(k * aRows) + i] = z; + var z = (cs * u[(k * rowsA) + i]) + (sn * u[((l - 1) * rowsA) + i]); + u[((l - 1) * rowsA) + i] = (cs * u[((l - 1) * rowsA) + i]) - (sn * u[(k * rowsA) + i]); + u[(k * rowsA) + i] = z; } } } @@ -2332,11 +2359,11 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra stemp[k + 1] = cs * stemp[k + 1]; if (computeVectors) { - for (i = 0; i < aColumns; i++) + for (i = 0; i < columnsA; i++) { - var z = (cs * v[(k * aColumns) + i]) + (sn * v[((k + 1) * aColumns) + i]); - v[((k + 1) * aColumns) + i] = (cs * v[((k + 1) * aColumns) + i]) - (sn * v[(k * aColumns) + i]); - v[(k * aColumns) + i] = z; + var z = (cs * v[(k * columnsA) + i]) + (sn * v[((k + 1) * columnsA) + i]); + v[((k + 1) * columnsA) + i] = (cs * v[((k + 1) * columnsA) + i]) - (sn * v[(k * columnsA) + i]); + v[(k * columnsA) + i] = z; } } @@ -2346,13 +2373,13 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra stemp[k + 1] = -(sn * e[k]) + (cs * stemp[k + 1]); g = sn * e[k + 1]; e[k + 1] = cs * e[k + 1]; - if (computeVectors && k < aRows) + if (computeVectors && k < rowsA) { - for (i = 0; i < aRows; i++) + for (i = 0; i < rowsA; i++) { - var z = (cs * u[(k * aRows) + i]) + (sn * u[((k + 1) * aRows) + i]); - u[((k + 1) * aRows) + i] = (cs * u[((k + 1) * aRows) + i]) - (sn * u[(k * aRows) + i]); - u[(k * aRows) + i] = z; + var z = (cs * u[(k * rowsA) + i]) + (sn * u[((k + 1) * rowsA) + i]); + u[((k + 1) * rowsA) + i] = (cs * u[((k + 1) * rowsA) + i]) - (sn * u[(k * rowsA) + i]); + u[(k * rowsA) + i] = z; } } } @@ -2371,9 +2398,9 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra if (computeVectors) { // A part of column "l" of matrix VT from row 0 to end multiply by -1 - for (i = 0; i < aColumns; i++) + for (i = 0; i < columnsA; i++) { - v[(l * aColumns) + i] = v[(l * aColumns) + i] * -1.0; + v[(l * columnsA) + i] = v[(l * columnsA) + i] * -1.0; } } } @@ -2389,25 +2416,25 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra t = stemp[l]; stemp[l] = stemp[l + 1]; stemp[l + 1] = t; - if (computeVectors && l < aColumns) + if (computeVectors && l < columnsA) { // Swap columns l, l + 1 - for (i = 0; i < aColumns; i++) + for (i = 0; i < columnsA; i++) { - var z = v[(l * aColumns) + i]; - v[(l * aColumns) + i] = v[((l + 1) * aColumns) + i]; - v[((l + 1) * aColumns) + i] = z; + var z = v[(l * columnsA) + i]; + v[(l * columnsA) + i] = v[((l + 1) * columnsA) + i]; + v[((l + 1) * columnsA) + i] = z; } } - if (computeVectors && l < aRows) + if (computeVectors && l < rowsA) { // Swap columns l, l + 1 - for (i = 0; i < aRows; i++) + for (i = 0; i < rowsA; i++) { - var z = u[(l * aRows) + i]; - u[(l * aRows) + i] = u[((l + 1) * aRows) + i]; - u[((l + 1) * aRows) + i] = z; + var z = u[(l * rowsA) + i]; + u[(l * rowsA) + i] = u[((l + 1) * rowsA) + i]; + u[((l + 1) * rowsA) + i] = z; } } @@ -2423,11 +2450,11 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra if (computeVectors) { // Finally transpose "v" to get "vt" matrix - for (i = 0; i < aColumns; i++) + for (i = 0; i < columnsA; i++) { - for (j = 0; j < aColumns; j++) + for (j = 0; j < columnsA; j++) { - vt[(j * aColumns) + i] = v[(i * aColumns) + j]; + vt[(j * columnsA) + i] = v[(i * columnsA) + j]; } } } @@ -2435,30 +2462,83 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra // Copy stemp to s with size adjustment. We are using ported copy of linpack's svd code and it uses // a singular vector of length rows+1 when rows < columns. The last element is not used and needs to be removed. // We should port lapack's svd routine to remove this problem. - Buffer.BlockCopy(stemp, 0, s, 0, Math.Min(aRows, aColumns) * Constants.SizeOfDouble); + Buffer.BlockCopy(stemp, 0, s, 0, Math.Min(rowsA, columnsA) * Constants.SizeOfDouble); // On return the first element of the work array stores the min size of the work array could have been // work[0] = Math.Max(3 * Math.Min(aRows, aColumns) + Math.Max(aRows, aColumns), 5 * Math.Min(aRows, aColumns)); - work[0] = aRows; + work[0] = rowsA; } /// - /// Computes the singular value decomposition of A using CommonParallel where possible. + /// 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. + /// + /// Provides the x-coordinate of the point p. On exit contains the parameter r associated with the Givens rotation + /// Provides the y-coordinate of the point p. On exit contains the parameter z associated with the Givens rotation + /// Contains the parameter c associated with the Givens rotation + /// Contains the parameter s associated with the Givens rotation + /// This is equivalent to the DROTG LAPACK routine. + private static void Drotg(ref double da, ref double db, ref double c, ref double s) + { + double 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.0; + s = 0.0; + r = 0.0; + z = 0.0; + } + else + { + var sda = da / scale; + var sdb = db / scale; + r = scale * Math.Sqrt((sda * sda) + (sdb * sdb)); + if (roe < 0.0) + { + r = -r; + } + + c = da / r; + s = db / r; + z = 1.0; + if (absda > absdb) + { + z = s; + } + + if (absdb >= absda && c != 0.0) + { + z = 1.0 / c; + } + } + + da = r; + db = z; + } + + /// + /// Solves A*X=B for X using the singular value decomposition of A. /// - /// Compute the singular U and VT vectors or not. /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// The singular values of A in ascending value. - /// If is true, on exit U contains the left - /// singular vectors. - /// If is true, on exit VT contains the transposed - /// right singular vectors. - /// The work array. For real matrices, the work array should be at least - /// Max(3*Min(M, N) + Max(M, N), 5*Min(M,N)). For complex matrices, 2*Min(M, N) + Max(M, N). - /// On exit, work[0] contains the optimal work size value. - /// THIS METHOD IS NOT USED. ONLY FOR BENCHMARK PURPOSES. - public void SingularValueDecompositionWithCommonParallel(bool computeVectors, double[] a, int aRows, int aColumns, double[] s, double[] u, double[] vt, double[] work) + /// On exit U contains the left singular vectors. + /// On exit VT contains the transposed right singular vectors. + /// The B matrix. + /// The number of columns of B. + /// On exit, the solution matrix. + public void SvdSolve(double[] a, int rowsA, int columnsA, double[] s, double[] u, double[] vt, double[] b, int columnsB, double[] x) { if (a == null) { @@ -2480,687 +2560,1932 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra throw new ArgumentNullException("vt"); } - if (u.Length != aRows * aRows) + if (b == null) + { + throw new ArgumentNullException("b"); + } + + if (x == null) + { + throw new ArgumentNullException("x"); + } + + if (u.Length != rowsA * rowsA) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "u"); } - if (vt.Length != aColumns * aColumns) + if (vt.Length != columnsA * columnsA) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "vt"); } - if (s.Length != Math.Min(aRows, aColumns)) + if (s.Length != Math.Min(rowsA, columnsA)) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "s"); } - if (work == null) + if (b.Length != rowsA * columnsB) { - throw new ArgumentNullException("work"); + throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); } - if (work.Length == 0) + if (x.Length != columnsA * columnsB) { - throw new ArgumentException(Resources.ArgumentSingleDimensionArray); + throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); } - if (work.Length < aRows) + // TODO: Actually "work = new double[aRows]" is acceptable size of work array. I set size proposed in method description + var work = new double[Math.Max((3 * Math.Min(rowsA, columnsA)) + Math.Max(rowsA, columnsA), 5 * Math.Min(rowsA, columnsA))]; + SvdSolve(a, rowsA, columnsA, s, u, vt, b, columnsB, x, work); + } + + /// + /// Solves A*X=B for X using the singular value decomposition of A. + /// + /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. + /// The singular values of A in ascending value. + /// On exit U contains the left singular vectors. + /// On exit VT contains the transposed right singular vectors. + /// The B matrix. + /// The number of columns of B. + /// On exit, the solution matrix. + /// The work array. For real matrices, the work array should be at least + /// Max(3*Min(M, N) + Max(M, N), 5*Min(M,N)). For complex matrices, 2*Min(M, N) + Max(M, N). + /// On exit, work[0] contains the optimal work size value. + public void SvdSolve(double[] a, int rowsA, int columnsA, double[] s, double[] u, double[] vt, double[] b, int columnsB, double[] x, double[] work) + { + if (a == null) { - work[0] = aRows; - throw new ArgumentException(Resources.WorkArrayTooSmall, "work"); + throw new ArgumentNullException("a"); } - const int Maxiter = 1000; - - var e = new double[aColumns]; - var v = new double[vt.Length]; - var stemp = new double[Math.Min(aRows + 1, aColumns)]; + if (s == null) + { + throw new ArgumentNullException("s"); + } - int i, j, l, lp1; + if (u == null) + { + throw new ArgumentNullException("u"); + } - var cs = 0.0; - var sn = 0.0; - double t; + if (vt == null) + { + throw new ArgumentNullException("vt"); + } - var ncu = aRows; + if (b == null) + { + throw new ArgumentNullException("b"); + } - // Reduce matrix to bidiagonal form, storing the diagonal elements - // in "s" and the super-diagonal elements in "e". - var nct = Math.Min(aRows - 1, aColumns); - var nrt = Math.Max(0, Math.Min(aColumns - 2, aRows)); - var lu = Math.Max(nct, nrt); - - for (l = 0; l < lu; l++) + if (x == null) { - lp1 = l + 1; - var lCopy = l; - if (l < nct) - { - // Compute the transformation for the l-th column and - // place the l-th diagonal in vector s[l]. - stemp[l] = Math.Sqrt(CommonParallel.Aggregate(lCopy, aRows, row => (a[(lCopy * aRows) + row] * a[(lCopy * aRows) + row]))); - - if (stemp[l] != 0.0) - { - if (a[(l * aRows) + l] != 0.0) - { - stemp[l] = Math.Abs(stemp[l]) * (a[(l * aRows) + l] / Math.Abs(a[(l * aRows) + l])); - } - - // A part of column "l" of Matrix A from row "l" to end multiply by 1.0 / s[l] - for (i = l; i < aRows; i++) - { - a[(l * aRows) + i] = a[(l * aRows) + i] * (1.0 / stemp[l]); - } + throw new ArgumentNullException("x"); + } - a[(l * aRows) + l] = 1.0 + a[(l * aRows) + l]; - } + if (u.Length != rowsA * rowsA) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "u"); + } - stemp[l] = -stemp[l]; - } + if (vt.Length != columnsA * columnsA) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "vt"); + } - for (j = lp1; j < aColumns; j++) - { - var jcopy = j; + if (s.Length != Math.Min(rowsA, columnsA)) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "s"); + } - if (l < nct) - { - if (stemp[l] != 0.0) - { - // Apply the transformation. - t = CommonParallel.Aggregate(lCopy, aRows, row => a[(jcopy * aRows) + row] * a[(lCopy * aRows) + row]); - t = -t / a[(l * aRows) + l]; - for (var ii = l; ii < aRows; ii++) - { - a[(j * aRows) + ii] += t * a[(l * aRows) + ii]; - } - } - } + if (b.Length != rowsA * columnsB) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); + } - // Place the l-th row of matrix into "e" for the - // subsequent calculation of the row transformation. - e[j] = a[(j * aRows) + l]; - } + if (x.Length != columnsA * columnsB) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); + } - if (computeVectors && l < nct) - { - // Place the transformation in "u" for subsequent back multiplication. - CommonParallel.For(lCopy, aRows, row => u[(lCopy * aRows) + row] = a[(lCopy * aRows) + row]); - } + if (work.Length == 0) + { + throw new ArgumentException(Resources.ArgumentSingleDimensionArray, "work"); + } - if (l >= nrt) - { - continue; - } + if (work.Length < rowsA) + { + work[0] = rowsA; + throw new ArgumentException(Resources.WorkArrayTooSmall, "work"); + } - // Compute the l-th row transformation and place the l-th super-diagonal in e(l). - e[l] = Math.Sqrt(CommonParallel.Aggregate(lp1, e.Length, index => e[index] * e[index])); - if (e[l] != 0.0) - { - if (e[lp1] != 0.0) - { - e[l] = Math.Abs(e[l]) * (e[lp1] / Math.Abs(e[lp1])); - } + SingularValueDecomposition(true, a, rowsA, columnsA, s, u, vt, work); + SvdSolveFactored(rowsA, columnsA, s, u, vt, b, columnsB, x); + } - // Scale vector "e" from "lp1" by 1.0 / e[l] - // lp1 > l, so we may use CommonParallel here - CommonParallel.For(lp1, e.Length, index => e[index] = e[index] * (1.0 / e[lCopy])); - e[lp1] = 1.0 + e[lp1]; - } + /// + /// Solves A*X=B for X using a previously SVD decomposed matrix. + /// + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. + /// The s values returned by . + /// The left singular vectors returned by . + /// The right singular vectors returned by . + /// The B matrix. + /// The number of columns of B. + /// On exit, the solution matrix. + public void SvdSolveFactored(int rowsA, int columnsA, double[] s, double[] u, double[] vt, double[] b, int columnsB, double[] x) + { + if (s == null) + { + throw new ArgumentNullException("s"); + } - e[l] = -e[l]; + if (u == null) + { + throw new ArgumentNullException("u"); + } - if (lp1 < aRows && e[l] != 0.0) - { - // Apply the transformation. - for (i = lp1; i < aRows; i++) - { - work[i] = 0.0; - } + if (vt == null) + { + throw new ArgumentNullException("vt"); + } - for (j = lp1; j < aColumns; j++) - { - for (var ii = lp1; ii < aRows; ii++) - { - work[ii] += e[j] * a[(j * aRows) + ii]; - } - } + if (b == null) + { + throw new ArgumentNullException("b"); + } - for (j = lp1; j < aColumns; j++) - { - var ww = -e[j] / e[lp1]; - for (var ii = lp1; ii < aRows; ii++) - { - a[(j * aRows) + ii] += ww * work[ii]; - } - } - } + if (x == null) + { + throw new ArgumentNullException("x"); + } - if (!computeVectors) - { - continue; - } + if (u.Length != rowsA * rowsA) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "u"); + } - // Place the transformation in v for subsequent back multiplication. - CommonParallel.For(lp1, aColumns, index => v[(lCopy * aColumns) + index] = e[index]); + if (vt.Length != columnsA * columnsA) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "vt"); } - // Set up the final bidiagonal matrix or order m. - var m = Math.Min(aColumns, aRows + 1); - var nctp1 = nct + 1; - var nrtp1 = nrt + 1; - if (nct < aColumns) + if (s.Length != Math.Min(rowsA, columnsA)) { - stemp[nctp1 - 1] = a[(nctp1 - 1) * aRows + (nctp1 - 1)]; + throw new ArgumentException(Resources.ArgumentArraysSameLength, "s"); } - if (aRows < m) + if (b.Length != rowsA * columnsB) { - stemp[m - 1] = 0.0; + throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); } - if (nrtp1 < m) + if (x.Length != columnsA * columnsB) { - e[nrtp1 - 1] = a[(m - 1) * aRows + (nrtp1 - 1)]; + throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); } - e[m - 1] = 0.0; + var mn = Math.Min(rowsA, columnsA); + var tmp = new double[columnsA]; - // If required, generate "u". - if (computeVectors) + for (var k = 0; k < columnsB; k++) { - CommonParallel.For(nctp1 - 1, ncu, indexCol => - { - CommonParallel.For(0, aRows, indexRow => u[(indexCol * aRows) + indexRow] = 0.0); - u[(indexCol * aRows) + indexCol] = 1.0; - }); - - for (l = nct - 1; l >= 0; l--) + for (var j = 0; j < columnsA; j++) { - var lCopy = l; - - if (stemp[l] != 0.0) + double value = 0; + if (j < mn) { - for (j = l + 1; j < ncu; j++) + for (var i = 0; i < rowsA; i++) { - var jCopy = j; - t = CommonParallel.Aggregate(lCopy, aRows, index => u[(jCopy * aRows) + index] * u[(lCopy * aRows) + index]); - t = -t / u[(l * aRows) + l]; - - for (var ii = l; ii < aRows; ii++) - { - u[(j * aRows) + ii] += t * u[(l * aRows) + ii]; - } + value += u[(j * rowsA) + i] * b[(k * rowsA) + i]; } - // A part of column "l" of matrix A from row "l" to end multiply by -1.0 - CommonParallel.For(lCopy, aRows, index => u[(lCopy * aRows) + index] *= -1.0); - u[(l * aRows) + l] = 1.0 + u[(l * aRows) + l]; - if (lCopy != 0) - { - CommonParallel.For(0, lCopy, index => u[(lCopy * aRows) + index] = 0.0); - } - } - else - { - CommonParallel.For(0, aRows, index => u[(lCopy * aRows) + index] = 0.0); - u[(l * aRows) + l] = 1.0; + value /= s[j]; } + + tmp[j] = value; } - } - // If it is required, generate v. - if (computeVectors) - { - for (l = aColumns - 1; l >= 0; l--) + for (var j = 0; j < columnsA; j++) { - lp1 = l + 1; - var lCopy = l; - - if (l < nrt) + double value = 0; + for (var i = 0; i < columnsA; i++) { - if (e[l] != 0.0) - { - for (j = lp1; j < aColumns; j++) - { - var jCopy = j; - t = CommonParallel.Aggregate(lp1, aColumns, index => v[(jCopy * aColumns) + index] * v[(lCopy * aColumns) + index]); - t = -t / v[(l * aColumns) + lp1]; - for (var ii = l; ii < aColumns; ii++) - { - v[(j * aColumns) + ii] += t * v[(l * aColumns) + ii]; - } - } - } + value += vt[(j * columnsA) + i] * tmp[i]; } - CommonParallel.For(0, aColumns, index => v[(lCopy * aColumns) + index] = 0.0); - v[(l * aColumns) + l] = 1.0; + x[(k * columnsA) + j] = value; } } + } - // Transform "s" and "e" so that they are double - for (i = 0; i < m; i++) - { - double r; - if (stemp[i] != 0.0) - { - t = stemp[i]; - r = stemp[i] / t; - stemp[i] = t; - if (i < m - 1) - { - e[i] = e[i] / r; - } - - if (computeVectors) - { - // A part of column "i" of matrix U from row 0 to end multiply by r - for (j = 0; j < aRows; j++) - { - u[(i * aRows) + j] = u[(i * aRows) + j] * r; - } - } - } + #endregion - // Exit - if (i == m - 1) - { - break; - } + #region ILinearAlgebraProvider Members - if (e[i] == 0.0) - { - continue; - } + /// + /// Adds a scaled vector to another: y += alpha*x. + /// + /// The vector to update. + /// The value to scale by. + /// The vector to add to . + /// This equivalent to the AXPY BLAS routine. + public void AddVectorToScaledVector(float[] y, float alpha, float[] x) + { + if (y == null) + { + throw new ArgumentNullException("y"); + } - t = e[i]; - r = t / e[i]; - e[i] = t; - stemp[i + 1] = stemp[i + 1] * r; - if (!computeVectors) - { - continue; - } + if (x == null) + { + throw new ArgumentNullException("x"); + } - // A part of column "i+1" of matrix VT from row 0 to end multiply by r - for (j = 0; j < aColumns; j++) - { - v[((i + 1) * aColumns) + j] = v[((i + 1) * aColumns) + j] * r; - } + if (y.Length != x.Length) + { + throw new ArgumentException(Resources.ArgumentVectorsSameLength); } - // Main iteration loop for the singular values. - var mn = m; - var iter = 0; + if (alpha == 0.0) + { + return; + } - while (m > 0) + if (alpha == 1.0) { - // Quit if all the singular values have been found. - // If too many iterations have been performed throw exception. - if (iter >= Maxiter) - { - throw new ArgumentException(Resources.ConvergenceFailed); + CommonParallel.For(0, y.Length, i => y[i] += x[i]); + } + else + { + CommonParallel.For(0, y.Length, i => y[i] += alpha * x[i]); + } + } + + /// + /// Scales an array. Can be used to scale a vector and a matrix. + /// + /// The scalar. + /// The values to scale. + /// This is equivalent to the SCAL BLAS routine. + public void ScaleArray(float alpha, float[] x) + { + if (x == null) + { + throw new ArgumentNullException("x"); + } + + if (alpha == 1.0) + { + return; + } + + CommonParallel.For(0, x.Length, i => x[i] = alpha * x[i]); + } + + /// + /// Computes the dot product of x and y. + /// + /// The vector x. + /// The vector y. + /// The dot product of x and y. + /// This is equivalent to the DOT BLAS routine. + public float DotProduct(float[] x, float[] y) + { + if (y == null) + { + throw new ArgumentNullException("y"); + } + + if (x == null) + { + throw new ArgumentNullException("x"); + } + + if (y.Length != x.Length) + { + throw new ArgumentException(Resources.ArgumentVectorsSameLength); + } + + float sum = 0; + CommonParallel.For(0, y.Length, index => sum += y[index] * x[index]); + return sum; + } + + /// + /// Does a point wise add of two arrays z = x + y. This can be used + /// to add vectors or matrices. + /// + /// The array x. + /// The array y. + /// The result of the addition. + /// There is no equivalent BLAS routine, but many libraries + /// provide optimized (parallel and/or vectorized) versions of this + /// routine. + public void AddArrays(float[] x, float[] y, float[] result) + { + if (y == null) + { + throw new ArgumentNullException("y"); + } + + if (x == null) + { + throw new ArgumentNullException("x"); + } + + if (result == null) + { + throw new ArgumentNullException("result"); + } + + if (y.Length != x.Length || y.Length != result.Length) + { + throw new ArgumentException(Resources.ArgumentVectorsSameLength); + } + + CommonParallel.For(0, y.Length, i => result[i] = x[i] + y[i]); + } + + /// + /// Does a point wise subtraction of two arrays z = x - y. This can be used + /// to subtract vectors or matrices. + /// + /// The array x. + /// The array y. + /// The result of the subtraction. + /// There is no equivalent BLAS routine, but many libraries + /// provide optimized (parallel and/or vectorized) versions of this + /// routine. + public void SubtractArrays(float[] x, float[] y, float[] result) + { + if (y == null) + { + throw new ArgumentNullException("y"); + } + + if (x == null) + { + throw new ArgumentNullException("x"); + } + + if (result == null) + { + throw new ArgumentNullException("result"); + } + + if (y.Length != x.Length || y.Length != result.Length) + { + throw new ArgumentException(Resources.ArgumentVectorsSameLength); + } + + CommonParallel.For(0, y.Length, i => result[i] = x[i] - y[i]); + } + + /// + /// Does a point wise multiplication of two arrays z = x * y. This can be used + /// to multiple elements of vectors or matrices. + /// + /// The array x. + /// The array y. + /// The result of the point wise multiplication. + /// There is no equivalent BLAS routine, but many libraries + /// provide optimized (parallel and/or vectorized) versions of this + /// routine. + public void PointWiseMultiplyArrays(float[] x, float[] y, float[] result) + { + if (y == null) + { + throw new ArgumentNullException("y"); + } + + if (x == null) + { + throw new ArgumentNullException("x"); + } + + if (result == null) + { + throw new ArgumentNullException("result"); + } + + if (y.Length != x.Length || y.Length != result.Length) + { + throw new ArgumentException(Resources.ArgumentVectorsSameLength); + } + + CommonParallel.For(0, y.Length, i => result[i] = x[i] * y[i]); + } + + /// + /// Computes the requested of the matrix. + /// + /// The type of norm to compute. + /// The number of rows. + /// The number of columns. + /// The matrix to compute the norm from. + /// + /// The requested of the matrix. + /// + public float MatrixNorm(Norm norm, int rows, int columns, float[] matrix) + { + throw new NotImplementedException(); + } + + /// + /// Computes the requested of the matrix. + /// + /// The type of norm to compute. + /// The number of rows. + /// The number of columns. + /// The matrix to compute the norm from. + /// The work array. Only used when + /// and needs to be have a length of at least M (number of rows of . + /// + /// The requested of the matrix. + /// + public float MatrixNorm(Norm norm, int rows, int columns, float[] matrix, float[] work) + { + throw new NotImplementedException(); + } + + /// + /// Multiples two matrices. result = x * y + /// + /// The x matrix. + /// The number of rows in the x matrix. + /// The number of columns in the x matrix. + /// The y matrix. + /// The number of rows in the y matrix. + /// The number of columns in the y matrix. + /// Where to store the result of the multiplication. + /// This is a simplified version of the BLAS GEMM routine with alpha + /// set to 1.0 and beta set to 0.0, and x and y are not transposed. + public void MatrixMultiply(float[] x, int rowsX, int columnsX, float[] y, int rowsY, int columnsY, float[] result) + { + // First check some basic requirement on the parameters of the matrix multiplication. + if (x == null) + { + throw new ArgumentNullException("x"); + } + + if (y == null) + { + throw new ArgumentNullException("y"); + } + + if (result == null) + { + throw new ArgumentNullException("result"); + } + + if (rowsX * columnsX != x.Length) + { + throw new ArgumentException("x.Length != xRows * xColumns"); + } + + if (rowsY * columnsY != y.Length) + { + throw new ArgumentException("y.Length != yRows * yColumns"); + } + + if (columnsX != rowsY) + { + throw new ArgumentException("xColumns != yRows"); + } + + if (rowsX * columnsY != result.Length) + { + throw new ArgumentException("xRows * yColumns != result.Length"); + } + + // Check whether we will be overwriting any of our inputs and make copies if necessary. + // TODO - we can don't have to allocate a completely new matrix when x or y point to the same memory + // as result, we can do it on a row wise basis. We should investigate this. + float[] xdata; + if (ReferenceEquals(x, result)) + { + xdata = (float[])x.Clone(); + } + else + { + xdata = x; + } + + float[] ydata; + if (ReferenceEquals(y, result)) + { + ydata = (float[])y.Clone(); + } + else + { + ydata = y; + } + + // Start the actual matrix multiplication. + // TODO - For small matrices we should get rid of the parallelism because of startup costs. + // Perhaps the following implementations would be a good one + // http://blog.feradz.com/2009/01/cache-efficient-matrix-multiplication/ + MatrixMultiplyWithUpdate(Transpose.DontTranspose, Transpose.DontTranspose, 1.0f, xdata, rowsX, columnsX, ydata, rowsY, columnsY, 0.0f, result); + } + + /// + /// Multiplies two matrices and updates another with the result. c = alpha*op(a)*op(b) + beta*c + /// + /// How to transpose the matrix. + /// How to transpose the matrix. + /// The value to scale matrix. + /// The a matrix. + /// The number of rows in the matrix. + /// The number of columns in the matrix. + /// The b matrix + /// The number of rows in the matrix. + /// The number of columns in the matrix. + /// The value to scale the matrix. + /// The c matrix. + public void MatrixMultiplyWithUpdate(Transpose transposeA, Transpose transposeB, float alpha, float[] a, int rowsA, int columnsA, float[] b, int rowsB, int columnsB, float beta, float[] c) + { + // Choose nonsensical values for the number of rows in c; fill them in depending + // on the operations on a and b. + int rowsC; + + // First check some basic requirement on the parameters of the matrix multiplication. + if (a == null) + { + throw new ArgumentNullException("a"); + } + + if (b == null) + { + throw new ArgumentNullException("b"); + } + + if ((int)transposeA > 111 && (int)transposeB > 111) + { + if (rowsA != columnsB) + { + throw new ArgumentOutOfRangeException(); + } + + if (columnsA * rowsB != c.Length) + { + throw new ArgumentOutOfRangeException(); + } + + rowsC = columnsA; + } + else if ((int)transposeA > 111) + { + if (rowsA != rowsB) + { + throw new ArgumentOutOfRangeException(); + } + + if (columnsA * columnsB != c.Length) + { + throw new ArgumentOutOfRangeException(); + } + + rowsC = columnsA; + } + else if ((int)transposeB > 111) + { + if (columnsA != columnsB) + { + throw new ArgumentOutOfRangeException(); + } + + if (rowsA * rowsB != c.Length) + { + throw new ArgumentOutOfRangeException(); + } + + rowsC = rowsA; + } + else + { + if (columnsA != rowsB) + { + throw new ArgumentOutOfRangeException(); + } + + if (rowsA * columnsB != c.Length) + { + throw new ArgumentOutOfRangeException(); + } + + rowsC = rowsA; + } + + if (alpha == 0.0 && beta == 0.0) + { + Array.Clear(c, 0, c.Length); + return; + } + + // Check whether we will be overwriting any of our inputs and make copies if necessary. + // TODO - we can don't have to allocate a completely new matrix when x or y point to the same memory + // as result, we can do it on a row wise basis. We should investigate this. + float[] adata; + if (ReferenceEquals(a, c)) + { + adata = (float[])a.Clone(); + } + else + { + adata = a; + } + + float[] bdata; + if (ReferenceEquals(b, c)) + { + bdata = (float[])b.Clone(); + } + else + { + bdata = b; + } + + if (alpha == 1.0) + { + if (beta == 0.0) + { + if ((int)transposeA > 111 && (int)transposeB > 111) + { + CommonParallel.For( + 0, + columnsA, + j => + { + var jIndex = j * rowsC; + for (var i = 0; i != rowsB; i++) + { + var iIndex = i * rowsA; + float s = 0; + for (var l = 0; l != columnsB; l++) + { + s += adata[iIndex + l] * bdata[(l * rowsB) + j]; + } + + c[jIndex + i] = s; + } + }); + } + else if ((int)transposeA > 111) + { + CommonParallel.For( + 0, + columnsB, + j => + { + var jcIndex = j * rowsC; + var jbIndex = j * rowsB; + for (var i = 0; i != columnsA; i++) + { + var iIndex = i * rowsA; + float s = 0; + for (var l = 0; l != rowsA; l++) + { + s += adata[iIndex + l] * bdata[jbIndex + l]; + } + + c[jcIndex + i] = s; + } + }); + } + else if ((int)transposeB > 111) + { + CommonParallel.For( + 0, + rowsB, + j => + { + var jIndex = j * rowsC; + for (var i = 0; i != rowsA; i++) + { + float s = 0; + for (var l = 0; l != columnsA; l++) + { + s += adata[(l * rowsA) + i] * bdata[(l * rowsB) + j]; + } + + c[jIndex + i] = s; + } + }); + } + else + { + CommonParallel.For( + 0, + columnsB, + j => + { + var jcIndex = j * rowsC; + var jbIndex = j * rowsB; + for (var i = 0; i != rowsA; i++) + { + float s = 0; + for (var l = 0; l != columnsA; l++) + { + s += adata[(l * rowsA) + i] * bdata[jbIndex + l]; + } + + c[jcIndex + i] = s; + } + }); + } + } + else + { + if ((int)transposeA > 111 && (int)transposeB > 111) + { + CommonParallel.For( + 0, + columnsA, + j => + { + var jIndex = j * rowsC; + for (var i = 0; i != rowsB; i++) + { + var iIndex = i * rowsA; + float s = 0; + for (var l = 0; l != columnsB; l++) + { + s += adata[iIndex + l] * bdata[(l * rowsB) + j]; + } + + c[jIndex + i] = (c[jIndex + i] * beta) + s; + } + }); + } + else if ((int)transposeA > 111) + { + CommonParallel.For( + 0, + columnsB, + j => + { + var jcIndex = j * rowsC; + var jbIndex = j * rowsB; + for (var i = 0; i != columnsA; i++) + { + var iIndex = i * rowsA; + float s = 0; + for (var l = 0; l != rowsA; l++) + { + s += adata[iIndex + l] * bdata[jbIndex + l]; + } + + c[jcIndex + i] = s + (c[jcIndex + i] * beta); + } + }); + } + else if ((int)transposeB > 111) + { + CommonParallel.For( + 0, + rowsB, + j => + { + var jIndex = j * rowsC; + for (var i = 0; i != rowsA; i++) + { + float s = 0; + for (var l = 0; l != columnsA; l++) + { + s += adata[(l * rowsA) + i] * bdata[(l * rowsB) + j]; + } + + c[jIndex + i] = s + (c[jIndex + i] * beta); + } + }); + } + else + { + CommonParallel.For( + 0, + columnsB, + j => + { + var jcIndex = j * rowsC; + var jbIndex = j * rowsB; + for (var i = 0; i != rowsA; i++) + { + float s = 0; + for (var l = 0; l != columnsA; l++) + { + s += adata[(l * rowsA) + i] * bdata[jbIndex + l]; + } + + c[jcIndex + i] = s + (c[jcIndex + i] * beta); + } + }); + } + } + } + else + { + if ((int)transposeA > 111 && (int)transposeB > 111) + { + CommonParallel.For( + 0, + columnsA, + j => + { + var jIndex = j * rowsC; + for (var i = 0; i != rowsB; i++) + { + var iIndex = i * rowsA; + float s = 0; + for (var l = 0; l != columnsB; l++) + { + s += adata[iIndex + l] * bdata[(l * rowsB) + j]; + } + + c[jIndex + i] = (c[jIndex + i] * beta) + (alpha * s); + } + }); + } + else if ((int)transposeA > 111) + { + CommonParallel.For( + 0, + columnsB, + j => + { + var jcIndex = j * rowsC; + var jbIndex = j * rowsB; + for (var i = 0; i != columnsA; i++) + { + var iIndex = i * rowsA; + float s = 0; + for (var l = 0; l != rowsA; l++) + { + s += adata[iIndex + l] * bdata[jbIndex + l]; + } + + c[jcIndex + i] = (alpha * s) + (c[jcIndex + i] * beta); + } + }); + } + else if ((int)transposeB > 111) + { + CommonParallel.For( + 0, + rowsB, + j => + { + var jIndex = j * rowsC; + for (var i = 0; i != rowsA; i++) + { + float s = 0; + for (var l = 0; l != columnsA; l++) + { + s += adata[(l * rowsA) + i] * bdata[(l * rowsB) + j]; + } + + c[jIndex + i] = (alpha * s) + (c[jIndex + i] * beta); + } + }); + } + else + { + CommonParallel.For( + 0, + columnsB, + j => + { + var jcIndex = j * rowsC; + var jbIndex = j * rowsB; + for (var i = 0; i != rowsA; i++) + { + float s = 0; + for (var l = 0; l != columnsA; l++) + { + s += adata[(l * rowsA) + i] * bdata[jbIndex + l]; + } + + c[jcIndex + i] = (alpha * s) + (c[jcIndex + i] * beta); + } + }); + } + } + } + + /// + /// Computes the LUP factorization of A. P*A = L*U. + /// + /// An by matrix. The matrix is overwritten with the + /// the LU factorization on exit. The lower triangular factor L is stored in under the diagonal of (the diagonal is always 1.0 + /// for the L factor). The upper triangular factor U is stored on and above the diagonal of . + /// The order of the square matrix . + /// On exit, it contains the pivot indices. The size of the array must be . + /// This is equivalent to the GETRF LAPACK routine. + public void LUFactor(float[] data, int order, int[] ipiv) + { + if (data == null) + { + throw new ArgumentNullException("data"); + } + + if (ipiv == null) + { + throw new ArgumentNullException("ipiv"); + } + + if (data.Length != order * order) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "data"); + } + + if (ipiv.Length != order) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "ipiv"); + } + + // Initialize the pivot matrix to the identity permutation. + for (var i = 0; i < order; i++) + { + ipiv[i] = i; + } + + var vecLUcolj = new float[order]; + + // Outer loop. + for (var j = 0; j < order; j++) + { + var indexj = j * order; + var indexjj = indexj + j; + + // Make a copy of the j-th column to localize references. + for (var i = 0; i < order; i++) + { + vecLUcolj[i] = data[indexj + i]; + } + + // Apply previous transformations. + for (var i = 0; i < order; i++) + { + // Most of the time is spent in the following dot product. + var kmax = Math.Min(i, j); + var s = 0.0f; + for (var k = 0; k < kmax; k++) + { + s += data[(k * order) + i] * vecLUcolj[k]; + } + + data[indexj + i] = vecLUcolj[i] -= s; + } + + // Find pivot and exchange if necessary. + var p = j; + for (var i = j + 1; i < order; i++) + { + if (Math.Abs(vecLUcolj[i]) > Math.Abs(vecLUcolj[p])) + { + p = i; + } + } + + if (p != j) + { + for (var k = 0; k < order; k++) + { + var indexk = k * order; + var indexkp = indexk + p; + var indexkj = indexk + j; + var temp = data[indexkp]; + data[indexkp] = data[indexkj]; + data[indexkj] = temp; + } + + ipiv[j] = p; + } + + // Compute multipliers. + if (j < order & data[indexjj] != 0.0) + { + for (var i = j + 1; i < order; i++) + { + data[indexj + i] /= data[indexjj]; + } + } + } + } + + /// + /// Computes the inverse of matrix using LU factorization. + /// + /// The N by N matrix to invert. Contains the inverse On exit. + /// The order of the square matrix . + /// This is equivalent to the GETRF and GETRI LAPACK routines. + public void LUInverse(float[] a, int order) + { + if (a == null) + { + throw new ArgumentNullException("a"); + } + + if (a.Length != order * order) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "a"); + } + + var ipiv = new int[order]; + LUFactor(a, order, ipiv); + LUInverseFactored(a, order, ipiv); + } + + /// + /// Computes the inverse of a previously factored matrix. + /// + /// The LU factored N by N matrix. Contains the inverse On exit. + /// The order of the square matrix . + /// The pivot indices of . + /// This is equivalent to the GETRI LAPACK routine. + public void LUInverseFactored(float[] a, int order, int[] ipiv) + { + if (a == null) + { + throw new ArgumentNullException("a"); + } + + if (ipiv == null) + { + throw new ArgumentNullException("ipiv"); + } + + if (a.Length != order * order) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "a"); + } + + if (ipiv.Length != order) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "ipiv"); + } + + var inverse = new float[a.Length]; + for (var i = 0; i < order; i++) + { + inverse[i + (order * i)] = 1.0f; + } + + LUSolveFactored(order, a, order, ipiv, inverse); + Buffer.BlockCopy(inverse, 0, a, 0, a.Length * Constants.SizeOfFloat); + } + + /// + /// Computes the inverse of matrix using LU factorization. + /// + /// The N by N matrix to invert. Contains the inverse On exit. + /// The order of the square matrix . + /// The work array. The array must have a length of at least N, + /// but should be N*blocksize. The blocksize is machine dependent. On exit, work[0] contains the optimal + /// work size value. + /// This is equivalent to the GETRF and GETRI LAPACK routines. + public void LUInverse(float[] a, int order, float[] work) + { + LUInverse(a, order); + } + + /// + /// Computes the inverse of a previously factored matrix. + /// + /// The LU factored N by N matrix. Contains the inverse On exit. + /// The order of the square matrix . + /// The pivot indices of . + /// The work array. The array must have a length of at least N, + /// but should be N*blocksize. The blocksize is machine dependent. On exit, work[0] contains the optimal + /// work size value. + /// This is equivalent to the GETRI LAPACK routine. + public void LUInverseFactored(float[] a, int order, int[] ipiv, float[] work) + { + LUInverseFactored(a, order, ipiv); + } + + /// + /// Solves A*X=B for X using LU factorization. + /// + /// The number of columns of B. + /// The square matrix A. + /// The order of the square matrix . + /// The B matrix. + /// This is equivalent to the GETRF and GETRS LAPACK routines. + public void LUSolve(int columnsOfB, float[] a, int order, float[] b) + { + if (a == null) + { + throw new ArgumentNullException("a"); + } + + if (b == null) + { + throw new ArgumentNullException("b"); + } + + if (a.Length != order * order) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "a"); + } + + if (b.Length != order * columnsOfB) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); + } + + var ipiv = new int[order]; + LUFactor(a, order, ipiv); + LUSolveFactored(columnsOfB, a, order, ipiv, b); + } + + /// + /// Solves A*X=B for X using a previously factored A matrix. + /// + /// The number of columns of B. + /// The factored A matrix. + /// The order of the square matrix . + /// The pivot indices of . + /// The B matrix. + /// This is equivalent to the GETRS LAPACK routine. + public void LUSolveFactored(int columnsOfB, float[] a, int order, int[] ipiv, float[] b) + { + if (a == null) + { + throw new ArgumentNullException("a"); + } + + if (ipiv == null) + { + throw new ArgumentNullException("ipiv"); + } + + if (b == null) + { + throw new ArgumentNullException("b"); + } + + if (a.Length != order * order) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "a"); + } + + if (ipiv.Length != order) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "ipiv"); + } + + if (b.Length != order * columnsOfB) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); + } + + // Compute the column vector P*B + for (var i = 0; i < ipiv.Length; i++) + { + if (ipiv[i] == i) + { + continue; + } + + var p = ipiv[i]; + for (var j = 0; j < columnsOfB; j++) + { + var indexk = j * order; + var indexkp = indexk + p; + var indexkj = indexk + i; + var temp = b[indexkp]; + b[indexkp] = b[indexkj]; + b[indexkj] = temp; + } + } + + // Solve L*Y = P*B + for (var k = 0; k < order; k++) + { + var korder = k * order; + for (var i = k + 1; i < order; i++) + { + for (var j = 0; j < columnsOfB; j++) + { + var index = j * order; + b[i + index] -= b[k + index] * a[i + korder]; + } + } + } + + // Solve U*X = Y; + for (var k = order - 1; k >= 0; k--) + { + var korder = k + (k * order); + for (var j = 0; j < columnsOfB; j++) + { + b[k + (j * order)] /= a[korder]; + } + + korder = k * order; + for (var i = 0; i < k; i++) + { + for (var j = 0; j < columnsOfB; j++) + { + var index = j * order; + b[i + index] -= b[k + index] * a[i + korder]; + } + } + } + } + + /// + /// Solves A*X=B for X using LU factorization. + /// + /// How to transpose the matrix. + /// The number of columns of B. + /// The square matrix A. + /// The order of the square matrix . + /// The B matrix. + /// This is equivalent to the GETRF and GETRS LAPACK routines. + public void LUSolve(Transpose transposeA, int columnsOfB, float[] a, int order, float[] b) + { + if (a == null) + { + throw new ArgumentNullException("a"); + } + + if (b == null) + { + throw new ArgumentNullException("b"); + } + + if (a.Length != order * order) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "a"); + } + + if (b.Length != order * columnsOfB) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); + } + + var ipiv = new int[order]; + LUFactor(a, order, ipiv); + LUSolveFactored(transposeA, columnsOfB, a, order, ipiv, b); + } + + /// + /// Solves A*X=B for X using a previously factored A matrix. + /// + /// How to transpose the matrix. + /// The number of columns of B. + /// The factored A matrix. + /// The order of the square matrix . + /// The pivot indices of . + /// The B matrix. + /// This is equivalent to the GETRS LAPACK routine. + public void LUSolveFactored(Transpose transposeA, int columnsOfB, float[] a, int order, int[] ipiv, float[] b) + { + if (a == null) + { + throw new ArgumentNullException("a"); + } + + if (ipiv == null) + { + throw new ArgumentNullException("ipiv"); + } + + if (b == null) + { + throw new ArgumentNullException("b"); + } + + if (a.Length != order * order) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "a"); + } + + if (ipiv.Length != order) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "ipiv"); + } + + if (b.Length != order * columnsOfB) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); + } + + if ((transposeA == Transpose.Transpose) || (transposeA == Transpose.ConjugateTranspose)) + { + var aT = new float[a.Length]; + for (var i = 0; i < order; i++) + { + for (var j = 0; j < order; j++) + { + aT[(j * order) + i] = a[(i * order) + j]; + } + } + + LUSolveFactored(columnsOfB, aT, order, ipiv, b); + } + else + { + LUSolveFactored(columnsOfB, a, order, ipiv, b); + } + } + + /// + /// Computes the Cholesky factorization of A. + /// + /// On entry, a square, positive definite matrix. On exit, the matrix is overwritten with the + /// the Cholesky factorization. + /// The number of rows or columns in the matrix. + /// This is equivalent to the POTRF LAPACK routine. + public void CholeskyFactor(float[] a, int order) + { + var factor = new float[a.Length]; + + for (var j = 0; j < order; j++) + { + float d = 0.0f; + int index; + for (var k = 0; k < j; k++) + { + float s = 0.0f; + int i; + for (i = 0; i < k; i++) + { + s += factor[(i * order) + k] * factor[(i * order) + j]; + } + + var tmp = k * order; + index = tmp + j; + s = (a[index] - s) / factor[tmp + k]; + factor[index] = s; + d += s * s; + } + + index = (j * order) + j; + d = a[index] - d; + if (d <= 0.0F) + { + throw new ArgumentException(Resources.ArgumentMatrixPositiveDefinite); + } + + factor[index] = (float)Math.Sqrt(d); + for (var k = j + 1; k < order; k++) + { + factor[(k * order) + j] = 0.0F; } + } + + Buffer.BlockCopy(factor, 0, a, 0, factor.Length * Constants.SizeOfFloat); + } + + /// + /// Solves A*X=B for X using Cholesky factorization. + /// + /// The square, positive definite matrix A. + /// The number of rows and columns in A. + /// The B matrix. + /// The number of rows in the B matrix. + /// The number of columns in the B matrix. + /// This is equivalent to the POTRF add POTRS LAPACK routines. + public void CholeskySolve(float[] a, int orderA, float[] b, int rowsB, int columnsB) + { + if (a == null) + { + throw new ArgumentNullException("a"); + } + + if (b == null) + { + throw new ArgumentNullException("b"); + } + + if (orderA != rowsB) + { + throw new ArgumentException(Resources.ArgumentMatrixDimensions); + } + + if (ReferenceEquals(a, b)) + { + throw new ArgumentException(Resources.ArgumentReferenceDifferent); + } + + CholeskyFactor(a, orderA); + CholeskySolveFactored(a, orderA, b, rowsB, columnsB); + } + + /// + /// Solves A*X=B for X using a previously factored A matrix. + /// + /// The square, positive definite matrix A. + /// The number of rows and columns in A. + /// The B matrix. + /// 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(float[] a, int orderA, float[] b, int rowsB, int columnsB) + { + if (a == null) + { + throw new ArgumentNullException("a"); + } + + if (b == null) + { + throw new ArgumentNullException("b"); + } - // 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. + if (orderA != rowsB) + { + throw new ArgumentException(Resources.ArgumentMatrixDimensions); + } - // kase = 1: if mS[m] and e[l-1] are negligible and l < m - // kase = 2: if mS[l] is negligible and l < m - // kase = 3: if e[l-1] is negligible, l < m, and mS[l, ..., mS[m] are not negligible (qr step). - // kase = 4: if e[m-1] is negligible (convergence). - double ztest; - double test; - for (l = m - 2; l >= 0; l--) - { - test = Math.Abs(stemp[l]) + Math.Abs(stemp[l + 1]); - ztest = test + Math.Abs(e[l]); - if (ztest.AlmostEqualInDecimalPlaces(test, 15)) - { - e[l] = 0.0; - break; - } - } + if (ReferenceEquals(a, b)) + { + throw new ArgumentException(Resources.ArgumentReferenceDifferent); + } - int kase; - if (l == m - 2) - { - kase = 4; - } - else + CommonParallel.For( + 0, + columnsB, + c => { - int ls; - for (ls = m - 1; ls > l; ls--) + var cindex = c * orderA; + + // Solve L*Y = B; + float sum; + for (var i = 0; i < orderA; i++) { - test = 0.0; - if (ls != m - 1) + sum = b[cindex + i]; + for (var k = i - 1; k >= 0; k--) { - test = test + Math.Abs(e[ls]); + sum -= a[(k * orderA) + i] * b[cindex + k]; } - if (ls != l + 1) - { - test = test + Math.Abs(e[ls - 1]); - } + b[cindex + i] = sum / a[(i * orderA) + i]; + } - ztest = test + Math.Abs(stemp[ls]); - if (ztest.AlmostEqualInDecimalPlaces(test, 15)) + // Solve L'*X = Y; + for (var i = orderA - 1; i >= 0; i--) + { + sum = b[cindex + i]; + var iindex = i * orderA; + for (var k = i + 1; k < orderA; k++) { - stemp[ls] = 0.0; - break; + sum -= a[iindex + k] * b[cindex + k]; } - } - if (ls == l) - { - kase = 3; - } - else if (ls == m - 1) - { - kase = 1; + b[cindex + i] = sum / a[iindex + i]; } - else - { - kase = 2; - l = ls; - } - } + }); + } - l = l + 1; + /// + /// Computes the QR factorization of A. + /// + /// On entry, it is the M by N A matrix to factor. On exit, + /// it is overwritten with the R matrix of the QR factorization. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. + /// On exit, A M by M matrix that holds the Q matrix of the + /// QR factorization. + /// This is similar to the GEQRF and ORGQR LAPACK routines. + public void QRFactor(float[] r, int rowsR, int columnsR, float[] q) + { + if (r == null) + { + throw new ArgumentNullException("r"); + } - // Perform the task indicated by kase. - int k; - double f; - switch (kase) + if (q == null) + { + throw new ArgumentNullException("q"); + } + + if (r.Length != rowsR * columnsR) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "r"); + } + + if (q.Length != rowsR * rowsR) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "q"); + } + + var work = new float[rowsR * rowsR]; + QRFactor(r, rowsR, columnsR, q, work); + } + + /// + /// Computes the QR factorization of A. + /// + /// On entry, it is the M by N A matrix to factor. On exit, + /// it is overwritten with the R matrix of the QR factorization. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. + /// On exit, A M by M matrix that holds the Q matrix of the + /// QR factorization. + /// The work array. The array must have a length of at least N, + /// but should be N*blocksize. The blocksize is machine dependent. On exit, work[0] contains the optimal + /// work size value. + /// This is similar to the GEQRF and ORGQR LAPACK routines. + public void QRFactor(float[] r, int rowsR, int columnsR, float[] q, float[] work) + { + if (r == null) + { + throw new ArgumentNullException("r"); + } + + if (q == null) + { + throw new ArgumentNullException("q"); + } + + if (work == null) + { + throw new ArgumentNullException("q"); + } + + if (r.Length != rowsR * columnsR) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "r"); + } + + if (q.Length != rowsR * rowsR) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "q"); + } + + if (work.Length < rowsR * rowsR) + { + work[0] = rowsR * rowsR; + throw new ArgumentException(Resources.WorkArrayTooSmall, "work"); + } + + CommonParallel.For(0, rowsR, i => q[(i * rowsR) + i] = 1.0f); + + var minmn = Math.Min(rowsR, columnsR); + for (var i = 0; i < minmn; i++) + { + GenerateColumn(work, r, rowsR, i, rowsR - 1, i); + ComputeQR(work, i, r, rowsR, i, rowsR - 1, i + 1, columnsR - 1); + } + + for (var i = minmn - 1; i >= 0; i--) + { + ComputeQR(work, i, q, rowsR, i, rowsR - 1, i, rowsR - 1); + } + + work[0] = rowsR * rowsR; + } + + #region QR Factor Helper functions + + /// + /// Perform calculation of Q or R + /// + /// Work array + /// Index of colunn in work array + /// Q or R matrices + /// The number of rows + /// The first row in + /// The last row + /// The first column + /// The last column + private static void ComputeQR(float[] work, int workIndex, float[] a, int rowCount, int rowStart, int rowEnd, int columnStart, int columnEnd) + { + if (rowStart > rowEnd || columnStart > columnEnd) + { + return; + } + + var vector = new float[columnEnd - columnStart + 1]; + for (var i = rowStart; i <= rowEnd; i++) + { + for (var j = columnStart; j <= columnEnd; j++) { - // Deflate negligible s[m]. - case 1: - f = e[m - 2]; - e[m - 2] = 0.0; - double t1; - for (var kk = l; kk < m - 1; kk++) - { - k = m - 2 - kk + l; - t1 = stemp[k]; + vector[j - columnStart] += work[(workIndex * rowCount) + i - rowStart] * a[(j * rowCount) + i]; + } + } - Drotg(ref t1, ref f, ref cs, ref sn); - stemp[k] = t1; - if (k != l) - { - f = -sn * e[k - 1]; - e[k - 1] = cs * e[k - 1]; - } + for (var i = rowStart; i <= rowEnd; i++) + { + for (var j = columnStart; j <= columnEnd; j++) + { + a[(j * rowCount) + i] -= work[(workIndex * rowCount) + i - rowStart] * vector[j - columnStart]; + } + } + } - if (computeVectors) - { - // Rotate - for (i = 0; i < aColumns; i++) - { - var z = cs * v[(k * aColumns) + i] + sn * v[((m - 1) * aColumns) + i]; - v[((m - 1) * aColumns) + i] = cs * v[((m - 1) * aColumns) + i] - sn * v[(k * aColumns) + i]; - v[(k * aColumns) + i] = z; - } - } - } + /// + /// Generate column from initial matrix to work array + /// + /// Work array + /// Initial matrix + /// The number of rows in matrix + /// The firts row + /// The last row + /// Column index + private static void GenerateColumn(float[] work, float[] a, int rowCount, int rowStart, int rowEnd, int column) + { + var tmp = column * rowCount; + var index = tmp + rowStart; - break; + CommonParallel.For( + rowStart, + rowEnd + 1, + i => + { + var iIndex = tmp + i; + work[iIndex - rowStart] = a[iIndex]; + a[iIndex] = 0.0f; + }); - // Split at negligible s[l]. - case 2: - f = e[l - 1]; - e[l - 1] = 0.0; - for (k = l; k < m; k++) - { - t1 = stemp[k]; - Drotg(ref t1, ref f, ref cs, ref sn); - stemp[k] = t1; - f = -sn * e[k]; - e[k] = cs * e[k]; - if (computeVectors) - { - // Rotate - for (i = 0; i < aRows; i++) - { - var z = cs * u[(k * aRows) + i] + sn * u[((l - 1) * aRows) + i]; - u[((l - 1) * aRows) + i] = cs * u[((l - 1) * aRows) + i] - sn * u[(k * aRows) + i]; - u[(k * aRows) + i] = z; - } - } - } + var norm = 0.0; + for (var i = 0; i < rowEnd - rowStart + 1; ++i) + { + var iindex = tmp + i; + norm += work[iindex] * work[iindex]; + } + + norm = Math.Sqrt(norm); + if (rowStart == rowEnd || norm == 0) + { + a[index] = -work[tmp]; + work[tmp] = (float)Math.Sqrt(2.0); + return; + } + + var scale = 1.0f / (float)norm; + if (work[tmp] < 0.0) + { + scale *= -1.0f; + } - break; + a[index] = -1.0f / scale; + CommonParallel.For(0, rowEnd - rowStart + 1, i => work[tmp + i] *= scale); + work[tmp] += 1.0f; - // Perform one qr step. - case 3: + var s = (float)Math.Sqrt(1.0 / work[tmp]); + CommonParallel.For(0, rowEnd - rowStart + 1, i => work[tmp + i] *= s); + } -// calculate the shift. - var scale = 0.0; - scale = Math.Max(scale, Math.Abs(stemp[m - 1])); - scale = Math.Max(scale, Math.Abs(stemp[m - 2])); - scale = Math.Max(scale, Math.Abs(e[m - 2])); - scale = Math.Max(scale, Math.Abs(stemp[l])); - scale = Math.Max(scale, Math.Abs(e[l])); - var sm = stemp[m - 1] / scale; - var smm1 = stemp[m - 2] / scale; - var emm1 = e[m - 2] / scale; - var sl = stemp[l] / scale; - var el = e[l] / scale; - var b = ((smm1 + sm) * (smm1 - sm) + (emm1 * emm1)) / 2.0; - var c = (sm * emm1) * (sm * emm1); - var shift = 0.0; - if (b != 0.0 || c != 0.0) - { - shift = Math.Sqrt((b * b) + c); - if (b < 0.0) - { - shift = -shift; - } + #endregion - shift = c / (b + shift); - } + /// + /// Solves A*X=B for X using QR factorization of A. + /// + /// On entry, it is the M by N A matrix to factor. On exit, + /// it is overwritten with the R matrix of the QR factorization. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. + /// On exit, A M by M matrix that holds the Q matrix of the + /// QR factorization. + /// The B matrix. + /// The number of columns of B. + /// On exit, the solution matrix. + public void QRSolve(float[] r, int rowsR, int columnsR, float[] q, float[] b, int columnsB, float[] x) + { + if (r == null) + { + throw new ArgumentNullException("r"); + } - f = (sl + sm) * (sl - sm) + shift; - var g = sl * el; + if (q == null) + { + throw new ArgumentNullException("q"); + } - // 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; - } + if (b == null) + { + throw new ArgumentNullException("q"); + } - f = cs * stemp[k] + sn * e[k]; - e[k] = cs * e[k] - sn * stemp[k]; - g = sn * stemp[k + 1]; - stemp[k + 1] = cs * stemp[k + 1]; - if (computeVectors) - { - for (i = 0; i < aColumns; i++) - { - var z = cs * v[k * aColumns + i] + sn * v[(k + 1) * aColumns + i]; - v[(k + 1) * aColumns + i] = cs * v[(k + 1) * aColumns + i] - sn * v[k * aColumns + i]; - v[k * aColumns + i] = z; - } - } + if (x == null) + { + throw new ArgumentNullException("q"); + } - Drotg(ref f, ref g, ref cs, ref sn); - stemp[k] = f; - f = cs * e[k] + sn * stemp[k + 1]; - stemp[k + 1] = -sn * e[k] + cs * stemp[k + 1]; - g = sn * e[k + 1]; - e[k + 1] = cs * e[k + 1]; - if (computeVectors && k < aRows) - { - for (i = 0; i < aRows; i++) - { - var z = cs * u[k * aRows + i] + sn * u[(k + 1) * aRows + i]; - u[(k + 1) * aRows + i] = cs * u[(k + 1) * aRows + i] - sn * u[k * aRows + i]; - u[k * aRows + i] = z; - } - } - } + if (r.Length != rowsR * columnsR) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "r"); + } - e[m - 2] = f; - iter = iter + 1; - break; + if (q.Length != rowsR * rowsR) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "q"); + } - // Convergence - case 4: + if (b.Length != rowsR * columnsB) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); + } -// Make the singular value positive - if (stemp[l] < 0.0) - { - stemp[l] = -stemp[l]; - if (computeVectors) - { - // A part of column "l" of matrix VT from row 0 to end multiply by -1 - for (i = 0; i < aColumns; i++) - { - v[(l * aColumns) + i] = v[(l * aColumns) + i] * -1.0; - } - } - } + if (x.Length != columnsR * columnsB) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "x"); + } - // Order the singular value. - while (l != mn - 1) - { - if (stemp[l] >= stemp[l + 1]) - { - break; - } + var work = new float[rowsR * rowsR]; + QRSolve(r, rowsR, columnsR, q, b, columnsB, x, work); + } - t = stemp[l]; - stemp[l] = stemp[l + 1]; - stemp[l + 1] = t; - if (computeVectors && l < aColumns) - { - // Swap columns l, l + 1 - for (i = 0; i < aColumns; i++) - { - var z = v[l * aColumns + i]; - v[l * aColumns + i] = v[(l + 1) * aColumns + i]; - v[(l + 1) * aColumns + i] = z; - } - } + /// + /// Solves A*X=B for X using QR factorization of A. + /// + /// On entry, it is the M by N A matrix to factor. On exit, + /// it is overwritten with the R matrix of the QR factorization. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. + /// On exit, A M by M matrix that holds the Q matrix of the + /// QR factorization. + /// The B matrix. + /// The number of columns of B. + /// On exit, the solution matrix. + /// The work array. The array must have a length of at least N, + /// but should be N*blocksize. The blocksize is machine dependent. On exit, work[0] contains the optimal + /// work size value. + public void QRSolve(float[] r, int rowsR, int columnsR, float[] q, float[] b, int columnsB, float[] x, float[] work) + { + if (r == null) + { + throw new ArgumentNullException("r"); + } - if (computeVectors && l < aRows) - { - // Swap columns l, l + 1 - for (i = 0; i < aRows; i++) - { - var z = u[l * aRows + i]; - u[l * aRows + i] = u[(l + 1) * aRows + i]; - u[(l + 1) * aRows + i] = z; - } - } + if (q == null) + { + throw new ArgumentNullException("q"); + } - l = l + 1; - } + if (b == null) + { + throw new ArgumentNullException("q"); + } - iter = 0; - m = m - 1; - break; - } + if (x == null) + { + throw new ArgumentNullException("q"); } - if (computeVectors) + if (r.Length != rowsR * columnsR) { - // Finally transpose "v" to get "vt" matrix - for (i = 0; i < aColumns; i++) - { - for (j = 0; j < aColumns; j++) - { - vt[j * aColumns + i] = v[i * aColumns + j]; - } - } + throw new ArgumentException(Resources.ArgumentArraysSameLength, "r"); } - // Copy stemp to s with size adjustment. We are using ported copy of linpack's svd code and it uses - // a singular vector of length rows+1 when rows < columns. The last element is not used and needs to be removed. - // We should port lapack's svd routine to remove this problem. - Buffer.BlockCopy(stemp, 0, s, 0, Math.Min(aRows, aColumns) * Constants.SizeOfDouble); + if (q.Length != rowsR * rowsR) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "q"); + } - // On return the first element of the work array stores the min size of the work array could have been - // work[0] = Math.Max(3 * Math.Min(aRows, aColumns) + Math.Max(aRows, aColumns), 5 * Math.Min(aRows, aColumns)); - work[0] = aRows; - } + if (b.Length != rowsR * columnsB) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); + } + + if (x.Length != columnsR * columnsB) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "x"); + } + + if (work.Length < rowsR * rowsR) + { + work[0] = rowsR * rowsR; + throw new ArgumentException(Resources.WorkArrayTooSmall, "work"); + } + + QRFactor(r, rowsR, columnsR, q, work); + QRSolveFactored(q, r, rowsR, columnsR, b, columnsB, x); + work[0] = rowsR * rowsR; + } + /// - /// 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. + /// Solves A*X=B for X using a previously QR factored matrix. /// - /// Provides the x-coordinate of the point p. On exit contains the parameter r associated with the Givens rotation - /// Provides the y-coordinate of the point p. On exit contains the parameter z associated with the Givens rotation - /// Contains the parameter c associated with the Givens rotation - /// Contains the parameter s associated with the Givens rotation - /// This is equivalent to the DROTG LAPACK routine. - private static void Drotg(ref double da, ref double db, ref double c, ref double s) + /// The Q matrix obtained by calling . + /// The R matrix obtained by calling . + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. + /// The B matrix. + /// The number of columns of B. + /// On exit, the solution matrix. + public void QRSolveFactored(float[] q, float[] r, int rowsR, int columnsR, float[] b, int columnsB, float[] x) { - double r, z; + if (r == null) + { + throw new ArgumentNullException("r"); + } - var roe = db; - var absda = Math.Abs(da); - var absdb = Math.Abs(db); - if (absda > absdb) + if (q == null) { - roe = da; + throw new ArgumentNullException("q"); } - var scale = absda + absdb; - if (scale == 0.0) + if (b == null) { - c = 1.0; - s = 0.0; - r = 0.0; - z = 0.0; + throw new ArgumentNullException("q"); + } + + if (x == null) + { + throw new ArgumentNullException("q"); + } + + if (r.Length != rowsR * columnsR) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "r"); + } + + if (q.Length != rowsR * rowsR) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "q"); + } + + if (b.Length != rowsR * columnsB) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); + } + + if (x.Length != columnsR * columnsB) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "x"); + } + + var sol = new float[b.Length]; + + // Copy B matrix to "sol", so B data will not be changed + Buffer.BlockCopy(b, 0, sol, 0, b.Length * Constants.SizeOfFloat); + + // Compute Y = transpose(Q)*B + var column = new float[rowsR]; + for (var j = 0; j < columnsB; j++) + { + var jm = j * rowsR; + CommonParallel.For(0, rowsR, k => column[k] = sol[jm + k]); + CommonParallel.For( + 0, + rowsR, + i => + { + var im = i * rowsR; + sol[jm + i] = CommonParallel.Aggregate(0, rowsR, k => q[im + k] * column[k]); + }); } - else - { - var sda = da / scale; - var sdb = db / scale; - r = scale * Math.Sqrt((sda * sda) + (sdb * sdb)); - if (roe < 0.0) - { - r = -r; - } - c = da / r; - s = db / r; - z = 1.0; - if (absda > absdb) + // Solve R*X = Y; + for (var k = columnsR - 1; k >= 0; k--) + { + var km = k * rowsR; + for (var j = 0; j < columnsB; j++) { - z = s; + sol[(j * rowsR) + k] /= r[km + k]; } - if (absdb >= absda && c != 0.0) + for (var i = 0; i < k; i++) { - z = 1.0 / c; + for (var j = 0; j < columnsB; j++) + { + var jm = j * rowsR; + sol[jm + i] -= sol[jm + k] * r[km + i]; + } } } - da = r; - db = z; + // Fill result matrix + CommonParallel.For( + 0, + columnsR, + row => + { + for (var col = 0; col < columnsB; col++) + { + x[(col * columnsR) + row] = sol[row + (col * rowsR)]; + } + }); } /// - /// Solves A*X=B for X using the singular value decomposition of A. + /// Computes the singular value decomposition of A. /// + /// Compute the singular U and VT vectors or not. /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// The singular values of A in ascending value. - /// On exit U contains the left singular vectors. - /// On exit VT contains the transposed right singular vectors. - /// The B matrix. - /// The number of columns of B. - /// On exit, the solution matrix. - public void SvdSolve(double[] a, int aRows, int aColumns, double[] s, double[] u, double[] vt, double[] b, int bColumns, double[] x) + /// If is true, on exit U contains the left + /// singular vectors. + /// If is true, on exit VT contains the transposed + /// right singular vectors. + /// This is equivalent to the GESVD LAPACK routine. + public void SingularValueDecomposition(bool computeVectors, float[] a, int rowsA, int columnsA, float[] s, float[] u, float[] vt) { - if (a == null) + if (a == null) { throw new ArgumentNullException("a"); } @@ -3180,62 +4505,43 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra throw new ArgumentNullException("vt"); } - if (b == null) - { - throw new ArgumentNullException("b"); - } - - if (x == null) - { - throw new ArgumentNullException("x"); - } - - if (u.Length != aRows * aRows) + if (u.Length != rowsA * rowsA) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "u"); } - if (vt.Length != aColumns * aColumns) + if (vt.Length != columnsA * columnsA) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "vt"); } - if (s.Length != Math.Min(aRows, aColumns)) + if (s.Length != Math.Min(rowsA, columnsA)) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "s"); } - if (b.Length != aRows * bColumns) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); - } - - if (x.Length != aColumns * bColumns) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); - } - // TODO: Actually "work = new double[aRows]" is acceptable size of work array. I set size proposed in method description - var work = new double[Math.Max((3 * Math.Min(aRows, aColumns)) + Math.Max(aRows, aColumns), 5 * Math.Min(aRows, aColumns))]; - SvdSolve(a, aRows, aColumns, s, u, vt, b, bColumns, x, work); + var work = new float[Math.Max((3 * Math.Min(rowsA, columnsA)) + Math.Max(rowsA, columnsA), 5 * Math.Min(rowsA, columnsA))]; + SingularValueDecomposition(computeVectors, a, rowsA, columnsA, s, u, vt, work); } /// - /// Solves A*X=B for X using the singular value decomposition of A. + /// Computes the singular value decomposition of A. /// + /// Compute the singular U and VT vectors or not. /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// The singular values of A in ascending value. - /// On exit U contains the left singular vectors. - /// On exit VT contains the transposed right singular vectors. - /// The B matrix. - /// The number of columns of B. - /// On exit, the solution matrix. + /// If is true, on exit U contains the left + /// singular vectors. + /// If is true, on exit VT contains the transposed + /// right singular vectors. /// The work array. For real matrices, the work array should be at least /// Max(3*Min(M, N) + Max(M, N), 5*Min(M,N)). For complex matrices, 2*Min(M, N) + Max(M, N). /// On exit, work[0] contains the optimal work size value. - public void SvdSolve(double[] a, int aRows, int aColumns, double[] s, double[] u, double[] vt, double[] b, int bColumns, double[] x, double[] work) + /// This is equivalent to the GESVD LAPACK routine. + public void SingularValueDecomposition(bool computeVectors, float[] a, int rowsA, int columnsA, float[] s, float[] u, float[] vt, float[] work) { if (a == null) { @@ -3257,1129 +4563,966 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra throw new ArgumentNullException("vt"); } - if (b == null) - { - throw new ArgumentNullException("b"); - } - - if (x == null) + if (work == null) { - throw new ArgumentNullException("x"); + throw new ArgumentNullException("work"); } - if (u.Length != aRows * aRows) + if (u.Length != rowsA * rowsA) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "u"); } - if (vt.Length != aColumns * aColumns) + if (vt.Length != columnsA * columnsA) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "vt"); } - if (s.Length != Math.Min(aRows, aColumns)) + if (s.Length != Math.Min(rowsA, columnsA)) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "s"); } - if (b.Length != aRows * bColumns) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); - } - - if (x.Length != aColumns * bColumns) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); - } - if (work.Length == 0) { throw new ArgumentException(Resources.ArgumentSingleDimensionArray, "work"); } - if (work.Length < aRows) + if (work.Length < rowsA) { - work[0] = aRows; + work[0] = rowsA; throw new ArgumentException(Resources.WorkArrayTooSmall, "work"); } - SingularValueDecomposition(true, a, aRows, aColumns, s, u, vt, work); - SvdSolveFactored(aRows, aColumns, s, u, vt, b, bColumns, x); - } + const int Maxiter = 1000; - /// - /// Solves A*X=B for X using a previously SVD decomposed matrix. - /// - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. - /// The s values returned by . - /// The left singular vectors returned by . - /// The right singular vectors returned by . - /// The B matrix. - /// The number of columns of B. - /// On exit, the solution matrix. - public void SvdSolveFactored(int aRows, int aColumns, double[] s, double[] u, double[] vt, double[] b, int bColumns, double[] x) - { - if (s == null) - { - throw new ArgumentNullException("s"); - } + var e = new float[columnsA]; + var v = new float[vt.Length]; + var stemp = new float[Math.Min(rowsA + 1, columnsA)]; - if (u == null) - { - throw new ArgumentNullException("u"); - } + int i, j, l, lp1; - if (vt == null) - { - throw new ArgumentNullException("vt"); - } + var cs = 0.0f; + var sn = 0.0f; + float t; - if (b == null) - { - throw new ArgumentNullException("b"); - } + var ncu = rowsA; - if (x == null) + // Reduce matrix to bidiagonal form, storing the diagonal elements + // in "s" and the super-diagonal elements in "e". + var nct = Math.Min(rowsA - 1, columnsA); + var nrt = Math.Max(0, Math.Min(columnsA - 2, rowsA)); + var lu = Math.Max(nct, nrt); + + for (l = 0; l < lu; l++) { - throw new ArgumentNullException("x"); + lp1 = l + 1; + if (l < nct) + { + // Compute the transformation for the l-th column and + // place the l-th diagonal in vector s[l]. + var l1 = l; + stemp[l] = (float)Math.Sqrt(CommonParallel.Aggregate(l, rowsA, i1 => (a[(l1 * rowsA) + i1] * a[(l1 * rowsA) + i1]))); + + if (stemp[l] != 0.0) + { + if (a[(l * rowsA) + l] != 0.0) + { + stemp[l] = Math.Abs(stemp[l]) * (a[(l * rowsA) + l] / Math.Abs(a[(l * rowsA) + l])); + } + + // A part of column "l" of Matrix A from row "l" to end multiply by 1.0 / s[l] + for (i = l; i < rowsA; i++) + { + a[(l * rowsA) + i] = a[(l * rowsA) + i] * (1.0f / stemp[l]); + } + + a[(l * rowsA) + l] = 1.0f + a[(l * rowsA) + l]; + } + + stemp[l] = -stemp[l]; + } + + for (j = lp1; j < columnsA; j++) + { + if (l < nct) + { + if (stemp[l] != 0.0) + { + // Apply the transformation. + t = 0.0f; + for (i = l; i < rowsA; i++) + { + t += a[(j * rowsA) + i] * a[(l * rowsA) + i]; + } + + t = -t / a[(l * rowsA) + l]; + + for (var ii = l; ii < rowsA; ii++) + { + a[(j * rowsA) + ii] += t * a[(l * rowsA) + ii]; + } + } + } + + // Place the l-th row of matrix into "e" for the + // subsequent calculation of the row transformation. + e[j] = a[(j * rowsA) + l]; + } + + if (computeVectors && l < nct) + { + // Place the transformation in "u" for subsequent back multiplication. + for (i = l; i < rowsA; i++) + { + u[(l * rowsA) + i] = a[(l * rowsA) + i]; + } + } + + if (l >= nrt) + { + continue; + } + + // Compute the l-th row transformation and place the l-th super-diagonal in e(l). + var enorm = 0.0; + for (i = lp1; i < e.Length; i++) + { + enorm += e[i] * e[i]; + } + + e[l] = (float)Math.Sqrt(enorm); + if (e[l] != 0.0) + { + if (e[lp1] != 0.0) + { + e[l] = Math.Abs(e[l]) * (e[lp1] / Math.Abs(e[lp1])); + } + + // Scale vector "e" from "lp1" by 1.0 / e[l] + for (i = lp1; i < e.Length; i++) + { + e[i] = e[i] * (1.0f / e[l]); + } + + e[lp1] = 1.0f + e[lp1]; + } + + e[l] = -e[l]; + + if (lp1 < rowsA && e[l] != 0.0) + { + // Apply the transformation. + for (i = lp1; i < rowsA; i++) + { + work[i] = 0.0f; + } + + for (j = lp1; j < columnsA; j++) + { + for (var ii = lp1; ii < rowsA; ii++) + { + work[ii] += e[j] * a[(j * rowsA) + ii]; + } + } + + for (j = lp1; j < columnsA; j++) + { + var ww = -e[j] / e[lp1]; + for (var ii = lp1; ii < rowsA; ii++) + { + a[(j * rowsA) + ii] += ww * work[ii]; + } + } + } + + if (!computeVectors) + { + continue; + } + + // Place the transformation in v for subsequent back multiplication. + for (i = lp1; i < columnsA; i++) + { + v[(l * columnsA) + i] = e[i]; + } } - if (u.Length != aRows * aRows) + // Set up the final bidiagonal matrix or order m. + var m = Math.Min(columnsA, rowsA + 1); + var nctp1 = nct + 1; + var nrtp1 = nrt + 1; + if (nct < columnsA) { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "u"); + stemp[nctp1 - 1] = a[((nctp1 - 1) * rowsA) + (nctp1 - 1)]; } - if (vt.Length != aColumns * aColumns) + if (rowsA < m) { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "vt"); + stemp[m - 1] = 0.0f; } - if (s.Length != Math.Min(aRows, aColumns)) + if (nrtp1 < m) { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "s"); + e[nrtp1 - 1] = a[((m - 1) * rowsA) + (nrtp1 - 1)]; } - if (b.Length != aRows * bColumns) + e[m - 1] = 0.0f; + + // If required, generate "u". + if (computeVectors) { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); - } + for (j = nctp1 - 1; j < ncu; j++) + { + for (i = 0; i < rowsA; i++) + { + u[(j * rowsA) + i] = 0.0f; + } + + u[(j * rowsA) + j] = 1.0f; + } + + for (l = nct - 1; l >= 0; l--) + { + if (stemp[l] != 0.0) + { + for (j = l + 1; j < ncu; j++) + { + t = 0.0f; + for (i = l; i < rowsA; i++) + { + t += u[(j * rowsA) + i] * u[(l * rowsA) + i]; + } + + t = -t / u[(l * rowsA) + l]; - if (x.Length != aColumns * bColumns) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); - } + for (var ii = l; ii < rowsA; ii++) + { + u[(j * rowsA) + ii] += t * u[(l * rowsA) + ii]; + } + } - var mn = Math.Min(aRows, aColumns); - var tmp = new double[aColumns]; + // A part of column "l" of matrix A from row "l" to end multiply by -1.0 + for (i = l; i < rowsA; i++) + { + u[(l * rowsA) + i] = u[(l * rowsA) + i] * -1.0f; + } - for (var k = 0; k < bColumns; k++) - { - for (var j = 0; j < aColumns; j++) - { - double value = 0; - if (j < mn) + u[(l * rowsA) + l] = 1.0f + u[(l * rowsA) + l]; + for (i = 0; i < l; i++) + { + u[(l * rowsA) + i] = 0.0f; + } + } + else { - for (var i = 0; i < aRows; i++) + for (i = 0; i < rowsA; i++) { - value += u[(j * aRows) + i] * b[(k * aRows) + i]; + u[(l * rowsA) + i] = 0.0f; } - value /= s[j]; + u[(l * rowsA) + l] = 1.0f; } - - tmp[j] = value; } + } - for (var j = 0; j < aColumns; j++) + // If it is required, generate v. + if (computeVectors) + { + for (l = columnsA - 1; l >= 0; l--) { - double value = 0; - for (var i = 0; i < aColumns; i++) + lp1 = l + 1; + if (l < nrt) { - value += vt[(j * aColumns) + i] * tmp[i]; - } - - x[(k * aColumns) + j] = value; - } - } - } + if (e[l] != 0.0) + { + for (j = lp1; j < columnsA; j++) + { + t = 0.0f; + for (i = lp1; i < columnsA; i++) + { + t += v[(j * columnsA) + i] * v[(l * columnsA) + i]; + } - #endregion + t = -t / v[(l * columnsA) + lp1]; + for (var ii = l; ii < columnsA; ii++) + { + v[(j * columnsA) + ii] += t * v[(l * columnsA) + ii]; + } + } + } + } - #region ILinearAlgebraProvider Members + for (i = 0; i < columnsA; i++) + { + v[(l * columnsA) + i] = 0.0f; + } - /// - /// Adds a scaled vector to another: y += alpha*x. - /// - /// The vector to update. - /// The value to scale by. - /// The vector to add to . - /// This equivalent to the AXPY BLAS routine. - public void AddVectorToScaledVector(float[] y, float alpha, float[] x) - { - if (y == null) - { - throw new ArgumentNullException("y"); + v[(l * columnsA) + l] = 1.0f; + } } - if (x == null) + // Transform "s" and "e" so that they are double + for (i = 0; i < m; i++) { - throw new ArgumentNullException("x"); - } + float r; + if (stemp[i] != 0.0) + { + t = stemp[i]; + r = stemp[i] / t; + stemp[i] = t; + if (i < m - 1) + { + e[i] = e[i] / r; + } - if (y.Length != x.Length) - { - throw new ArgumentException(Resources.ArgumentVectorsSameLength); - } + if (computeVectors) + { + // A part of column "i" of matrix U from row 0 to end multiply by r + for (j = 0; j < rowsA; j++) + { + u[(i * rowsA) + j] = u[(i * rowsA) + j] * r; + } + } + } - if (alpha == 0.0) - { - return; - } + // Exit + if (i == m - 1) + { + break; + } - if (alpha == 1.0) - { - CommonParallel.For(0, y.Length, i => y[i] += x[i]); - } - else - { - CommonParallel.For(0, y.Length, i => y[i] += alpha * x[i]); - } - } + if (e[i] == 0.0) + { + continue; + } - /// - /// Scales an array. Can be used to scale a vector and a matrix. - /// - /// The scalar. - /// The values to scale. - /// This is equivalent to the SCAL BLAS routine. - public void ScaleArray(float alpha, float[] x) - { - if (x == null) - { - throw new ArgumentNullException("x"); - } + t = e[i]; + r = t / e[i]; + e[i] = t; + stemp[i + 1] = stemp[i + 1] * r; + if (!computeVectors) + { + continue; + } - if (alpha == 1.0) - { - return; + // A part of column "i+1" of matrix VT from row 0 to end multiply by r + for (j = 0; j < columnsA; j++) + { + v[((i + 1) * columnsA) + j] = v[((i + 1) * columnsA) + j] * r; + } } - CommonParallel.For(0, x.Length, i => x[i] = alpha * x[i]); - } - - /// - /// Computes the dot product of x and y. - /// - /// The vector x. - /// The vector y. - /// The dot product of x and y. - /// This is equivalent to the DOT BLAS routine. - public float DotProduct(float[] x, float[] y) - { - if (y == null) - { - throw new ArgumentNullException("y"); - } + // Main iteration loop for the singular values. + var mn = m; + var iter = 0; - if (x == null) + while (m > 0) { - throw new ArgumentNullException("x"); - } + // Quit if all the singular values have been found. + // If too many iterations have been performed throw exception. + if (iter >= Maxiter) + { + throw new ArgumentException(Resources.ConvergenceFailed); + } - if (y.Length != x.Length) - { - throw new ArgumentException(Resources.ArgumentVectorsSameLength); - } + // 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 mS[m] and e[l-1] are negligible and l < m + // kase = 2: if mS[l] is negligible and l < m + // kase = 3: if e[l-1] is negligible, l < m, and mS[l, ..., mS[m] are not negligible (qr step). + // kase = 4: if e[m-1] is negligible (convergence). + double ztest; + double test; + for (l = m - 2; l >= 0; l--) + { + test = Math.Abs(stemp[l]) + Math.Abs(stemp[l + 1]); + ztest = test + Math.Abs(e[l]); + if (ztest.AlmostEqualInDecimalPlaces(test, 7)) + { + e[l] = 0.0f; + break; + } + } - return y.Select((t, i) => t * x[i]).Sum(); - } + int kase; + if (l == m - 2) + { + kase = 4; + } + else + { + int ls; + for (ls = m - 1; ls > l; ls--) + { + test = 0.0; + if (ls != m - 1) + { + test = test + Math.Abs(e[ls]); + } - /// - /// Does a point wise add of two arrays z = x + y. This can be used - /// to add vectors or matrices. - /// - /// The array x. - /// The array y. - /// The result of the addition. - /// There is no equivalent BLAS routine, but many libraries - /// provide optimized (parallel and/or vectorized) versions of this - /// routine. - public void AddArrays(float[] x, float[] y, float[] result) - { - if (y == null) - { - throw new ArgumentNullException("y"); - } + if (ls != l + 1) + { + test = test + Math.Abs(e[ls - 1]); + } - if (x == null) - { - throw new ArgumentNullException("x"); - } + ztest = test + Math.Abs(stemp[ls]); + if (ztest.AlmostEqualInDecimalPlaces(test, 7)) + { + stemp[ls] = 0.0f; + break; + } + } - if (result == null) - { - throw new ArgumentNullException("result"); - } + if (ls == l) + { + kase = 3; + } + else if (ls == m - 1) + { + kase = 1; + } + else + { + kase = 2; + l = ls; + } + } - if (y.Length != x.Length || y.Length != result.Length) - { - throw new ArgumentException(Resources.ArgumentVectorsSameLength); - } + l = l + 1; - CommonParallel.For(0, y.Length, i => result[i] = x[i] + y[i]); - } + // Perform the task indicated by kase. + int k; + float f; + switch (kase) + { + // Deflate negligible s[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 = stemp[k]; - /// - /// Does a point wise subtraction of two arrays z = x - y. This can be used - /// to subtract vectors or matrices. - /// - /// The array x. - /// The array y. - /// The result of the subtraction. - /// There is no equivalent BLAS routine, but many libraries - /// provide optimized (parallel and/or vectorized) versions of this - /// routine. - public void SubtractArrays(float[] x, float[] y, float[] result) - { - if (y == null) - { - throw new ArgumentNullException("y"); - } + Drotg(ref t1, ref f, ref cs, ref sn); + stemp[k] = t1; + if (k != l) + { + f = -sn * e[k - 1]; + e[k - 1] = cs * e[k - 1]; + } - if (x == null) - { - throw new ArgumentNullException("x"); - } + if (computeVectors) + { + // Rotate + for (i = 0; i < columnsA; i++) + { + var z = (cs * v[(k * columnsA) + i]) + (sn * v[((m - 1) * columnsA) + i]); + v[((m - 1) * columnsA) + i] = (cs * v[((m - 1) * columnsA) + i]) - (sn * v[(k * columnsA) + i]); + v[(k * columnsA) + i] = z; + } + } + } - if (result == null) - { - throw new ArgumentNullException("result"); - } + break; - if (y.Length != x.Length || y.Length != result.Length) - { - throw new ArgumentException(Resources.ArgumentVectorsSameLength); - } + // Split at negligible s[l]. + case 2: + f = e[l - 1]; + e[l - 1] = 0.0f; + for (k = l; k < m; k++) + { + t1 = stemp[k]; + Drotg(ref t1, ref f, ref cs, ref sn); + stemp[k] = t1; + f = -sn * e[k]; + e[k] = cs * e[k]; + if (computeVectors) + { + // Rotate + for (i = 0; i < rowsA; i++) + { + var z = (cs * u[(k * rowsA) + i]) + (sn * u[((l - 1) * rowsA) + i]); + u[((l - 1) * rowsA) + i] = (cs * u[((l - 1) * rowsA) + i]) - (sn * u[(k * rowsA) + i]); + u[(k * rowsA) + i] = z; + } + } + } - CommonParallel.For(0, y.Length, i => result[i] = x[i] - y[i]); - } + break; - /// - /// Does a point wise multiplication of two arrays z = x * y. This can be used - /// to multiple elements of vectors or matrices. - /// - /// The array x. - /// The array y. - /// The result of the point wise multiplication. - /// There is no equivalent BLAS routine, but many libraries - /// provide optimized (parallel and/or vectorized) versions of this - /// routine. - public void PointWiseMultiplyArrays(float[] x, float[] y, float[] result) - { - if (y == null) - { - throw new ArgumentNullException("y"); - } + // Perform one qr step. + case 3: - if (x == null) - { - throw new ArgumentNullException("x"); - } + // calculate the shift. + var scale = 0.0f; + scale = Math.Max(scale, Math.Abs(stemp[m - 1])); + scale = Math.Max(scale, Math.Abs(stemp[m - 2])); + scale = Math.Max(scale, Math.Abs(e[m - 2])); + scale = Math.Max(scale, Math.Abs(stemp[l])); + scale = Math.Max(scale, Math.Abs(e[l])); + var sm = stemp[m - 1] / scale; + var smm1 = stemp[m - 2] / scale; + var emm1 = e[m - 2] / scale; + var sl = stemp[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; + } - if (result == null) - { - throw new ArgumentNullException("result"); - } + shift = c / (b + shift); + } - if (y.Length != x.Length || y.Length != result.Length) - { - throw new ArgumentException(Resources.ArgumentVectorsSameLength); - } + f = ((sl + sm) * (sl - sm)) + shift; + var g = sl * el; - CommonParallel.For(0, y.Length, i => result[i] = x[i] * y[i]); - } + // 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; + } - public float MatrixNorm(Norm norm, int rows, int columns, float[] matrix) - { - throw new NotImplementedException(); - } + f = (cs * stemp[k]) + (sn * e[k]); + e[k] = (cs * e[k]) - (sn * stemp[k]); + g = sn * stemp[k + 1]; + stemp[k + 1] = cs * stemp[k + 1]; + if (computeVectors) + { + for (i = 0; i < columnsA; i++) + { + var z = (cs * v[(k * columnsA) + i]) + (sn * v[((k + 1) * columnsA) + i]); + v[((k + 1) * columnsA) + i] = (cs * v[((k + 1) * columnsA) + i]) - (sn * v[(k * columnsA) + i]); + v[(k * columnsA) + i] = z; + } + } - public float MatrixNorm(Norm norm, int rows, int columns, float[] matrix, float[] work) - { - throw new NotImplementedException(); - } + Drotg(ref f, ref g, ref cs, ref sn); + stemp[k] = f; + f = (cs * e[k]) + (sn * stemp[k + 1]); + stemp[k + 1] = -(sn * e[k]) + (cs * stemp[k + 1]); + g = sn * e[k + 1]; + e[k + 1] = cs * e[k + 1]; + if (computeVectors && k < rowsA) + { + for (i = 0; i < rowsA; i++) + { + var z = (cs * u[(k * rowsA) + i]) + (sn * u[((k + 1) * rowsA) + i]); + u[((k + 1) * rowsA) + i] = (cs * u[((k + 1) * rowsA) + i]) - (sn * u[(k * rowsA) + i]); + u[(k * rowsA) + i] = z; + } + } + } - /// - /// Multiples two matrices. result = x * y - /// - /// The x matrix. - /// The number of rows in the x matrix. - /// The number of columns in the x matrix. - /// The y matrix. - /// The number of rows in the y matrix. - /// The number of columns in the y matrix. - /// Where to store the result of the multiplication. - /// This is a simplified version of the BLAS GEMM routine with alpha - /// set to 1.0 and beta set to 0.0, and x and y are not transposed. - public void MatrixMultiply(float[] x, int xRows, int xColumns, float[] y, int yRows, int yColumns, float[] result) - { - // First check some basic requirement on the parameters of the matrix multiplication. - if (x == null) - { - throw new ArgumentNullException("x"); - } + e[m - 2] = f; + iter = iter + 1; + break; - if (y == null) - { - throw new ArgumentNullException("y"); - } + // Convergence + case 4: - if (result == null) - { - throw new ArgumentNullException("result"); - } + // Make the singular value positive + if (stemp[l] < 0.0) + { + stemp[l] = -stemp[l]; + if (computeVectors) + { + // A part of column "l" of matrix VT from row 0 to end multiply by -1 + for (i = 0; i < columnsA; i++) + { + v[(l * columnsA) + i] = v[(l * columnsA) + i] * -1.0f; + } + } + } - if (xRows * xColumns != x.Length) - { - throw new ArgumentException("x.Length != xRows * xColumns"); - } + // Order the singular value. + while (l != mn - 1) + { + if (stemp[l] >= stemp[l + 1]) + { + break; + } - if (yRows * yColumns != y.Length) - { - throw new ArgumentException("y.Length != yRows * yColumns"); - } + t = stemp[l]; + stemp[l] = stemp[l + 1]; + stemp[l + 1] = t; + if (computeVectors && l < columnsA) + { + // Swap columns l, l + 1 + for (i = 0; i < columnsA; i++) + { + var z = v[(l * columnsA) + i]; + v[(l * columnsA) + i] = v[((l + 1) * columnsA) + i]; + v[((l + 1) * columnsA) + i] = z; + } + } - if (xColumns != yRows) - { - throw new ArgumentException("xColumns != yRows"); - } + if (computeVectors && l < rowsA) + { + // Swap columns l, l + 1 + for (i = 0; i < rowsA; i++) + { + var z = u[(l * rowsA) + i]; + u[(l * rowsA) + i] = u[((l + 1) * rowsA) + i]; + u[((l + 1) * rowsA) + i] = z; + } + } - if (xRows * yColumns != result.Length) - { - throw new ArgumentException("xRows * yColumns != result.Length"); - } + l = l + 1; + } - // Check whether we will be overwriting any of our inputs and make copies if necessary. - // TODO - we can don't have to allocate a completely new matrix when x or y point to the same memory - // as result, we can do it on a row wise basis. We should investigate this. - float[] xdata; - if (ReferenceEquals(x, result)) - { - xdata = (float[])x.Clone(); - } - else - { - xdata = x; + iter = 0; + m = m - 1; + break; + } } - float[] ydata; - if (ReferenceEquals(y, result)) - { - ydata = (float[])y.Clone(); - } - else + if (computeVectors) { - ydata = y; + // Finally transpose "v" to get "vt" matrix + for (i = 0; i < columnsA; i++) + { + for (j = 0; j < columnsA; j++) + { + vt[(j * columnsA) + i] = v[(i * columnsA) + j]; + } + } } - // Start the actual matrix multiplication. - // TODO - For small matrices we should get rid of the parallelism because of startup costs. - // Perhaps the following implementations would be a good one - // http://blog.feradz.com/2009/01/cache-efficient-matrix-multiplication/ - MatrixMultiplyWithUpdate(Transpose.DontTranspose, Transpose.DontTranspose, 1.0f, xdata, xRows, xColumns, ydata, yRows, yColumns, 0.0f, result); + // Copy stemp to s with size adjustment. We are using ported copy of linpack's svd code and it uses + // a singular vector of length rows+1 when rows < columns. The last element is not used and needs to be removed. + // We should port lapack's svd routine to remove this problem. + Buffer.BlockCopy(stemp, 0, s, 0, Math.Min(rowsA, columnsA) * Constants.SizeOfFloat); + + // On return the first element of the work array stores the min size of the work array could have been + // work[0] = Math.Max(3 * Math.Min(aRows, aColumns) + Math.Max(aRows, aColumns), 5 * Math.Min(aRows, aColumns)); + work[0] = rowsA; } /// - /// Multiplies two matrices and updates another with the result. c = alpha*op(a)*op(b) + beta*c + /// 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. /// - /// How to transpose the matrix. - /// How to transpose the matrix. - /// The value to scale matrix. - /// The a matrix. - /// The number of rows in the matrix. - /// The number of columns in the matrix. - /// The b matrix - /// The number of rows in the matrix. - /// The number of columns in the matrix. - /// The value to scale the matrix. - /// The c matrix. - public void MatrixMultiplyWithUpdate(Transpose transposeA, Transpose transposeB, float alpha, float[] a, - int aRows, int aColumns, float[] b, int bRows, int bColumns, float beta, float[] c) + /// Provides the x-coordinate of the point p. On exit contains the parameter r associated with the Givens rotation + /// Provides the y-coordinate of the point p. On exit contains the parameter z associated with the Givens rotation + /// Contains the parameter c associated with the Givens rotation + /// Contains the parameter s associated with the Givens rotation + /// This is equivalent to the DROTG LAPACK routine. + private static void Drotg(ref float da, ref float db, ref float c, ref float s) { - // Choose nonsensical values for the number of rows and columns in c; fill them in depending - // on the operations on a and b. - var cRows = -1; - var cColumns = -1; + float r, z; - // First check some basic requirement on the parameters of the matrix multiplication. - if (a == null) + var roe = db; + var absda = Math.Abs(da); + var absdb = Math.Abs(db); + if (absda > absdb) { - throw new ArgumentNullException("a"); + roe = da; } - if (b == null) + var scale = absda + absdb; + if (scale == 0.0) { - throw new ArgumentNullException("b"); + c = 1.0f; + s = 0.0f; + r = 0.0f; + z = 0.0f; } - - if ((int)transposeA > 111 && (int)transposeB > 111) + else { - if (aRows != bColumns) - { - throw new ArgumentOutOfRangeException(); - } - - if (aColumns * bRows != c.Length) + var sda = da / scale; + var sdb = db / scale; + r = scale * (float)Math.Sqrt((sda * sda) + (sdb * sdb)); + if (roe < 0.0) { - throw new ArgumentOutOfRangeException(); + r = -r; } - cRows = aColumns; - cColumns = bRows; - } - else if ((int)transposeA > 111) - { - if (aRows != bRows) + c = da / r; + s = db / r; + z = 1.0f; + if (absda > absdb) { - throw new ArgumentOutOfRangeException(); + z = s; } - if (aColumns * bColumns != c.Length) + if (absdb >= absda && c != 0.0) { - throw new ArgumentOutOfRangeException(); + z = 1.0f / c; } - - cRows = aColumns; - cColumns = bColumns; } - else if ((int)transposeB > 111) - { - if (aColumns != bColumns) - { - throw new ArgumentOutOfRangeException(); - } - if (aRows * bRows != c.Length) - { - throw new ArgumentOutOfRangeException(); - } + da = r; + db = z; + } - cRows = aRows; - cColumns = bRows; - } - else + /// + /// Solves A*X=B for X using the singular value decomposition of A. + /// + /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. + /// The singular values of A in ascending value. + /// On exit U contains the left singular vectors. + /// On exit VT contains the transposed right singular vectors. + /// The B matrix. + /// The number of columns of B. + /// On exit, the solution matrix. + public void SvdSolve(float[] a, int rowsA, int columnsA, float[] s, float[] u, float[] vt, float[] b, int columnsB, float[] x) + { + if (a == null) { - if (aColumns != bRows) - { - throw new ArgumentOutOfRangeException(); - } - - if (aRows * bColumns != c.Length) - { - throw new ArgumentOutOfRangeException(); - } - - cRows = aRows; - cColumns = bColumns; + throw new ArgumentNullException("a"); } - if (alpha == 0.0 && beta == 0.0) + if (s == null) { - Array.Clear(c, 0, c.Length); - return; + throw new ArgumentNullException("s"); } - - // Check whether we will be overwriting any of our inputs and make copies if necessary. - // TODO - we can don't have to allocate a completely new matrix when x or y point to the same memory - // as result, we can do it on a row wise basis. We should investigate this. - float[] adata; - if (ReferenceEquals(a, c)) + if (u == null) { - adata = (float[])a.Clone(); + throw new ArgumentNullException("u"); } - else + + if (vt == null) { - adata = a; + throw new ArgumentNullException("vt"); } - float[] bdata; - if (ReferenceEquals(b, c)) + if (b == null) { - bdata = (float[])b.Clone(); + throw new ArgumentNullException("b"); } - else + + if (x == null) { - bdata = b; + throw new ArgumentNullException("x"); } - if (alpha == 1.0) + if (u.Length != rowsA * rowsA) { - if (beta == 0.0) - { - if ((int)transposeA > 111 && (int)transposeB > 111) - { - CommonParallel.For(0, aColumns, j => - { - var jIndex = j * cRows; - for (var i = 0; i != bRows; i++) - { - var iIndex = i * aRows; - float s = 0; - for (var l = 0; l != bColumns; l++) - { - s += adata[iIndex + l] * bdata[l * bRows + j]; - } - - c[jIndex + i] = s; - } - }); - } - else if ((int)transposeA > 111) - { - CommonParallel.For(0, bColumns, j => - { - var jcIndex = j * cRows; - var jbIndex = j * bRows; - for (var i = 0; i != aColumns; i++) - { - var iIndex = i * aRows; - float s = 0; - for (var l = 0; l != aRows; l++) - { - s += adata[iIndex + l] * bdata[jbIndex + l]; - } - - c[jcIndex + i] = s; - } - }); - } - else if ((int)transposeB > 111) - { - CommonParallel.For(0, bRows, j => - { - var jIndex = j * cRows; - for (var i = 0; i != aRows; i++) - { - float s = 0; - for (var l = 0; l != aColumns; l++) - { - s += adata[l * aRows + i] * bdata[l * bRows + j]; - } - - c[jIndex + i] = s; - } - }); - } - else - { - CommonParallel.For(0, bColumns, j => - { - var jcIndex = j * cRows; - var jbIndex = j * bRows; - for (var i = 0; i != aRows; i++) - { - float s = 0; - for (var l = 0; l != aColumns; l++) - { - s += adata[l * aRows + i] * bdata[jbIndex + l]; - } - - c[jcIndex + i] = s; - } - }); - } - } - else - { - if ((int)transposeA > 111 && (int)transposeB > 111) - { - CommonParallel.For(0, aColumns, j => - { - var jIndex = j * cRows; - for (var i = 0; i != bRows; i++) - { - var iIndex = i * aRows; - float s = 0; - for (var l = 0; l != bColumns; l++) - { - s += adata[iIndex + l] * bdata[l * bRows + j]; - } - - c[jIndex + i] = c[jIndex + i] * beta + s; - } - }); - } - else if ((int)transposeA > 111) - { - CommonParallel.For(0, bColumns, j => - { - var jcIndex = j * cRows; - var jbIndex = j * bRows; - for (var i = 0; i != aColumns; i++) - { - var iIndex = i * aRows; - float s = 0; - for (var l = 0; l != aRows; l++) - { - s += adata[iIndex + l] * bdata[jbIndex + l]; - } - - c[jcIndex + i] = s + c[jcIndex + i] * beta; - } - }); - } - else if ((int)transposeB > 111) - { - CommonParallel.For(0, bRows, j => - { - var jIndex = j * cRows; - for (var i = 0; i != aRows; i++) - { - float s = 0; - for (var l = 0; l != aColumns; l++) - { - s += adata[l * aRows + i] * bdata[l * bRows + j]; - } - - c[jIndex + i] = s + c[jIndex + i] * beta; - } - }); - } - else - { - CommonParallel.For(0, bColumns, j => - { - var jcIndex = j * cRows; - var jbIndex = j * bRows; - for (var i = 0; i != aRows; i++) - { - float s = 0; - for (var l = 0; l != aColumns; l++) - { - s += adata[l * aRows + i] * bdata[jbIndex + l]; - } - - c[jcIndex + i] = s + c[jcIndex + i] * beta; - } - }); - } - } + throw new ArgumentException(Resources.ArgumentArraysSameLength, "u"); } - else - { - if ((int)transposeA > 111 && (int)transposeB > 111) - { - CommonParallel.For(0, aColumns, j => - { - var jIndex = j * cRows; - for (var i = 0; i != bRows; i++) - { - var iIndex = i * aRows; - float s = 0; - for (var l = 0; l != bColumns; l++) - { - s += adata[iIndex + l] * bdata[l * bRows + j]; - } - - c[jIndex + i] = c[jIndex + i] * beta + alpha * s; - } - }); - } - else if ((int)transposeA > 111) - { - CommonParallel.For(0, bColumns, j => - { - var jcIndex = j * cRows; - var jbIndex = j * bRows; - for (var i = 0; i != aColumns; i++) - { - var iIndex = i * aRows; - float s = 0; - for (var l = 0; l != aRows; l++) - { - s += adata[iIndex + l] * bdata[jbIndex + l]; - } - - c[jcIndex + i] = alpha * s + c[jcIndex + i] * beta; - } - }); - } - else if ((int)transposeB > 111) - { - CommonParallel.For(0, bRows, j => - { - var jIndex = j * cRows; - for (var i = 0; i != aRows; i++) - { - float s = 0; - for (var l = 0; l != aColumns; l++) - { - s += adata[l * aRows + i] * bdata[l * bRows + j]; - } - - c[jIndex + i] = alpha * s + c[jIndex + i] * beta; - } - }); - } - else - { - CommonParallel.For(0, bColumns, j => - { - var jcIndex = j * cRows; - var jbIndex = j * bRows; - for (var i = 0; i != aRows; i++) - { - float s = 0; - for (var l = 0; l != aColumns; l++) - { - s += adata[l * aRows + i] * bdata[jbIndex + l]; - } - c[jcIndex + i] = alpha * s + c[jcIndex + i] * beta; - } - }); - } + if (vt.Length != columnsA * columnsA) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "vt"); } - } - - /// - /// Computes the LUP factorization of A. P*A = L*U. - /// - /// An by matrix. The matrix is overwritten with the - /// the LU factorization on exit. The lower triangular factor L is stored in under the diagonal of (the diagonal is always 1.0 - /// for the L factor). The upper triangular factor U is stored on and above the diagonal of . - /// The order of the square matrix . - /// On exit, it contains the pivot indices. The size of the array must be . - /// This is equivalent to the GETRF LAPACK routine. - public void LUFactor(float[] data, int order, int[] ipiv) - { - throw new NotImplementedException(); - } - /// - /// Computes the inverse of matrix using LU factorization. - /// - /// The N by N matrix to invert. Contains the inverse On exit. - /// The order of the square matrix . - /// This is equivalent to the GETRF and GETRI LAPACK routines. - public void LUInverse(float[] a, int order) - { - throw new NotImplementedException(); - } - - /// - /// Computes the inverse of a previously factored matrix. - /// - /// The LU factored N by N matrix. Contains the inverse On exit. - /// The order of the square matrix . - /// The pivot indices of . - /// This is equivalent to the GETRI LAPACK routine. - public void LUInverseFactored(float[] a, int order, int[] ipiv) - { - throw new NotImplementedException(); - } - - /// - /// Computes the inverse of matrix using LU factorization. - /// - /// The N by N matrix to invert. Contains the inverse On exit. - /// The order of the square matrix . - /// The work array. The array must have a length of at least N, - /// but should be N*blocksize. The blocksize is machine dependent. On exit, work[0] contains the optimal - /// work size value. - /// This is equivalent to the GETRF and GETRI LAPACK routines. - public void LUInverse(float[] a, int order, float[] work) - { - throw new NotImplementedException(); - } - - /// - /// Computes the inverse of a previously factored matrix. - /// - /// The LU factored N by N matrix. Contains the inverse On exit. - /// The order of the square matrix . - /// The pivot indices of . - /// The work array. The array must have a length of at least N, - /// but should be N*blocksize. The blocksize is machine dependent. On exit, work[0] contains the optimal - /// work size value. - /// This is equivalent to the GETRI LAPACK routine. - public void LUInverseFactored(float[] a, int order, int[] ipiv, float[] work) - { - throw new NotImplementedException(); - } - - public void LUSolve(int columnsOfB, float[] a, int order, float[] b) - { - throw new NotImplementedException(); - } - - public void LUSolveFactored(int columnsOfB, float[] a, int order, int[] ipiv, float[] b) - { - throw new NotImplementedException(); - } + if (s.Length != Math.Min(rowsA, columnsA)) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "s"); + } - public void LUSolve(Transpose transposeA, int columnsOfB, float[] a, int order, float[] b) - { - throw new NotImplementedException(); - } + if (b.Length != rowsA * columnsB) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); + } - public void LUSolveFactored(Transpose transposeA, int columnsOfB, float[] a, int order, int[] ipiv, float[] b) - { - throw new NotImplementedException(); + if (x.Length != columnsA * columnsB) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); + } + + // TODO: Actually "work = new double[aRows]" is acceptable size of work array. I set size proposed in method description + var work = new float[Math.Max((3 * Math.Min(rowsA, columnsA)) + Math.Max(rowsA, columnsA), 5 * Math.Min(rowsA, columnsA))]; + SvdSolve(a, rowsA, columnsA, s, u, vt, b, columnsB, x, work); } /// - /// Computes the Cholesky factorization of A. + /// Solves A*X=B for X using the singular value decomposition of A. /// - /// On entry, a square, positive definite matrix. On exit, the matrix is overwritten with the - /// the Cholesky factorization. - /// The number of rows or columns in the matrix. - /// This is equivalent to the POTRF LAPACK routine. - public void CholeskyFactor(float[] a, int order) + /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. + /// The singular values of A in ascending value. + /// On exit U contains the left singular vectors. + /// On exit VT contains the transposed right singular vectors. + /// The B matrix. + /// The number of columns of B. + /// On exit, the solution matrix. + /// The work array. For real matrices, the work array should be at least + /// Max(3*Min(M, N) + Max(M, N), 5*Min(M,N)). For complex matrices, 2*Min(M, N) + Max(M, N). + /// On exit, work[0] contains the optimal work size value. + public void SvdSolve(float[] a, int rowsA, int columnsA, float[] s, float[] u, float[] vt, float[] b, int columnsB, float[] x, float[] work) { - var factor = new float[a.Length]; + if (a == null) + { + throw new ArgumentNullException("a"); + } - for (var j = 0; j < order; j++) + if (s == null) { - var d = 0.0F; - int index; - for (var k = 0; k < j; k++) - { - var s = 0.0F; - int i; - for (i = 0; i < k; i++) - { - s += factor[i * order + k] * factor[i * order + j]; - } + throw new ArgumentNullException("s"); + } - var tmp = k * order; - index = tmp + j; - factor[index] = s = (a[index] - s) / factor[tmp + k]; - d += s * s; - } + if (u == null) + { + throw new ArgumentNullException("u"); + } - index = j * order + j; - d = a[index] - d; - if (d <= 0.0F) - { - throw new ArgumentException(Resources.ArgumentMatrixPositiveDefinite); - } + if (vt == null) + { + throw new ArgumentNullException("vt"); + } - factor[index] = (float)Math.Sqrt(d); - for (var k = j + 1; k < order; k++) - { - factor[k * order + j] = 0.0F; - } + if (b == null) + { + throw new ArgumentNullException("b"); } - Buffer.BlockCopy(factor, 0, a, 0, factor.Length * Constants.SizeOfFloat); - } + if (x == null) + { + throw new ArgumentNullException("x"); + } - /// - /// Solves A*X=B for X using Cholesky factorization. - /// - /// The square, positive definite matrix A. - /// The number of rows and columns in A. - /// The B matrix. - /// The number of rows in the B matrix. - /// The number of columns in the B matrix. - /// This is equivalent to the POTRF add POTRS LAPACK routines. - public void CholeskySolve(float[] a, int aOrder, float[] b, int bRows, int bColumns) - { - throw new NotImplementedException(); - } + if (u.Length != rowsA * rowsA) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "u"); + } - /// - /// Solves A*X=B for X using a previously factored A matrix. - /// - /// The square, positive definite matrix A. - /// The number of rows and columns in A. - /// The B matrix. - /// 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(float[] a, int aOrder, float[] b, int bRows, int bColumns) - { - throw new NotImplementedException(); - } + if (vt.Length != columnsA * columnsA) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "vt"); + } - /// - /// Computes the QR factorization of A. - /// - /// On entry, it is the M by N A matrix to factor. On exit, - /// it is overwritten with the R matrix of the QR factorization. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. - /// On exit, A M by M matrix that holds the Q matrix of the - /// QR factorization. - /// This is similar to the GEQRF and ORGQR LAPACK routines. - public void QRFactor(float[] r, int rRows, int rColumns, float[] q) - { - throw new NotImplementedException(); - } + if (s.Length != Math.Min(rowsA, columnsA)) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "s"); + } - /// - /// Computes the QR factorization of A. - /// - /// On entry, it is the M by N A matrix to factor. On exit, - /// it is overwritten with the R matrix of the QR factorization. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. - /// On exit, A M by M matrix that holds the Q matrix of the - /// QR factorization. - /// The work array. The array must have a length of at least N, - /// but should be N*blocksize. The blocksize is machine dependent. On exit, work[0] contains the optimal - /// work size value. - /// This is similar to the GEQRF and ORGQR LAPACK routines. - public void QRFactor(float[] r, int rRows, int rColumns, float[] q, float[] work) - { - throw new NotImplementedException(); - } + if (b.Length != rowsA * columnsB) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); + } - /// - /// Solves A*X=B for X using QR factorization of A. - /// - /// On entry, it is the M by N A matrix to factor. On exit, - /// it is overwritten with the R matrix of the QR factorization. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. - /// On exit, A M by M matrix that holds the Q matrix of the - /// QR factorization. - /// The B matrix. - /// The number of columns of B. - /// On exit, the solution matrix. - public void QRSolve(float[] r, int rRows, int rColumns, float[] q, float[] b, int bColumns, float[] x) - { - throw new NotImplementedException(); - } + if (x.Length != columnsA * columnsB) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); + } - /// - /// Solves A*X=B for X using QR factorization of A. - /// - /// On entry, it is the M by N A matrix to factor. On exit, - /// it is overwritten with the R matrix of the QR factorization. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. - /// On exit, A M by M matrix that holds the Q matrix of the - /// QR factorization. - /// The B matrix. - /// The number of columns of B. - /// On exit, the solution matrix. - /// The work array. The array must have a length of at least N, - /// but should be N*blocksize. The blocksize is machine dependent. On exit, work[0] contains the optimal - /// work size value. - public void QRSolve(float[] r, int rRows, int rColumns, float[] q, float[] b, int bColumns, float[] x, float[] work) - { - throw new NotImplementedException(); + if (work.Length == 0) + { + throw new ArgumentException(Resources.ArgumentSingleDimensionArray, "work"); + } + + if (work.Length < rowsA) + { + work[0] = rowsA; + throw new ArgumentException(Resources.WorkArrayTooSmall, "work"); + } + + SingularValueDecomposition(true, a, rowsA, columnsA, s, u, vt, work); + SvdSolveFactored(rowsA, columnsA, s, u, vt, b, columnsB, x); } /// - /// Solves A*X=B for X using a previously QR factored matrix. + /// Solves A*X=B for X using a previously SVD decomposed matrix. /// - /// The Q matrix obtained by calling . - /// The R matrix obtained by calling . - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. + /// The s values returned by . + /// The left singular vectors returned by . + /// The right singular vectors returned by . /// The B matrix. - /// The number of columns of B. + /// The number of columns of B. /// On exit, the solution matrix. - public void QRSolveFactored(float[] q, float[] r, int rRows, int rColumns, float[] b, int bColumns, float[] x) + public void SvdSolveFactored(int rowsA, int columnsA, float[] s, float[] u, float[] vt, float[] b, int columnsB, float[] x) { - throw new NotImplementedException(); - } + if (s == null) + { + throw new ArgumentNullException("s"); + } - /// - /// Computes the singular value decomposition of A. - /// - /// Compute the singular U and VT vectors or not. - /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. - /// The singular values of A in ascending value. - /// If is true, on exit U contains the left - /// singular vectors. - /// If is true, on exit VT contains the transposed - /// right singular vectors. - /// This is equivalent to the GESVD LAPACK routine. - public void SingularValueDecomposition(bool computeVectors, float[] a, int aRows, int aColumns, float[] s, float[] u, float[] vt) - { - throw new NotImplementedException(); - } + if (u == null) + { + throw new ArgumentNullException("u"); + } - /// - /// Computes the singular value decomposition of A. - /// - /// Compute the singular U and VT vectors or not. - /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. - /// The singular values of A in ascending value. - /// If is true, on exit U contains the left - /// singular vectors. - /// If is true, on exit VT contains the transposed - /// right singular vectors. - /// The work array. For real matrices, the work array should be at least - /// Max(3*Min(M, N) + Max(M, N), 5*Min(M,N)). For complex matrices, 2*Min(M, N) + Max(M, N). - /// On exit, work[0] contains the optimal work size value. - /// This is equivalent to the GESVD LAPACK routine. - public void SingularValueDecomposition(bool computeVectors, float[] a, int aRows, int aColumns, float[] s, float[] u, float[] vt, float[] work) - { - throw new NotImplementedException(); - } + if (vt == null) + { + throw new ArgumentNullException("vt"); + } + + if (b == null) + { + throw new ArgumentNullException("b"); + } + + if (x == null) + { + throw new ArgumentNullException("x"); + } + + if (u.Length != rowsA * rowsA) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "u"); + } + + if (vt.Length != columnsA * columnsA) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "vt"); + } + + if (s.Length != Math.Min(rowsA, columnsA)) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "s"); + } + + if (b.Length != rowsA * columnsB) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); + } + + if (x.Length != columnsA * columnsB) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); + } + + var mn = Math.Min(rowsA, columnsA); + var tmp = new float[columnsA]; + + for (var k = 0; k < columnsB; k++) + { + for (var j = 0; j < columnsA; j++) + { + float value = 0; + if (j < mn) + { + for (var i = 0; i < rowsA; i++) + { + value += u[(j * rowsA) + i] * b[(k * rowsA) + i]; + } - /// - /// Solves A*X=B for X using the singular value decomposition of A. - /// - /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. - /// The singular values of A in ascending value. - /// On exit U contains the left singular vectors. - /// On exit VT contains the transposed right singular vectors. - /// The B matrix. - /// The number of columns of B. - /// On exit, the solution matrix. - public void SvdSolve(float[] a, int aRows, int aColumns, float[] s, float[] u, float[] vt, float[] b, int bColumns, float[] x) - { - throw new NotImplementedException(); - } + value /= s[j]; + } - /// - /// Solves A*X=B for X using the singular value decomposition of A. - /// - /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. - /// The singular values of A in ascending value. - /// On exit U contains the left singular vectors. - /// On exit VT contains the transposed right singular vectors. - /// The B matrix. - /// The number of columns of B. - /// On exit, the solution matrix. - /// The work array. For real matrices, the work array should be at least - /// Max(3*Min(M, N) + Max(M, N), 5*Min(M,N)). For complex matrices, 2*Min(M, N) + Max(M, N). - /// On exit, work[0] contains the optimal work size value. - public void SvdSolve(float[] a, int aRows, int aColumns, float[] s, float[] u, float[] vt, float[] b, int bColumns, float[] x, float[] work) - { - throw new NotImplementedException(); - } + tmp[j] = value; + } - /// - /// Solves A*X=B for X using a previously SVD decomposed matrix. - /// - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. - /// The s values returned by . - /// The left singular vectors returned by . - /// The right singular vectors returned by . - /// The B matrix. - /// The number of columns of B. - /// On exit, the solution matrix. - public void SvdSolveFactored(int aRows, int aColumns, float[] s, float[] u, float[] vt, float[] b, int bColumns, float[] x) - { - throw new NotImplementedException(); + for (var j = 0; j < columnsA; j++) + { + float value = 0; + for (var i = 0; i < columnsA; i++) + { + value += vt[(j * columnsA) + i] * tmp[i]; + } + + x[(k * columnsA) + j] = value; + } + } } #endregion @@ -4470,14 +5613,7 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra throw new ArgumentException(Resources.ArgumentVectorsSameLength); } - var d = new Complex(0.0, 0.0); - - for (var i = 0; i < y.Length; i++) - { - d += y[i] * x[i]; - } - - return d; + return CommonParallel.Aggregate(0, y.Length, index => y[index] * x[index]); } /// @@ -4585,11 +5721,33 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra CommonParallel.For(0, y.Length, i => result[i] = x[i] * y[i]); } + /// + /// Computes the requested of the matrix. + /// + /// The type of norm to compute. + /// The number of rows. + /// The number of columns. + /// The matrix to compute the norm from. + /// + /// The requested of the matrix. + /// public Complex MatrixNorm(Norm norm, int rows, int columns, Complex[] matrix) { throw new NotImplementedException(); } + /// + /// Computes the requested of the matrix. + /// + /// The type of norm to compute. + /// The number of rows. + /// The number of columns. + /// The matrix to compute the norm from. + /// The work array. Only used when + /// and needs to be have a length of at least M (number of rows of . + /// + /// The requested of the matrix. + /// public Complex MatrixNorm(Norm norm, int rows, int columns, Complex[] matrix, Complex[] work) { throw new NotImplementedException(); @@ -4599,15 +5757,15 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra /// Multiples two matrices. result = x * y /// /// The x matrix. - /// The number of rows in the x matrix. - /// The number of columns in the x matrix. + /// The number of rows in the x matrix. + /// The number of columns in the x matrix. /// The y matrix. - /// The number of rows in the y matrix. - /// The number of columns in the y matrix. + /// The number of rows in the y matrix. + /// The number of columns in the y matrix. /// Where to store the result of the multiplication. /// This is a simplified version of the BLAS GEMM routine with alpha /// set to 1.0 and beta set to 0.0, and x and y are not transposed. - public void MatrixMultiply(Complex[] x, int xRows, int xColumns, Complex[] y, int yRows, int yColumns, Complex[] result) + public void MatrixMultiply(Complex[] x, int rowsX, int columnsX, Complex[] y, int rowsY, int columnsY, Complex[] result) { // First check some basic requirement on the parameters of the matrix multiplication. if (x == null) @@ -4625,22 +5783,22 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra throw new ArgumentNullException("result"); } - if (xRows * xColumns != x.Length) + if (rowsX * columnsX != x.Length) { throw new ArgumentException("x.Length != xRows * xColumns"); } - if (yRows * yColumns != y.Length) + if (rowsY * columnsY != y.Length) { throw new ArgumentException("y.Length != yRows * yColumns"); } - if (xColumns != yRows) + if (columnsX != rowsY) { throw new ArgumentException("xColumns != yRows"); } - if (xRows * yColumns != result.Length) + if (rowsX * columnsY != result.Length) { throw new ArgumentException("xRows * yColumns != result.Length"); } @@ -4672,7 +5830,7 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra // TODO - For small matrices we should get rid of the parallelism because of startup costs. // Perhaps the following implementations would be a good one // http://blog.feradz.com/2009/01/cache-efficient-matrix-multiplication/ - MatrixMultiplyWithUpdate(Transpose.DontTranspose, Transpose.DontTranspose, Complex.One, xdata, xRows, xColumns, ydata, yRows, yColumns, Complex.Zero, result); + MatrixMultiplyWithUpdate(Transpose.DontTranspose, Transpose.DontTranspose, Complex.One, xdata, rowsX, columnsX, ydata, rowsY, columnsY, Complex.Zero, result); } /// @@ -4682,20 +5840,18 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra /// How to transpose the matrix. /// The value to scale matrix. /// The a matrix. - /// The number of rows in the matrix. - /// The number of columns in the matrix. + /// The number of rows in the matrix. + /// The number of columns in the matrix. /// The b matrix - /// The number of rows in the matrix. - /// The number of columns in the matrix. + /// The number of rows in the matrix. + /// The number of columns in the matrix. /// The value to scale the matrix. /// The c matrix. - public void MatrixMultiplyWithUpdate(Transpose transposeA, Transpose transposeB, Complex alpha, Complex[] a, - int aRows, int aColumns, Complex[] b, int bRows, int bColumns, Complex beta, Complex[] c) + public void MatrixMultiplyWithUpdate(Transpose transposeA, Transpose transposeB, Complex alpha, Complex[] a, int rowsA, int columnsA, Complex[] b, int rowsB, int columnsB, Complex beta, Complex[] c) { - // Choose nonsensical values for the number of rows and columns in c; fill them in depending + // Choose nonsensical values for the number of rows in c; fill them in depending // on the operations on a and b. - var cRows = -1; - var cColumns = -1; + int rowsC; // First check some basic requirement on the parameters of the matrix multiplication. if (a == null) @@ -4710,63 +5866,59 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra if ((int)transposeA > 111 && (int)transposeB > 111) { - if (aRows != bColumns) + if (rowsA != columnsB) { throw new ArgumentOutOfRangeException(); } - if (aColumns * bRows != c.Length) + if (columnsA * rowsB != c.Length) { throw new ArgumentOutOfRangeException(); } - cRows = aColumns; - cColumns = bRows; + rowsC = columnsA; } else if ((int)transposeA > 111) { - if (aRows != bRows) + if (rowsA != rowsB) { throw new ArgumentOutOfRangeException(); } - if (aColumns * bColumns != c.Length) + if (columnsA * columnsB != c.Length) { throw new ArgumentOutOfRangeException(); } - cRows = aColumns; - cColumns = bColumns; + rowsC = columnsA; } else if ((int)transposeB > 111) { - if (aColumns != bColumns) + if (columnsA != columnsB) { throw new ArgumentOutOfRangeException(); } - if (aRows * bRows != c.Length) + if (rowsA * rowsB != c.Length) { throw new ArgumentOutOfRangeException(); } - cRows = aRows; - cColumns = bRows; + rowsC = rowsA; } else { - if (aColumns != bRows) + if (columnsA != rowsB) { throw new ArgumentOutOfRangeException(); } - if (aRows * bColumns != c.Length) + if (rowsA * columnsB != c.Length) { throw new ArgumentOutOfRangeException(); } - cRows = aRows; - cColumns = bColumns; + rowsC = rowsA; } if (alpha == 0.0 && beta == 0.0) @@ -4775,7 +5927,6 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra return; } - // Check whether we will be overwriting any of our inputs and make copies if necessary. // TODO - we can don't have to allocate a completely new matrix when x or y point to the same memory // as result, we can do it on a row wise basis. We should investigate this. @@ -4805,150 +5956,174 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra { if ((int)transposeA > 111 && (int)transposeB > 111) { - CommonParallel.For(0, aColumns, j => - { - var jIndex = j * cRows; - for (var i = 0; i != bRows; i++) - { - var iIndex = i * aRows; - Complex s = 0; - for (var l = 0; l != bColumns; l++) - { - s += adata[iIndex + l] * bdata[l * bRows + j]; - } - - c[jIndex + i] = s; - } - }); + CommonParallel.For( + 0, + columnsA, + j => + { + var jIndex = j * rowsC; + for (var i = 0; i != rowsB; i++) + { + var iIndex = i * rowsA; + Complex s = 0; + for (var l = 0; l != columnsB; l++) + { + s += adata[iIndex + l] * bdata[(l * rowsB) + j]; + } + + c[jIndex + i] = s; + } + }); } else if ((int)transposeA > 111) { - CommonParallel.For(0, bColumns, j => - { - var jcIndex = j * cRows; - var jbIndex = j * bRows; - for (var i = 0; i != aColumns; i++) - { - var iIndex = i * aRows; - Complex s = 0; - for (var l = 0; l != aRows; l++) - { - s += adata[iIndex + l] * bdata[jbIndex + l]; - } - - c[jcIndex + i] = s; - } - }); + CommonParallel.For( + 0, + columnsB, + j => + { + var jcIndex = j * rowsC; + var jbIndex = j * rowsB; + for (var i = 0; i != columnsA; i++) + { + var iIndex = i * rowsA; + Complex s = 0; + for (var l = 0; l != rowsA; l++) + { + s += adata[iIndex + l] * bdata[jbIndex + l]; + } + + c[jcIndex + i] = s; + } + }); } else if ((int)transposeB > 111) { - CommonParallel.For(0, bRows, j => - { - var jIndex = j * cRows; - for (var i = 0; i != aRows; i++) - { - Complex s = 0; - for (var l = 0; l != aColumns; l++) - { - s += adata[l * aRows + i] * bdata[l * bRows + j]; - } - - c[jIndex + i] = s; - } - }); + CommonParallel.For( + 0, + rowsB, + j => + { + var jIndex = j * rowsC; + for (var i = 0; i != rowsA; i++) + { + Complex s = 0; + for (var l = 0; l != columnsA; l++) + { + s += adata[(l * rowsA) + i] * bdata[(l * rowsB) + j]; + } + + c[jIndex + i] = s; + } + }); } else { - CommonParallel.For(0, bColumns, j => - { - var jcIndex = j * cRows; - var jbIndex = j * bRows; - for (var i = 0; i != aRows; i++) - { - Complex s = 0; - for (var l = 0; l != aColumns; l++) - { - s += adata[l * aRows + i] * bdata[jbIndex + l]; - } + CommonParallel.For( + 0, + columnsB, + j => + { + var jcIndex = j * rowsC; + var jbIndex = j * rowsB; + for (var i = 0; i != rowsA; i++) + { + Complex s = 0; + for (var l = 0; l != columnsA; l++) + { + s += adata[(l * rowsA) + i] * bdata[jbIndex + l]; + } - c[jcIndex + i] = s; - } - }); + c[jcIndex + i] = s; + } + }); } } else { if ((int)transposeA > 111 && (int)transposeB > 111) { - CommonParallel.For(0, aColumns, j => - { - var jIndex = j * cRows; - for (var i = 0; i != bRows; i++) - { - var iIndex = i * aRows; - Complex s = 0; - for (var l = 0; l != bColumns; l++) - { - s += adata[iIndex + l] * bdata[l * bRows + j]; - } - - c[jIndex + i] = c[jIndex + i] * beta + s; - } - }); + CommonParallel.For( + 0, + columnsA, + j => + { + var jIndex = j * rowsC; + for (var i = 0; i != rowsB; i++) + { + var iIndex = i * rowsA; + Complex s = 0; + for (var l = 0; l != columnsB; l++) + { + s += adata[iIndex + l] * bdata[(l * rowsB) + j]; + } + + c[jIndex + i] = (c[jIndex + i] * beta) + s; + } + }); } else if ((int)transposeA > 111) { - CommonParallel.For(0, bColumns, j => - { - var jcIndex = j * cRows; - var jbIndex = j * bRows; - for (var i = 0; i != aColumns; i++) - { - var iIndex = i * aRows; - Complex s = 0; - for (var l = 0; l != aRows; l++) - { - s += adata[iIndex + l] * bdata[jbIndex + l]; - } - - c[jcIndex + i] = s + c[jcIndex + i] * beta; - } - }); + CommonParallel.For( + 0, + columnsB, + j => + { + var jcIndex = j * rowsC; + var jbIndex = j * rowsB; + for (var i = 0; i != columnsA; i++) + { + var iIndex = i * rowsA; + Complex s = 0; + for (var l = 0; l != rowsA; l++) + { + s += adata[iIndex + l] * bdata[jbIndex + l]; + } + + c[jcIndex + i] = s + (c[jcIndex + i] * beta); + } + }); } else if ((int)transposeB > 111) { - CommonParallel.For(0, bRows, j => - { - var jIndex = j * cRows; - for (var i = 0; i != aRows; i++) - { - Complex s = 0; - for (var l = 0; l != aColumns; l++) - { - s += adata[l * aRows + i] * bdata[l * bRows + j]; - } - - c[jIndex + i] = s + c[jIndex + i] * beta; - } - }); + CommonParallel.For( + 0, + rowsB, + j => + { + var jIndex = j * rowsC; + for (var i = 0; i != rowsA; i++) + { + Complex s = 0; + for (var l = 0; l != columnsA; l++) + { + s += adata[(l * rowsA) + i] * bdata[(l * rowsB) + j]; + } + + c[jIndex + i] = s + (c[jIndex + i] * beta); + } + }); } else { - CommonParallel.For(0, bColumns, j => - { - var jcIndex = j * cRows; - var jbIndex = j * bRows; - for (var i = 0; i != aRows; i++) - { - Complex s = 0; - for (var l = 0; l != aColumns; l++) - { - s += adata[l * aRows + i] * bdata[jbIndex + l]; - } + CommonParallel.For( + 0, + columnsB, + j => + { + var jcIndex = j * rowsC; + var jbIndex = j * rowsB; + for (var i = 0; i != rowsA; i++) + { + Complex s = 0; + for (var l = 0; l != columnsA; l++) + { + s += adata[(l * rowsA) + i] * bdata[jbIndex + l]; + } - c[jcIndex + i] = s + c[jcIndex + i] * beta; - } - }); + c[jcIndex + i] = s + (c[jcIndex + i] * beta); + } + }); } } } @@ -4956,75 +6131,87 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra { if ((int)transposeA > 111 && (int)transposeB > 111) { - CommonParallel.For(0, aColumns, j => - { - var jIndex = j * cRows; - for (var i = 0; i != bRows; i++) - { - var iIndex = i * aRows; - Complex s = 0; - for (var l = 0; l != bColumns; l++) - { - s += adata[iIndex + l] * bdata[l * bRows + j]; - } - - c[jIndex + i] = c[jIndex + i] * beta + alpha * s; - } - }); + CommonParallel.For( + 0, + columnsA, + j => + { + var jIndex = j * rowsC; + for (var i = 0; i != rowsB; i++) + { + var iIndex = i * rowsA; + Complex s = 0; + for (var l = 0; l != columnsB; l++) + { + s += adata[iIndex + l] * bdata[(l * rowsB) + j]; + } + + c[jIndex + i] = (c[jIndex + i] * beta) + (alpha * s); + } + }); } else if ((int)transposeA > 111) { - CommonParallel.For(0, bColumns, j => - { - var jcIndex = j * cRows; - var jbIndex = j * bRows; - for (var i = 0; i != aColumns; i++) - { - var iIndex = i * aRows; - Complex s = 0; - for (var l = 0; l != aRows; l++) - { - s += adata[iIndex + l] * bdata[jbIndex + l]; - } - - c[jcIndex + i] = alpha * s + c[jcIndex + i] * beta; - } - }); + CommonParallel.For( + 0, + columnsB, + j => + { + var jcIndex = j * rowsC; + var jbIndex = j * rowsB; + for (var i = 0; i != columnsA; i++) + { + var iIndex = i * rowsA; + Complex s = 0; + for (var l = 0; l != rowsA; l++) + { + s += adata[iIndex + l] * bdata[jbIndex + l]; + } + + c[jcIndex + i] = (alpha * s) + (c[jcIndex + i] * beta); + } + }); } else if ((int)transposeB > 111) { - CommonParallel.For(0, bRows, j => - { - var jIndex = j * cRows; - for (var i = 0; i != aRows; i++) - { - Complex s = 0; - for (var l = 0; l != aColumns; l++) - { - s += adata[l * aRows + i] * bdata[l * bRows + j]; - } - - c[jIndex + i] = alpha * s + c[jIndex + i] * beta; - } - }); + CommonParallel.For( + 0, + rowsB, + j => + { + var jIndex = j * rowsC; + for (var i = 0; i != rowsA; i++) + { + Complex s = 0; + for (var l = 0; l != columnsA; l++) + { + s += adata[(l * rowsA) + i] * bdata[(l * rowsB) + j]; + } + + c[jIndex + i] = (alpha * s) + (c[jIndex + i] * beta); + } + }); } else { - CommonParallel.For(0, bColumns, j => - { - var jcIndex = j * cRows; - var jbIndex = j * bRows; - for (var i = 0; i != aRows; i++) - { - Complex s = 0; - for (var l = 0; l != aColumns; l++) - { - s += adata[l * aRows + i] * bdata[jbIndex + l]; - } + CommonParallel.For( + 0, + columnsB, + j => + { + var jcIndex = j * rowsC; + var jbIndex = j * rowsB; + for (var i = 0; i != rowsA; i++) + { + Complex s = 0; + for (var l = 0; l != columnsA; l++) + { + s += adata[(l * rowsA) + i] * bdata[jbIndex + l]; + } - c[jcIndex + i] = alpha * s + c[jcIndex + i] * beta; - } - }); + c[jcIndex + i] = (alpha * s) + (c[jcIndex + i] * beta); + } + }); } } } @@ -5066,7 +6253,7 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra ipiv[i] = i; } - var vLUcolj = new Complex[order]; + var vecLUcolj = new Complex[order]; // Outer loop. for (var j = 0; j < order; j++) @@ -5077,7 +6264,7 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra // Make a copy of the j-th column to localize references. for (var i = 0; i < order; i++) { - vLUcolj[i] = data[indexj + i]; + vecLUcolj[i] = data[indexj + i]; } // Apply previous transformations. @@ -5088,17 +6275,17 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra var s = Complex.Zero; for (var k = 0; k < kmax; k++) { - s += data[k * order + i] * vLUcolj[k]; + s += data[(k * order) + i] * vecLUcolj[k]; } - data[indexj + i] = vLUcolj[i] -= s; + data[indexj + i] = vecLUcolj[i] -= s; } // Find pivot and exchange if necessary. var p = j; for (var i = j + 1; i < order; i++) { - if (vLUcolj[i].Magnitude > vLUcolj[p].Magnitude) + if (vecLUcolj[i].Magnitude > vecLUcolj[p].Magnitude) { p = i; } @@ -5185,7 +6372,7 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra var inverse = new Complex[a.Length]; for (var i = 0; i < order; i++) { - inverse[i + order * i] = Complex.One; + inverse[i + (order * i)] = Complex.One; } LUSolveFactored(order, a, order, ipiv, inverse); @@ -5334,10 +6521,10 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra // Solve U*X = Y; for (var k = order - 1; k >= 0; k--) { - var korder = k + k * order; + var korder = k + (k * order); for (var j = 0; j < columnsOfB; j++) { - b[k + j * order] /= a[korder]; + b[k + (j * order)] /= a[korder]; } korder = k * order; @@ -5350,7 +6537,6 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra } } } - } /// @@ -5486,7 +6672,7 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra var s = Complex.Zero; for (var k = 0; k < j; k++) { - s += a[k * order + i] * a[k * order + j].Conjugate(); + s += a[(k * order) + i] * a[(k * order) + j].Conjugate(); } var tmp = j * order; @@ -5495,7 +6681,7 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra d += s * s.Conjugate(); } - index = i * order + i; + index = (i * order) + i; d = a[index] - d; if (d.Real <= 0.0) { @@ -5505,7 +6691,7 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra a[index] = d.SquareRoot(); for (var k = i + 1; k < order; k++) { - a[k * order + i] = 0.0; + a[(k * order) + i] = 0.0; } } } @@ -5514,12 +6700,12 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra /// Solves A*X=B for X using Cholesky factorization. /// /// The square, positive definite matrix A. - /// The number of rows and columns in A. + /// The number of rows and columns in A. /// The B matrix. - /// The number of rows in the B matrix. - /// The number of columns in the B matrix. + /// The number of rows in the B matrix. + /// The number of columns in the B matrix. /// This is equivalent to the POTRF add POTRS LAPACK routines. - public void CholeskySolve(Complex[] a, int aOrder, Complex[] b, int bRows, int bColumns) + public void CholeskySolve(Complex[] a, int orderA, Complex[] b, int rowsB, int columnsB) { if (a == null) { @@ -5531,7 +6717,7 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra throw new ArgumentNullException("b"); } - if (aOrder != bRows) + if (orderA != rowsB) { throw new ArgumentException(Resources.ArgumentMatrixDimensions); } @@ -5541,20 +6727,20 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra throw new ArgumentException(Resources.ArgumentReferenceDifferent); } - CholeskyFactor(a, aOrder); - CholeskySolveFactored(a, aOrder, b, bRows, bColumns); + CholeskyFactor(a, orderA); + CholeskySolveFactored(a, orderA, b, rowsB, columnsB); } /// /// Solves A*X=B for X using a previously factored A matrix. /// /// The square, positive definite matrix A. - /// The number of rows and columns in A. + /// The number of rows and columns in A. /// The B matrix. - /// The number of rows in the B matrix. - /// The number of columns in the B matrix. + /// 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(Complex[] a, int aOrder, Complex[] b, int bRows, int bColumns) + public void CholeskySolveFactored(Complex[] a, int orderA, Complex[] b, int rowsB, int columnsB) { if (a == null) { @@ -5566,7 +6752,7 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra throw new ArgumentNullException("b"); } - if (aOrder != bRows) + if (orderA != rowsB) { throw new ArgumentException(Resources.ArgumentMatrixDimensions); } @@ -5576,36 +6762,39 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra throw new ArgumentException(Resources.ArgumentReferenceDifferent); } - CommonParallel.For(0, bColumns, c => - { - var cindex = c * aOrder; - - // Solve L*Y = B; - Complex sum; - for (var i = 0; i < aOrder; i++) + CommonParallel.For( + 0, + columnsB, + c => { - sum = b[cindex + i]; - for (var k = i - 1; k >= 0; k--) + var cindex = c * orderA; + + // Solve L*Y = B; + Complex sum; + for (var i = 0; i < orderA; i++) { - sum -= a[k * aOrder + i] * b[cindex + k]; - } + sum = b[cindex + i]; + for (var k = i - 1; k >= 0; k--) + { + sum -= a[(k * orderA) + i] * b[cindex + k]; + } - b[cindex + i] = sum / a[i * aOrder + i]; - } + b[cindex + i] = sum / a[(i * orderA) + i]; + } - // Solve L'*X = Y; - for (var i = aOrder - 1; i >= 0; i--) - { - sum = b[cindex + i]; - var iindex = i * aOrder; - for (var k = i + 1; k < aOrder; k++) + // Solve L'*X = Y; + for (var i = orderA - 1; i >= 0; i--) { - sum -= a[iindex + k].Conjugate() * b[cindex + k]; - } + sum = b[cindex + i]; + var iindex = i * orderA; + for (var k = i + 1; k < orderA; k++) + { + sum -= a[iindex + k].Conjugate() * b[cindex + k]; + } - b[cindex + i] = sum / a[iindex + i]; - } - }); + b[cindex + i] = sum / a[iindex + i]; + } + }); } /// @@ -5613,12 +6802,12 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra /// /// On entry, it is the M by N A matrix to factor. On exit, /// it is overwritten with the R matrix of the QR factorization. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// On exit, A M by M matrix that holds the Q matrix of the /// QR factorization. /// This is similar to the GEQRF and ORGQR LAPACK routines. - public void QRFactor(Complex[] r, int rRows, int rColumns, Complex[] q) + public void QRFactor(Complex[] r, int rowsR, int columnsR, Complex[] q) { if (r == null) { @@ -5630,18 +6819,18 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra throw new ArgumentNullException("q"); } - if (r.Length != rRows * rColumns) + if (r.Length != rowsR * columnsR) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "r"); } - if (q.Length != rRows * rRows) + if (q.Length != rowsR * rowsR) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "q"); } - var work = new Complex[rRows * rRows]; - QRFactor(r, rRows, rColumns, q, work); + var work = new Complex[rowsR * rowsR]; + QRFactor(r, rowsR, columnsR, q, work); } /// @@ -5649,15 +6838,15 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra /// /// On entry, it is the M by N A matrix to factor. On exit, /// it is overwritten with the R matrix of the QR factorization. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// On exit, A M by M matrix that holds the Q matrix of the /// QR factorization. /// The work array. The array must have a length of at least N, /// but should be N*blocksize. The blocksize is machine dependent. On exit, work[0] contains the optimal /// work size value. /// This is similar to the GEQRF and ORGQR LAPACK routines. - public void QRFactor(Complex[] r, int rRows, int rColumns, Complex[] q, Complex[] work) + public void QRFactor(Complex[] r, int rowsR, int columnsR, Complex[] q, Complex[] work) { if (r == null) { @@ -5674,37 +6863,37 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra throw new ArgumentNullException("q"); } - if (r.Length != rRows * rColumns) + if (r.Length != rowsR * columnsR) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "r"); } - if (q.Length != rRows * rRows) + if (q.Length != rowsR * rowsR) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "q"); } - if (work.Length < rRows * rRows) + if (work.Length < rowsR * rowsR) { - work[0] = rRows * rRows; + work[0] = rowsR * rowsR; throw new ArgumentException(Resources.WorkArrayTooSmall, "work"); } - CommonParallel.For(0, rRows, i => q[(i * rRows) + i] = Complex.One); + CommonParallel.For(0, rowsR, i => q[(i * rowsR) + i] = Complex.One); - var minmn = Math.Min(rRows, rColumns); + var minmn = Math.Min(rowsR, columnsR); for (var i = 0; i < minmn; i++) { - GenerateColumn(work, r, rRows, i, rRows - 1, i); - ComputeQR(work, i, r, rRows, i, rRows - 1, i + 1, rColumns - 1); + GenerateColumn(work, r, rowsR, i, rowsR - 1, i); + ComputeQR(work, i, r, rowsR, i, rowsR - 1, i + 1, columnsR - 1); } for (var i = minmn - 1; i >= 0; i--) { - ComputeQR(work, i, q, rRows, i, rRows - 1, i, rRows - 1); + ComputeQR(work, i, q, rowsR, i, rowsR - 1, i, rowsR - 1); } - work[0] = rRows * rRows; + work[0] = rowsR * rowsR; } #region QR Factor Helper functions @@ -5772,8 +6961,8 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra var norm = Complex.Zero; for (var i = 0; i < rowEnd - rowStart + 1; ++i) { - var iIndex = tmp + i; - norm += work[iIndex].Magnitude * work[iIndex].Magnitude; + var index1 = tmp + i; + norm += work[index1].Magnitude * work[index1].Magnitude; } norm = norm.SquareRoot(); @@ -5804,14 +6993,14 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra /// /// On entry, it is the M by N A matrix to factor. On exit, /// it is overwritten with the R matrix of the QR factorization. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// On exit, A M by M matrix that holds the Q matrix of the /// QR factorization. /// The B matrix. - /// The number of columns of B. + /// The number of columns of B. /// On exit, the solution matrix. - public void QRSolve(Complex[] r, int rRows, int rColumns, Complex[] q, Complex[] b, int bColumns, Complex[] x) + public void QRSolve(Complex[] r, int rowsR, int columnsR, Complex[] q, Complex[] b, int columnsB, Complex[] x) { if (r == null) { @@ -5833,28 +7022,28 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra throw new ArgumentNullException("q"); } - if (r.Length != rRows * rColumns) + if (r.Length != rowsR * columnsR) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "r"); } - if (q.Length != rRows * rRows) + if (q.Length != rowsR * rowsR) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "q"); } - if (b.Length != rRows * bColumns) + if (b.Length != rowsR * columnsB) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); } - if (x.Length != rColumns * bColumns) + if (x.Length != columnsR * columnsB) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "x"); } - var work = new Complex[rRows * rRows]; - QRSolve(r, rRows, rColumns, q, b, bColumns, x, work); + var work = new Complex[rowsR * rowsR]; + QRSolve(r, rowsR, columnsR, q, b, columnsB, x, work); } /// @@ -5862,17 +7051,17 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra /// /// On entry, it is the M by N A matrix to factor. On exit, /// it is overwritten with the R matrix of the QR factorization. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// On exit, A M by M matrix that holds the Q matrix of the /// QR factorization. /// The B matrix. - /// The number of columns of B. + /// The number of columns of B. /// On exit, the solution matrix. /// The work array. The array must have a length of at least N, /// but should be N*blocksize. The blocksize is machine dependent. On exit, work[0] contains the optimal /// work size value. - public void QRSolve(Complex[] r, int rRows, int rColumns, Complex[] q, Complex[] b, int bColumns, Complex[] x, Complex[] work) + public void QRSolve(Complex[] r, int rowsR, int columnsR, Complex[] q, Complex[] b, int columnsB, Complex[] x, Complex[] work) { if (r == null) { @@ -5894,36 +7083,36 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra throw new ArgumentNullException("q"); } - if (r.Length != rRows * rColumns) + if (r.Length != rowsR * columnsR) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "r"); } - if (q.Length != rRows * rRows) + if (q.Length != rowsR * rowsR) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "q"); } - if (b.Length != rRows * bColumns) + if (b.Length != rowsR * columnsB) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); } - if (x.Length != rColumns * bColumns) + if (x.Length != columnsR * columnsB) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "x"); } - if (work.Length < rRows * rRows) + if (work.Length < rowsR * rowsR) { - work[0] = rRows * rRows; + work[0] = rowsR * rowsR; throw new ArgumentException(Resources.WorkArrayTooSmall, "work"); } - QRFactor(r, rRows, rColumns, q, work); - QRSolveFactored(q, r, rRows, rColumns, b, bColumns, x); + QRFactor(r, rowsR, columnsR, q, work); + QRSolveFactored(q, r, rowsR, columnsR, b, columnsB, x); - work[0] = rRows * rRows; + work[0] = rowsR * rowsR; } /// @@ -5931,12 +7120,12 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra /// /// The Q matrix obtained by calling . /// The R matrix obtained by calling . - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// The B matrix. - /// The number of columns of B. + /// The number of columns of B. /// On exit, the solution matrix. - public void QRSolveFactored(Complex[] q, Complex[] r, int rRows, int rColumns, Complex[] b, int bColumns, Complex[] x) + public void QRSolveFactored(Complex[] q, Complex[] r, int rowsR, int columnsR, Complex[] b, int columnsB, Complex[] x) { if (r == null) { @@ -5958,22 +7147,22 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra throw new ArgumentNullException("q"); } - if (r.Length != rRows * rColumns) + if (r.Length != rowsR * columnsR) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "r"); } - if (q.Length != rRows * rRows) + if (q.Length != rowsR * rowsR) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "q"); } - if (b.Length != rRows * bColumns) + if (b.Length != rowsR * columnsB) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); } - if (x.Length != rColumns * bColumns) + if (x.Length != columnsR * columnsB) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "x"); } @@ -5984,35 +7173,35 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra CommonParallel.For(0, b.Length, index => sol[index] = b[index]); // Compute Y = transpose(Q)*B - var column = new Complex[rRows]; - for (var j = 0; j < bColumns; j++) + var column = new Complex[rowsR]; + for (var j = 0; j < columnsB; j++) { - var jm = j * rRows; - CommonParallel.For(0, rRows, k => column[k] = sol[jm + k]); + var jm = j * rowsR; + CommonParallel.For(0, rowsR, k => column[k] = sol[jm + k]); CommonParallel.For( 0, - rRows, + rowsR, i => { - var im = i * rRows; - sol[jm + i] = CommonParallel.Aggregate(0, rRows, k => q[im + k].Conjugate() * column[k]); + var im = i * rowsR; + sol[jm + i] = CommonParallel.Aggregate(0, rowsR, k => q[im + k].Conjugate() * column[k]); }); } // Solve R*X = Y; - for (var k = rColumns - 1; k >= 0; k--) + for (var k = columnsR - 1; k >= 0; k--) { - var km = k * rRows; - for (var j = 0; j < bColumns; j++) + var km = k * rowsR; + for (var j = 0; j < columnsB; j++) { - sol[(j * rRows) + k] /= r[km + k]; + sol[(j * rowsR) + k] /= r[km + k]; } for (var i = 0; i < k; i++) { - for (var j = 0; j < bColumns; j++) + for (var j = 0; j < columnsB; j++) { - var jm = j * rRows; + var jm = j * rowsR; sol[jm + i] -= sol[jm + k] * r[km + i]; } } @@ -6021,12 +7210,12 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra // Fill result matrix CommonParallel.For( 0, - rColumns, + columnsR, row => { - for (var col = 0; col < bColumns; col++) + for (var col = 0; col < columnsB; col++) { - x[(col * rColumns) + row] = sol[row + (col * rRows)]; + x[(col * columnsR) + row] = sol[row + (col * rowsR)]; } }); } @@ -6036,15 +7225,15 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra /// /// Compute the singular U and VT vectors or not. /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// The singular values of A in ascending value. /// If is true, on exit U contains the left /// singular vectors. /// If is true, on exit VT contains the transposed /// right singular vectors. /// This is equivalent to the GESVD LAPACK routine. - public void SingularValueDecomposition(bool computeVectors, Complex[] a, int aRows, int aColumns, Complex[] s, Complex[] u, Complex[] vt) + public void SingularValueDecomposition(bool computeVectors, Complex[] a, int rowsA, int columnsA, Complex[] s, Complex[] u, Complex[] vt) { if (a == null) { @@ -6066,25 +7255,24 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra throw new ArgumentNullException("vt"); } - if (u.Length != aRows * aRows) + if (u.Length != rowsA * rowsA) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "u"); } - if (vt.Length != aColumns * aColumns) + if (vt.Length != columnsA * columnsA) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "vt"); } - if (s.Length != Math.Min(aRows, aColumns)) + if (s.Length != Math.Min(rowsA, columnsA)) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "s"); } // TODO: Actually "work = new double[aRows]" is acceptable size of work array. I set size proposed in method description - var work = new Complex[Math.Max((3 * Math.Min(aRows, aColumns)) + Math.Max(aRows, aColumns), 5 * Math.Min(aRows, aColumns))]; - SingularValueDecomposition(computeVectors, a, aRows, aColumns, s, u, vt, work); - + var work = new Complex[(2 * Math.Min(rowsA, columnsA)) + Math.Max(rowsA, columnsA)]; + SingularValueDecomposition(computeVectors, a, rowsA, columnsA, s, u, vt, work); } /// @@ -6092,8 +7280,8 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra /// /// Compute the singular U and VT vectors or not. /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// The singular values of A in ascending value. /// If is true, on exit U contains the left /// singular vectors. @@ -6103,7 +7291,7 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra /// Max(3*Min(M, N) + Max(M, N), 5*Min(M,N)). For complex matrices, 2*Min(M, N) + Max(M, N). /// On exit, work[0] contains the optimal work size value. /// This is equivalent to the GESVD LAPACK routine. - public void SingularValueDecomposition(bool computeVectors, Complex[] a, int aRows, int aColumns, Complex[] s, Complex[] u, Complex[] vt, Complex[] work) + public void SingularValueDecomposition(bool computeVectors, Complex[] a, int rowsA, int columnsA, Complex[] s, Complex[] u, Complex[] vt, Complex[] work) { if (a == null) { @@ -6130,17 +7318,17 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra throw new ArgumentNullException("work"); } - if (u.Length != aRows * aRows) + if (u.Length != rowsA * rowsA) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "u"); } - if (vt.Length != aColumns * aColumns) + if (vt.Length != columnsA * columnsA) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "vt"); } - if (s.Length != Math.Min(aRows, aColumns)) + if (s.Length != Math.Min(rowsA, columnsA)) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "s"); } @@ -6150,17 +7338,17 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra throw new ArgumentException(Resources.ArgumentSingleDimensionArray, "work"); } - if (work.Length < aRows) + if (work.Length < rowsA) { - work[0] = aRows; + work[0] = rowsA; throw new ArgumentException(Resources.WorkArrayTooSmall, "work"); } const int Maxiter = 1000; - var e = new Complex[aColumns]; + var e = new Complex[columnsA]; var v = new Complex[vt.Length]; - var stemp = new Complex[Math.Min(aRows + 1, aColumns)]; + var stemp = new Complex[Math.Min(rowsA + 1, columnsA)]; int i, j, l, lp1; @@ -6168,12 +7356,12 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra var sn = 0.0; Complex t; - var ncu = aRows; + var ncu = rowsA; // Reduce matrix to bidiagonal form, storing the diagonal elements // in "s" and the super-diagonal elements in "e". - var nct = Math.Min(aRows - 1, aColumns); - var nrt = Math.Max(0, Math.Min(aColumns - 2, aRows)); + var nct = Math.Min(rowsA - 1, columnsA); + var nrt = Math.Max(0, Math.Min(columnsA - 2, rowsA)); var lu = Math.Max(nct, nrt); for (l = 0; l < lu; l++) @@ -6184,32 +7372,32 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra // Compute the transformation for the l-th column and // place the l-th diagonal in vector s[l]. var sum = 0.0; - for (i = l; i < aRows; i++) + for (i = l; i < rowsA; i++) { - sum += a[(l * aRows) + i].Magnitude * a[(l * aRows) + i].Magnitude; + sum += a[(l * rowsA) + i].Magnitude * a[(l * rowsA) + i].Magnitude; } stemp[l] = Math.Sqrt(sum); if (stemp[l] != 0.0) { - if (a[(l * aRows) + l] != 0.0) + if (a[(l * rowsA) + l] != 0.0) { - stemp[l] = stemp[l].Magnitude * (a[(l * aRows) + l] / a[(l * aRows) + l].Magnitude); + stemp[l] = stemp[l].Magnitude * (a[(l * rowsA) + l] / a[(l * rowsA) + l].Magnitude); } // A part of column "l" of Matrix A from row "l" to end multiply by 1.0 / s[l] - for (i = l; i < aRows; i++) + for (i = l; i < rowsA; i++) { - a[(l * aRows) + i] = a[(l * aRows) + i] * (1.0 / stemp[l]); + a[(l * rowsA) + i] = a[(l * rowsA) + i] * (1.0 / stemp[l]); } - a[(l * aRows) + l] = 1.0 + a[(l * aRows) + l]; + a[(l * rowsA) + l] = 1.0 + a[(l * rowsA) + l]; } stemp[l] = -stemp[l]; } - for (j = lp1; j < aColumns; j++) + for (j = lp1; j < columnsA; j++) { if (l < nct) { @@ -6217,31 +7405,31 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra { // Apply the transformation. t = 0.0; - for (i = l; i < aRows; i++) + for (i = l; i < rowsA; i++) { - t += a[(l * aRows) + i].Conjugate() * a[(j * aRows) + i]; + t += a[(l * rowsA) + i].Conjugate() * a[(j * rowsA) + i]; } - t = -t / a[(l * aRows) + l]; + t = -t / a[(l * rowsA) + l]; - for (var ii = l; ii < aRows; ii++) + for (var ii = l; ii < rowsA; ii++) { - a[(j * aRows) + ii] += t * a[(l * aRows) + ii]; + a[(j * rowsA) + ii] += t * a[(l * rowsA) + ii]; } } } // Place the l-th row of matrix into "e" for the // subsequent calculation of the row transformation. - e[j] = a[(j * aRows) + l].Conjugate(); + e[j] = a[(j * rowsA) + l].Conjugate(); } if (computeVectors && l < nct) { // Place the transformation in "u" for subsequent back multiplication. - for (i = l; i < aRows; i++) + for (i = l; i < rowsA; i++) { - u[(l * aRows) + i] = a[(l * aRows) + i]; + u[(l * rowsA) + i] = a[(l * rowsA) + i]; } } @@ -6276,28 +7464,28 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra e[l] = -e[l].Conjugate(); - if (lp1 < aRows && e[l] != 0.0) + if (lp1 < rowsA && e[l] != 0.0) { // Apply the transformation. - for (i = lp1; i < aRows; i++) + for (i = lp1; i < rowsA; i++) { work[i] = 0.0; } - for (j = lp1; j < aColumns; j++) + for (j = lp1; j < columnsA; j++) { - for (var ii = lp1; ii < aRows; ii++) + for (var ii = lp1; ii < rowsA; ii++) { - work[ii] += e[j] * a[(j * aRows) + ii]; + work[ii] += e[j] * a[(j * rowsA) + ii]; } } - for (j = lp1; j < aColumns; j++) + for (j = lp1; j < columnsA; j++) { var ww = (-e[j] / e[lp1]).Conjugate(); - for (var ii = lp1; ii < aRows; ii++) + for (var ii = lp1; ii < rowsA; ii++) { - a[(j * aRows) + ii] += ww * work[ii]; + a[(j * rowsA) + ii] += ww * work[ii]; } } } @@ -6308,29 +7496,29 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra } // Place the transformation in v for subsequent back multiplication. - for (i = lp1; i < aColumns; i++) + for (i = lp1; i < columnsA; i++) { - v[(l * aColumns) + i] = e[i]; + v[(l * columnsA) + i] = e[i]; } } // Set up the final bidiagonal matrix or order m. - var m = Math.Min(aColumns, aRows + 1); + var m = Math.Min(columnsA, rowsA + 1); var nctp1 = nct + 1; var nrtp1 = nrt + 1; - if (nct < aColumns) + if (nct < columnsA) { - stemp[nctp1 - 1] = a[((nctp1 - 1) * aRows) + (nctp1 - 1)]; + stemp[nctp1 - 1] = a[((nctp1 - 1) * rowsA) + (nctp1 - 1)]; } - if (aRows < m) + if (rowsA < m) { stemp[m - 1] = 0.0; } if (nrtp1 < m) { - e[nrtp1 - 1] = a[((m - 1) * aRows) + (nrtp1 - 1)]; + e[nrtp1 - 1] = a[((m - 1) * rowsA) + (nrtp1 - 1)]; } e[m - 1] = 0.0; @@ -6340,12 +7528,12 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra { for (j = nctp1 - 1; j < ncu; j++) { - for (i = 0; i < aRows; i++) + for (i = 0; i < rowsA; i++) { - u[(j * aRows) + i] = 0.0; + u[(j * rowsA) + i] = 0.0; } - u[(j * aRows) + j] = 1.0; + u[(j * rowsA) + j] = 1.0; } for (l = nct - 1; l >= 0; l--) @@ -6355,38 +7543,38 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra for (j = l + 1; j < ncu; j++) { t = 0.0; - for (i = l; i < aRows; i++) + for (i = l; i < rowsA; i++) { - t += u[(l * aRows) + i].Conjugate() * u[(j * aRows) + i]; + t += u[(l * rowsA) + i].Conjugate() * u[(j * rowsA) + i]; } - t = -t / u[(l * aRows) + l]; - for (var ii = l; ii < aRows; ii++) + t = -t / u[(l * rowsA) + l]; + for (var ii = l; ii < rowsA; ii++) { - u[(j * aRows) + ii] += t * u[(l * aRows) + ii]; + u[(j * rowsA) + ii] += t * u[(l * rowsA) + ii]; } } // A part of column "l" of matrix A from row "l" to end multiply by -1.0 - for (i = l; i < aRows; i++) + for (i = l; i < rowsA; i++) { - u[(l * aRows) + i] = u[(l * aRows) + i] * -1.0; + u[(l * rowsA) + i] = u[(l * rowsA) + i] * -1.0; } - u[(l * aRows) + l] = 1.0 + u[(l * aRows) + l]; + u[(l * rowsA) + l] = 1.0 + u[(l * rowsA) + l]; for (i = 0; i < l; i++) { - u[(l * aRows) + i] = 0.0; + u[(l * rowsA) + i] = 0.0; } } else { - for (i = 0; i < aRows; i++) + for (i = 0; i < rowsA; i++) { - u[(l * aRows) + i] = 0.0; + u[(l * rowsA) + i] = 0.0; } - u[(l * aRows) + l] = 1.0; + u[(l * rowsA) + l] = 1.0; } } } @@ -6394,36 +7582,36 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra // If it is required, generate v. if (computeVectors) { - for (l = aColumns - 1; l >= 0; l--) + for (l = columnsA - 1; l >= 0; l--) { lp1 = l + 1; if (l < nrt) { if (e[l] != 0.0) { - for (j = lp1; j < aColumns; j++) + for (j = lp1; j < columnsA; j++) { t = 0.0; - for (i = lp1; i < aColumns; i++) + for (i = lp1; i < columnsA; i++) { - t += v[(l * aColumns) + i].Conjugate() * v[(j * aColumns) + i]; + t += v[(l * columnsA) + i].Conjugate() * v[(j * columnsA) + i]; } - t = -t / v[(l * aColumns) + lp1]; - for (var ii = l; ii < aColumns; ii++) + t = -t / v[(l * columnsA) + lp1]; + for (var ii = l; ii < columnsA; ii++) { - v[(j * aColumns) + ii] += t * v[(l * aColumns) + ii]; + v[(j * columnsA) + ii] += t * v[(l * columnsA) + ii]; } } } } - for (i = 0; i < aColumns; i++) + for (i = 0; i < columnsA; i++) { - v[(l * aColumns) + i] = 0.0; + v[(l * columnsA) + i] = 0.0; } - v[(l * aColumns) + l] = 1.0; + v[(l * columnsA) + l] = 1.0; } } @@ -6444,9 +7632,9 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra if (computeVectors) { // A part of column "i" of matrix U from row 0 to end multiply by r - for (j = 0; j < aRows; j++) + for (j = 0; j < rowsA; j++) { - u[(i * aRows) + j] = u[(i * aRows) + j] * r; + u[(i * rowsA) + j] = u[(i * rowsA) + j] * r; } } } @@ -6472,9 +7660,9 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra } // A part of column "i+1" of matrix VT from row 0 to end multiply by r - for (j = 0; j < aColumns; j++) + for (j = 0; j < columnsA; j++) { - v[((i + 1) * aColumns) + j] = v[((i + 1) * aColumns) + j] * r; + v[((i + 1) * columnsA) + j] = v[((i + 1) * columnsA) + j] * r; } } @@ -6581,11 +7769,11 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra if (computeVectors) { // Rotate - for (i = 0; i < aColumns; i++) + for (i = 0; i < columnsA; i++) { - var z = (cs * v[(k * aColumns) + i]) + (sn * v[((m - 1) * aColumns) + i]); - v[((m - 1) * aColumns) + i] = (cs * v[((m - 1) * aColumns) + i]) - (sn * v[(k * aColumns) + i]); - v[(k * aColumns) + i] = z; + var z = (cs * v[(k * columnsA) + i]) + (sn * v[((m - 1) * columnsA) + i]); + v[((m - 1) * columnsA) + i] = (cs * v[((m - 1) * columnsA) + i]) - (sn * v[(k * columnsA) + i]); + v[(k * columnsA) + i] = z; } } } @@ -6606,11 +7794,11 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra if (computeVectors) { // Rotate - for (i = 0; i < aRows; i++) + for (i = 0; i < rowsA; i++) { - var z = (cs * u[(k * aRows) + i]) + (sn * u[((l - 1) * aRows) + i]); - u[((l - 1) * aRows) + i] = (cs * u[((l - 1) * aRows) + i]) - (sn * u[(k * aRows) + i]); - u[(k * aRows) + i] = z; + var z = (cs * u[(k * rowsA) + i]) + (sn * u[((l - 1) * rowsA) + i]); + u[((l - 1) * rowsA) + i] = (cs * u[((l - 1) * rowsA) + i]) - (sn * u[(k * rowsA) + i]); + u[(k * rowsA) + i] = z; } } } @@ -6663,11 +7851,11 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra stemp[k + 1] = cs * stemp[k + 1]; if (computeVectors) { - for (i = 0; i < aColumns; i++) + for (i = 0; i < columnsA; i++) { - var z = (cs * v[(k * aColumns) + i]) + (sn * v[((k + 1) * aColumns) + i]); - v[((k + 1) * aColumns) + i] = (cs * v[((k + 1) * aColumns) + i]) - (sn * v[(k * aColumns) + i]); - v[(k * aColumns) + i] = z; + var z = (cs * v[(k * columnsA) + i]) + (sn * v[((k + 1) * columnsA) + i]); + v[((k + 1) * columnsA) + i] = (cs * v[((k + 1) * columnsA) + i]) - (sn * v[(k * columnsA) + i]); + v[(k * columnsA) + i] = z; } } @@ -6677,13 +7865,13 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra stemp[k + 1] = -(sn * e[k]) + (cs * stemp[k + 1]); g = sn * e[k + 1].Real; e[k + 1] = cs * e[k + 1]; - if (computeVectors && k < aRows) + if (computeVectors && k < rowsA) { - for (i = 0; i < aRows; i++) + for (i = 0; i < rowsA; i++) { - var z = (cs * u[(k * aRows) + i]) + (sn * u[((k + 1) * aRows) + i]); - u[((k + 1) * aRows) + i] = (cs * u[((k + 1) * aRows) + i]) - (sn * u[(k * aRows) + i]); - u[(k * aRows) + i] = z; + var z = (cs * u[(k * rowsA) + i]) + (sn * u[((k + 1) * rowsA) + i]); + u[((k + 1) * rowsA) + i] = (cs * u[((k + 1) * rowsA) + i]) - (sn * u[(k * rowsA) + i]); + u[(k * rowsA) + i] = z; } } } @@ -6702,9 +7890,9 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra if (computeVectors) { // A part of column "l" of matrix VT from row 0 to end multiply by -1 - for (i = 0; i < aColumns; i++) + for (i = 0; i < columnsA; i++) { - v[(l * aColumns) + i] = v[(l * aColumns) + i] * -1.0; + v[(l * columnsA) + i] = v[(l * columnsA) + i] * -1.0; } } } @@ -6720,25 +7908,25 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra t = stemp[l]; stemp[l] = stemp[l + 1]; stemp[l + 1] = t; - if (computeVectors && l < aColumns) + if (computeVectors && l < columnsA) { // Swap columns l, l + 1 - for (i = 0; i < aColumns; i++) + for (i = 0; i < columnsA; i++) { - var z = v[(l * aColumns) + i]; - v[(l * aColumns) + i] = v[((l + 1) * aColumns) + i]; - v[((l + 1) * aColumns) + i] = z; + var z = v[(l * columnsA) + i]; + v[(l * columnsA) + i] = v[((l + 1) * columnsA) + i]; + v[((l + 1) * columnsA) + i] = z; } } - if (computeVectors && l < aRows) + if (computeVectors && l < rowsA) { // Swap columns l, l + 1 - for (i = 0; i < aRows; i++) + for (i = 0; i < rowsA; i++) { - var z = u[(l * aRows) + i]; - u[(l * aRows) + i] = u[((l + 1) * aRows) + i]; - u[((l + 1) * aRows) + i] = z; + var z = u[(l * rowsA) + i]; + u[(l * rowsA) + i] = u[((l + 1) * rowsA) + i]; + u[((l + 1) * rowsA) + i] = z; } } @@ -6754,11 +7942,11 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra if (computeVectors) { // Finally transpose "v" to get "vt" matrix - for (i = 0; i < aColumns; i++) + for (i = 0; i < columnsA; i++) { - for (j = 0; j < aColumns; j++) + for (j = 0; j < columnsA; j++) { - vt[(j * aColumns) + i] = v[(i * aColumns) + j].Conjugate(); + vt[(j * columnsA) + i] = v[(i * columnsA) + j].Conjugate(); } } } @@ -6766,26 +7954,26 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra // Copy stemp to s with size adjustment. We are using ported copy of linpack's svd code and it uses // a singular vector of length rows+1 when rows < columns. The last element is not used and needs to be removed. // We should port lapack's svd routine to remove this problem. - CommonParallel.For(0, Math.Min(aRows, aColumns), index => s[index] = stemp[index]); + CommonParallel.For(0, Math.Min(rowsA, columnsA), index => s[index] = stemp[index]); // On return the first element of the work array stores the min size of the work array could have been // work[0] = Math.Max(3 * Math.Min(aRows, aColumns) + Math.Max(aRows, aColumns), 5 * Math.Min(aRows, aColumns)); - work[0] = aRows; + work[0] = rowsA; } /// /// Solves A*X=B for X using the singular value decomposition of A. /// /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// The singular values of A in ascending value. /// On exit U contains the left singular vectors. /// On exit VT contains the transposed right singular vectors. /// The B matrix. - /// The number of columns of B. + /// The number of columns of B. /// On exit, the solution matrix. - public void SvdSolve(Complex[] a, int aRows, int aColumns, Complex[] s, Complex[] u, Complex[] vt, Complex[] b, int bColumns, Complex[] x) + public void SvdSolve(Complex[] a, int rowsA, int columnsA, Complex[] s, Complex[] u, Complex[] vt, Complex[] b, int columnsB, Complex[] x) { if (a == null) { @@ -6817,52 +8005,52 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra throw new ArgumentNullException("x"); } - if (u.Length != aRows * aRows) + if (u.Length != rowsA * rowsA) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "u"); } - if (vt.Length != aColumns * aColumns) + if (vt.Length != columnsA * columnsA) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "vt"); } - if (s.Length != Math.Min(aRows, aColumns)) + if (s.Length != Math.Min(rowsA, columnsA)) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "s"); } - if (b.Length != aRows * bColumns) + if (b.Length != rowsA * columnsB) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); } - if (x.Length != aColumns * bColumns) + if (x.Length != columnsA * columnsB) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); } // TODO: Actually "work = new double[aRows]" is acceptable size of work array. I set size proposed in method description - var work = new Complex[Math.Max((3 * Math.Min(aRows, aColumns)) + Math.Max(aRows, aColumns), 5 * Math.Min(aRows, aColumns))]; - SvdSolve(a, aRows, aColumns, s, u, vt, b, bColumns, x, work); + var work = new Complex[(2 * Math.Min(rowsA, columnsA)) + Math.Max(rowsA, columnsA)]; + SvdSolve(a, rowsA, columnsA, s, u, vt, b, columnsB, x, work); } /// /// Solves A*X=B for X using the singular value decomposition of A. /// /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// The singular values of A in ascending value. /// On exit U contains the left singular vectors. /// On exit VT contains the transposed right singular vectors. /// The B matrix. - /// The number of columns of B. + /// The number of columns of B. /// On exit, the solution matrix. /// The work array. For real matrices, the work array should be at least /// Max(3*Min(M, N) + Max(M, N), 5*Min(M,N)). For complex matrices, 2*Min(M, N) + Max(M, N). /// On exit, work[0] contains the optimal work size value. - public void SvdSolve(Complex[] a, int aRows, int aColumns, Complex[] s, Complex[] u, Complex[] vt, Complex[] b, int bColumns, Complex[] x, Complex[] work) + public void SvdSolve(Complex[] a, int rowsA, int columnsA, Complex[] s, Complex[] u, Complex[] vt, Complex[] b, int columnsB, Complex[] x, Complex[] work) { if (a == null) { @@ -6894,27 +8082,27 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra throw new ArgumentNullException("x"); } - if (u.Length != aRows * aRows) + if (u.Length != rowsA * rowsA) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "u"); } - if (vt.Length != aColumns * aColumns) + if (vt.Length != columnsA * columnsA) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "vt"); } - if (s.Length != Math.Min(aRows, aColumns)) + if (s.Length != Math.Min(rowsA, columnsA)) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "s"); } - if (b.Length != aRows * bColumns) + if (b.Length != rowsA * columnsB) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); } - if (x.Length != aColumns * bColumns) + if (x.Length != columnsA * columnsB) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); } @@ -6924,28 +8112,28 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra throw new ArgumentException(Resources.ArgumentSingleDimensionArray, "work"); } - if (work.Length < aRows) + if (work.Length < rowsA) { - work[0] = aRows; + work[0] = rowsA; throw new ArgumentException(Resources.WorkArrayTooSmall, "work"); } - SingularValueDecomposition(true, a, aRows, aColumns, s, u, vt, work); - SvdSolveFactored(aRows, aColumns, s, u, vt, b, bColumns, x); + SingularValueDecomposition(true, a, rowsA, columnsA, s, u, vt, work); + SvdSolveFactored(rowsA, columnsA, s, u, vt, b, columnsB, x); } /// /// Solves A*X=B for X using a previously SVD decomposed matrix. /// - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// The s values returned by . /// The left singular vectors returned by . /// The right singular vectors returned by . /// The B matrix. - /// The number of columns of B. + /// The number of columns of B. /// On exit, the solution matrix. - public void SvdSolveFactored(int aRows, int aColumns, Complex[] s, Complex[] u, Complex[] vt, Complex[] b, int bColumns, Complex[] x) + public void SvdSolveFactored(int rowsA, int columnsA, Complex[] s, Complex[] u, Complex[] vt, Complex[] b, int columnsB, Complex[] x) { if (s == null) { @@ -6972,44 +8160,44 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra throw new ArgumentNullException("x"); } - if (u.Length != aRows * aRows) + if (u.Length != rowsA * rowsA) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "u"); } - if (vt.Length != aColumns * aColumns) + if (vt.Length != columnsA * columnsA) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "vt"); } - if (s.Length != Math.Min(aRows, aColumns)) + if (s.Length != Math.Min(rowsA, columnsA)) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "s"); } - if (b.Length != aRows * bColumns) + if (b.Length != rowsA * columnsB) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); } - if (x.Length != aColumns * bColumns) + if (x.Length != columnsA * columnsB) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); } - var mn = Math.Min(aRows, aColumns); - var tmp = new Complex[aColumns]; + var mn = Math.Min(rowsA, columnsA); + var tmp = new Complex[columnsA]; - for (var k = 0; k < bColumns; k++) + for (var k = 0; k < columnsB; k++) { - for (var j = 0; j < aColumns; j++) + for (var j = 0; j < columnsA; j++) { var value = Complex.Zero; if (j < mn) { - for (var i = 0; i < aRows; i++) + for (var i = 0; i < rowsA; i++) { - value += u[(j * aRows) + i].Conjugate() * b[(k * aRows) + i]; + value += u[(j * rowsA) + i].Conjugate() * b[(k * rowsA) + i]; } value /= s[j]; @@ -7018,15 +8206,15 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra tmp[j] = value; } - for (var j = 0; j < aColumns; j++) + for (var j = 0; j < columnsA; j++) { var value = Complex.Zero; - for (var i = 0; i < aColumns; i++) + for (var i = 0; i < columnsA; i++) { - value += vt[(j * aColumns) + i].Conjugate() * tmp[i]; + value += vt[(j * columnsA) + i].Conjugate() * tmp[i]; } - x[(k * aColumns) + j] = value; + x[(k * columnsA) + j] = value; } } } @@ -7234,11 +8422,33 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra CommonParallel.For(0, y.Length, i => result[i] = x[i] * y[i]); } + /// + /// Computes the requested of the matrix. + /// + /// The type of norm to compute. + /// The number of rows. + /// The number of columns. + /// The matrix to compute the norm from. + /// + /// The requested of the matrix. + /// public Complex32 MatrixNorm(Norm norm, int rows, int columns, Complex32[] matrix) { throw new NotImplementedException(); } + /// + /// Computes the requested of the matrix. + /// + /// The type of norm to compute. + /// The number of rows. + /// The number of columns. + /// The matrix to compute the norm from. + /// The work array. Only used when + /// and needs to be have a length of at least M (number of rows of . + /// + /// The requested of the matrix. + /// public Complex32 MatrixNorm(Norm norm, int rows, int columns, Complex32[] matrix, Complex32[] work) { throw new NotImplementedException(); @@ -7248,15 +8458,15 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra /// Multiples two matrices. result = x * y /// /// The x matrix. - /// The number of rows in the x matrix. - /// The number of columns in the x matrix. + /// The number of rows in the x matrix. + /// The number of columns in the x matrix. /// The y matrix. - /// The number of rows in the y matrix. - /// The number of columns in the y matrix. + /// The number of rows in the y matrix. + /// The number of columns in the y matrix. /// Where to store the result of the multiplication. /// This is a simplified version of the BLAS GEMM routine with alpha /// set to 1.0 and beta set to 0.0, and x and y are not transposed. - public void MatrixMultiply(Complex32[] x, int xRows, int xColumns, Complex32[] y, int yRows, int yColumns, Complex32[] result) + public void MatrixMultiply(Complex32[] x, int rowsX, int columnsX, Complex32[] y, int rowsY, int columnsY, Complex32[] result) { // First check some basic requirement on the parameters of the matrix multiplication. if (x == null) @@ -7274,22 +8484,22 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra throw new ArgumentNullException("result"); } - if (xRows * xColumns != x.Length) + if (rowsX * columnsX != x.Length) { throw new ArgumentException("x.Length != xRows * xColumns"); } - if (yRows * yColumns != y.Length) + if (rowsY * columnsY != y.Length) { throw new ArgumentException("y.Length != yRows * yColumns"); } - if (xColumns != yRows) + if (columnsX != rowsY) { throw new ArgumentException("xColumns != yRows"); } - if (xRows * yColumns != result.Length) + if (rowsX * columnsY != result.Length) { throw new ArgumentException("xRows * yColumns != result.Length"); } @@ -7321,7 +8531,7 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra // TODO - For small matrices we should get rid of the parallelism because of startup costs. // Perhaps the following implementations would be a good one // http://blog.feradz.com/2009/01/cache-efficient-matrix-multiplication/ - MatrixMultiplyWithUpdate(Transpose.DontTranspose, Transpose.DontTranspose, Complex32.One, xdata, xRows, xColumns, ydata, yRows, yColumns, Complex32.Zero, result); + MatrixMultiplyWithUpdate(Transpose.DontTranspose, Transpose.DontTranspose, Complex32.One, xdata, rowsX, columnsX, ydata, rowsY, columnsY, Complex32.Zero, result); } /// @@ -7331,20 +8541,18 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra /// How to transpose the matrix. /// The value to scale matrix. /// The a matrix. - /// The number of rows in the matrix. - /// The number of columns in the matrix. + /// The number of rows in the matrix. + /// The number of columns in the matrix. /// The b matrix - /// The number of rows in the matrix. - /// The number of columns in the matrix. + /// The number of rows in the matrix. + /// The number of columns in the matrix. /// The value to scale the matrix. /// The c matrix. - public void MatrixMultiplyWithUpdate(Transpose transposeA, Transpose transposeB, Complex32 alpha, Complex32[] a, - int aRows, int aColumns, Complex32[] b, int bRows, int bColumns, Complex32 beta, Complex32[] c) + public void MatrixMultiplyWithUpdate(Transpose transposeA, Transpose transposeB, Complex32 alpha, Complex32[] a, int rowsA, int columnsA, Complex32[] b, int rowsB, int columnsB, Complex32 beta, Complex32[] c) { - // Choose nonsensical values for the number of rows and columns in c; fill them in depending + // Choose nonsensical values for the number of rows in c; fill them in depending // on the operations on a and b. - var cRows = -1; - var cColumns = -1; + int rowsC; // First check some basic requirement on the parameters of the matrix multiplication. if (a == null) @@ -7359,63 +8567,59 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra if ((int)transposeA > 111 && (int)transposeB > 111) { - if (aRows != bColumns) + if (rowsA != columnsB) { throw new ArgumentOutOfRangeException(); } - if (aColumns * bRows != c.Length) + if (columnsA * rowsB != c.Length) { throw new ArgumentOutOfRangeException(); } - cRows = aColumns; - cColumns = bRows; + rowsC = columnsA; } else if ((int)transposeA > 111) { - if (aRows != bRows) + if (rowsA != rowsB) { throw new ArgumentOutOfRangeException(); } - if (aColumns * bColumns != c.Length) + if (columnsA * columnsB != c.Length) { throw new ArgumentOutOfRangeException(); } - cRows = aColumns; - cColumns = bColumns; + rowsC = columnsA; } else if ((int)transposeB > 111) { - if (aColumns != bColumns) + if (columnsA != columnsB) { throw new ArgumentOutOfRangeException(); } - if (aRows * bRows != c.Length) + if (rowsA * rowsB != c.Length) { throw new ArgumentOutOfRangeException(); } - cRows = aRows; - cColumns = bRows; + rowsC = rowsA; } else { - if (aColumns != bRows) + if (columnsA != rowsB) { throw new ArgumentOutOfRangeException(); } - if (aRows * bColumns != c.Length) + if (rowsA * columnsB != c.Length) { throw new ArgumentOutOfRangeException(); } - cRows = aRows; - cColumns = bColumns; + rowsC = rowsA; } if (alpha.IsZero() && beta.IsZero()) @@ -7424,7 +8628,6 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra return; } - // Check whether we will be overwriting any of our inputs and make copies if necessary. // TODO - we can don't have to allocate a completely new matrix when x or y point to the same memory // as result, we can do it on a row wise basis. We should investigate this. @@ -7454,150 +8657,174 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra { if ((int)transposeA > 111 && (int)transposeB > 111) { - CommonParallel.For(0, aColumns, j => - { - var jIndex = j * cRows; - for (var i = 0; i != bRows; i++) - { - var iIndex = i * aRows; - Complex32 s = 0; - for (var l = 0; l != bColumns; l++) - { - s += adata[iIndex + l] * bdata[l * bRows + j]; - } - - c[jIndex + i] = s; - } - }); + CommonParallel.For( + 0, + columnsA, + j => + { + var jIndex = j * rowsC; + for (var i = 0; i != rowsB; i++) + { + var iIndex = i * rowsA; + Complex32 s = 0; + for (var l = 0; l != columnsB; l++) + { + s += adata[iIndex + l] * bdata[(l * rowsB) + j]; + } + + c[jIndex + i] = s; + } + }); } else if ((int)transposeA > 111) { - CommonParallel.For(0, bColumns, j => - { - var jcIndex = j * cRows; - var jbIndex = j * bRows; - for (var i = 0; i != aColumns; i++) - { - var iIndex = i * aRows; - Complex32 s = 0; - for (var l = 0; l != aRows; l++) - { - s += adata[iIndex + l] * bdata[jbIndex + l]; - } - - c[jcIndex + i] = s; - } - }); + CommonParallel.For( + 0, + columnsB, + j => + { + var jcIndex = j * rowsC; + var jbIndex = j * rowsB; + for (var i = 0; i != columnsA; i++) + { + var iIndex = i * rowsA; + Complex32 s = 0; + for (var l = 0; l != rowsA; l++) + { + s += adata[iIndex + l] * bdata[jbIndex + l]; + } + + c[jcIndex + i] = s; + } + }); } else if ((int)transposeB > 111) { - CommonParallel.For(0, bRows, j => - { - var jIndex = j * cRows; - for (var i = 0; i != aRows; i++) - { - Complex32 s = 0; - for (var l = 0; l != aColumns; l++) - { - s += adata[l * aRows + i] * bdata[l * bRows + j]; - } - - c[jIndex + i] = s; - } - }); + CommonParallel.For( + 0, + rowsB, + j => + { + var jIndex = j * rowsC; + for (var i = 0; i != rowsA; i++) + { + Complex32 s = 0; + for (var l = 0; l != columnsA; l++) + { + s += adata[(l * rowsA) + i] * bdata[(l * rowsB) + j]; + } + + c[jIndex + i] = s; + } + }); } else { - CommonParallel.For(0, bColumns, j => - { - var jcIndex = j * cRows; - var jbIndex = j * bRows; - for (var i = 0; i != aRows; i++) - { - Complex32 s = 0; - for (var l = 0; l != aColumns; l++) - { - s += adata[l * aRows + i] * bdata[jbIndex + l]; - } + CommonParallel.For( + 0, + columnsB, + j => + { + var jcIndex = j * rowsC; + var jbIndex = j * rowsB; + for (var i = 0; i != rowsA; i++) + { + Complex32 s = 0; + for (var l = 0; l != columnsA; l++) + { + s += adata[(l * rowsA) + i] * bdata[jbIndex + l]; + } - c[jcIndex + i] = s; - } - }); + c[jcIndex + i] = s; + } + }); } } else { if ((int)transposeA > 111 && (int)transposeB > 111) { - CommonParallel.For(0, aColumns, j => - { - var jIndex = j * cRows; - for (var i = 0; i != bRows; i++) - { - var iIndex = i * aRows; - Complex32 s = 0; - for (var l = 0; l != bColumns; l++) - { - s += adata[iIndex + l] * bdata[l * bRows + j]; - } - - c[jIndex + i] = c[jIndex + i] * beta + s; - } - }); + CommonParallel.For( + 0, + columnsA, + j => + { + var jIndex = j * rowsC; + for (var i = 0; i != rowsB; i++) + { + var iIndex = i * rowsA; + Complex32 s = 0; + for (var l = 0; l != columnsB; l++) + { + s += adata[iIndex + l] * bdata[(l * rowsB) + j]; + } + + c[jIndex + i] = (c[jIndex + i] * beta) + s; + } + }); } else if ((int)transposeA > 111) { - CommonParallel.For(0, bColumns, j => - { - var jcIndex = j * cRows; - var jbIndex = j * bRows; - for (var i = 0; i != aColumns; i++) - { - var iIndex = i * aRows; - Complex32 s = 0; - for (var l = 0; l != aRows; l++) - { - s += adata[iIndex + l] * bdata[jbIndex + l]; - } - - c[jcIndex + i] = s + c[jcIndex + i] * beta; - } - }); + CommonParallel.For( + 0, + columnsB, + j => + { + var jcIndex = j * rowsC; + var jbIndex = j * rowsB; + for (var i = 0; i != columnsA; i++) + { + var iIndex = i * rowsA; + Complex32 s = 0; + for (var l = 0; l != rowsA; l++) + { + s += adata[iIndex + l] * bdata[jbIndex + l]; + } + + c[jcIndex + i] = s + (c[jcIndex + i] * beta); + } + }); } else if ((int)transposeB > 111) { - CommonParallel.For(0, bRows, j => - { - var jIndex = j * cRows; - for (var i = 0; i != aRows; i++) - { - Complex32 s = 0; - for (var l = 0; l != aColumns; l++) - { - s += adata[l * aRows + i] * bdata[l * bRows + j]; - } - - c[jIndex + i] = s + c[jIndex + i] * beta; - } - }); + CommonParallel.For( + 0, + rowsB, + j => + { + var jIndex = j * rowsC; + for (var i = 0; i != rowsA; i++) + { + Complex32 s = 0; + for (var l = 0; l != columnsA; l++) + { + s += adata[(l * rowsA) + i] * bdata[(l * rowsB) + j]; + } + + c[jIndex + i] = s + (c[jIndex + i] * beta); + } + }); } else { - CommonParallel.For(0, bColumns, j => - { - var jcIndex = j * cRows; - var jbIndex = j * bRows; - for (var i = 0; i != aRows; i++) - { - Complex32 s = 0; - for (var l = 0; l != aColumns; l++) - { - s += adata[l * aRows + i] * bdata[jbIndex + l]; - } + CommonParallel.For( + 0, + columnsB, + j => + { + var jcIndex = j * rowsC; + var jbIndex = j * rowsB; + for (var i = 0; i != rowsA; i++) + { + Complex32 s = 0; + for (var l = 0; l != columnsA; l++) + { + s += adata[(l * rowsA) + i] * bdata[jbIndex + l]; + } - c[jcIndex + i] = s + c[jcIndex + i] * beta; - } - }); + c[jcIndex + i] = s + (c[jcIndex + i] * beta); + } + }); } } } @@ -7605,75 +8832,87 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra { if ((int)transposeA > 111 && (int)transposeB > 111) { - CommonParallel.For(0, aColumns, j => - { - var jIndex = j * cRows; - for (var i = 0; i != bRows; i++) - { - var iIndex = i * aRows; - Complex32 s = 0; - for (var l = 0; l != bColumns; l++) - { - s += adata[iIndex + l] * bdata[l * bRows + j]; - } - - c[jIndex + i] = c[jIndex + i] * beta + alpha * s; - } - }); + CommonParallel.For( + 0, + columnsA, + j => + { + var jIndex = j * rowsC; + for (var i = 0; i != rowsB; i++) + { + var iIndex = i * rowsA; + Complex32 s = 0; + for (var l = 0; l != columnsB; l++) + { + s += adata[iIndex + l] * bdata[(l * rowsB) + j]; + } + + c[jIndex + i] = (c[jIndex + i] * beta) + (alpha * s); + } + }); } else if ((int)transposeA > 111) { - CommonParallel.For(0, bColumns, j => - { - var jcIndex = j * cRows; - var jbIndex = j * bRows; - for (var i = 0; i != aColumns; i++) - { - var iIndex = i * aRows; - Complex32 s = 0; - for (var l = 0; l != aRows; l++) - { - s += adata[iIndex + l] * bdata[jbIndex + l]; - } - - c[jcIndex + i] = alpha * s + c[jcIndex + i] * beta; - } - }); + CommonParallel.For( + 0, + columnsB, + j => + { + var jcIndex = j * rowsC; + var jbIndex = j * rowsB; + for (var i = 0; i != columnsA; i++) + { + var iIndex = i * rowsA; + Complex32 s = 0; + for (var l = 0; l != rowsA; l++) + { + s += adata[iIndex + l] * bdata[jbIndex + l]; + } + + c[jcIndex + i] = (alpha * s) + (c[jcIndex + i] * beta); + } + }); } else if ((int)transposeB > 111) { - CommonParallel.For(0, bRows, j => - { - var jIndex = j * cRows; - for (var i = 0; i != aRows; i++) - { - Complex32 s = 0; - for (var l = 0; l != aColumns; l++) - { - s += adata[l * aRows + i] * bdata[l * bRows + j]; - } - - c[jIndex + i] = alpha * s + c[jIndex + i] * beta; - } - }); + CommonParallel.For( + 0, + rowsB, + j => + { + var jIndex = j * rowsC; + for (var i = 0; i != rowsA; i++) + { + Complex32 s = 0; + for (var l = 0; l != columnsA; l++) + { + s += adata[(l * rowsA) + i] * bdata[(l * rowsB) + j]; + } + + c[jIndex + i] = (alpha * s) + (c[jIndex + i] * beta); + } + }); } else { - CommonParallel.For(0, bColumns, j => - { - var jcIndex = j * cRows; - var jbIndex = j * bRows; - for (var i = 0; i != aRows; i++) - { - Complex32 s = 0; - for (var l = 0; l != aColumns; l++) - { - s += adata[l * aRows + i] * bdata[jbIndex + l]; - } + CommonParallel.For( + 0, + columnsB, + j => + { + var jcIndex = j * rowsC; + var jbIndex = j * rowsB; + for (var i = 0; i != rowsA; i++) + { + Complex32 s = 0; + for (var l = 0; l != columnsA; l++) + { + s += adata[(l * rowsA) + i] * bdata[jbIndex + l]; + } - c[jcIndex + i] = alpha * s + c[jcIndex + i] * beta; - } - }); + c[jcIndex + i] = (alpha * s) + (c[jcIndex + i] * beta); + } + }); } } } @@ -7744,21 +8983,57 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra throw new NotImplementedException(); } + /// + /// Solves A*X=B for X using LU factorization. + /// + /// The number of columns of B. + /// The square matrix A. + /// The order of the square matrix . + /// The B matrix. + /// This is equivalent to the GETRF and GETRS LAPACK routines. public void LUSolve(int columnsOfB, Complex32[] a, int order, Complex32[] b) { throw new NotImplementedException(); } + /// + /// Solves A*X=B for X using a previously factored A matrix. + /// + /// The number of columns of B. + /// The factored A matrix. + /// The order of the square matrix . + /// The pivot indices of . + /// The B matrix. + /// This is equivalent to the GETRS LAPACK routine. public void LUSolveFactored(int columnsOfB, Complex32[] a, int order, int[] ipiv, Complex32[] b) { throw new NotImplementedException(); } + /// + /// Solves A*X=B for X using LU factorization. + /// + /// How to transpose the matrix. + /// The number of columns of B. + /// The square matrix A. + /// The order of the square matrix . + /// The B matrix. + /// This is equivalent to the GETRF and GETRS LAPACK routines. public void LUSolve(Transpose transposeA, int columnsOfB, Complex32[] a, int order, Complex32[] b) { throw new NotImplementedException(); } + /// + /// Solves A*X=B for X using a previously factored A matrix. + /// + /// How to transpose the matrix. + /// The number of columns of B. + /// The factored A matrix. + /// The order of the square matrix . + /// The pivot indices of . + /// The B matrix. + /// This is equivalent to the GETRS LAPACK routine. public void LUSolveFactored(Transpose transposeA, int columnsOfB, Complex32[] a, int order, int[] ipiv, Complex32[] b) { throw new NotImplementedException(); @@ -7780,12 +9055,12 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra /// Solves A*X=B for X using Cholesky factorization. /// /// The square, positive definite matrix A. - /// The number of rows and columns in A. + /// The number of rows and columns in A. /// The B matrix. - /// The number of rows in the B matrix. - /// The number of columns in the B matrix. + /// The number of rows in the B matrix. + /// The number of columns in the B matrix. /// This is equivalent to the POTRF add POTRS LAPACK routines. - public void CholeskySolve(Complex32[] a, int aOrder, Complex32[] b, int bRows, int bColumns) + public void CholeskySolve(Complex32[] a, int orderA, Complex32[] b, int rowsB, int columnsB) { throw new NotImplementedException(); } @@ -7794,12 +9069,12 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra /// Solves A*X=B for X using a previously factored A matrix. /// /// The square, positive definite matrix A. - /// The number of rows and columns in A. + /// The number of rows and columns in A. /// The B matrix. - /// The number of rows in the B matrix. - /// The number of columns in the B matrix. + /// 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(Complex32[] a, int aOrder, Complex32[] b, int bRows, int bColumns) + public void CholeskySolveFactored(Complex32[] a, int orderA, Complex32[] b, int rowsB, int columnsB) { throw new NotImplementedException(); } @@ -7809,12 +9084,12 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra /// /// On entry, it is the M by N A matrix to factor. On exit, /// it is overwritten with the R matrix of the QR factorization. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// On exit, A M by M matrix that holds the Q matrix of the /// QR factorization. /// This is similar to the GEQRF and ORGQR LAPACK routines. - public void QRFactor(Complex32[] r, int rRows, int rColumns, Complex32[] q) + public void QRFactor(Complex32[] r, int rowsR, int columnsR, Complex32[] q) { throw new NotImplementedException(); } @@ -7824,15 +9099,15 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra /// /// On entry, it is the M by N A matrix to factor. On exit, /// it is overwritten with the R matrix of the QR factorization. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// On exit, A M by M matrix that holds the Q matrix of the /// QR factorization. /// The work array. The array must have a length of at least N, /// but should be N*blocksize. The blocksize is machine dependent. On exit, work[0] contains the optimal /// work size value. /// This is similar to the GEQRF and ORGQR LAPACK routines. - public void QRFactor(Complex32[] r, int rRows, int rColumns, Complex32[] q, Complex32[] work) + public void QRFactor(Complex32[] r, int rowsR, int columnsR, Complex32[] q, Complex32[] work) { throw new NotImplementedException(); } @@ -7842,14 +9117,14 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra /// /// On entry, it is the M by N A matrix to factor. On exit, /// it is overwritten with the R matrix of the QR factorization. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// On exit, A M by M matrix that holds the Q matrix of the /// QR factorization. /// The B matrix. - /// The number of columns of B. + /// The number of columns of B. /// On exit, the solution matrix. - public void QRSolve(Complex32[] r, int rRows, int rColumns, Complex32[] q, Complex32[] b, int bColumns, Complex32[] x) + public void QRSolve(Complex32[] r, int rowsR, int columnsR, Complex32[] q, Complex32[] b, int columnsB, Complex32[] x) { throw new NotImplementedException(); } @@ -7859,17 +9134,17 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra /// /// On entry, it is the M by N A matrix to factor. On exit, /// it is overwritten with the R matrix of the QR factorization. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// On exit, A M by M matrix that holds the Q matrix of the /// QR factorization. /// The B matrix. - /// The number of columns of B. + /// The number of columns of B. /// On exit, the solution matrix. /// The work array. The array must have a length of at least N, /// but should be N*blocksize. The blocksize is machine dependent. On exit, work[0] contains the optimal /// work size value. - public void QRSolve(Complex32[] r, int rRows, int rColumns, Complex32[] q, Complex32[] b, int bColumns, Complex32[] x, Complex32[] work) + public void QRSolve(Complex32[] r, int rowsR, int columnsR, Complex32[] q, Complex32[] b, int columnsB, Complex32[] x, Complex32[] work) { throw new NotImplementedException(); } @@ -7879,12 +9154,12 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra /// /// The Q matrix obtained by calling . /// The R matrix obtained by calling . - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// The B matrix. - /// The number of columns of B. + /// The number of columns of B. /// On exit, the solution matrix. - public void QRSolveFactored(Complex32[] q, Complex32[] r, int rRows, int rColumns, Complex32[] b, int bColumns, Complex32[] x) + public void QRSolveFactored(Complex32[] q, Complex32[] r, int rowsR, int columnsR, Complex32[] b, int columnsB, Complex32[] x) { throw new NotImplementedException(); } @@ -7894,15 +9169,15 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra /// /// Compute the singular U and VT vectors or not. /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// The singular values of A in ascending value. /// If is true, on exit U contains the left /// singular vectors. /// If is true, on exit VT contains the transposed /// right singular vectors. /// This is equivalent to the GESVD LAPACK routine. - public void SingularValueDecomposition(bool computeVectors, Complex32[] a, int aRows, int aColumns, Complex32[] s, Complex32[] u, Complex32[] vt) + public void SingularValueDecomposition(bool computeVectors, Complex32[] a, int rowsA, int columnsA, Complex32[] s, Complex32[] u, Complex32[] vt) { throw new NotImplementedException(); } @@ -7912,8 +9187,8 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra /// /// Compute the singular U and VT vectors or not. /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// The singular values of A in ascending value. /// If is true, on exit U contains the left /// singular vectors. @@ -7923,7 +9198,7 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra /// Max(3*Min(M, N) + Max(M, N), 5*Min(M,N)). For complex matrices, 2*Min(M, N) + Max(M, N). /// On exit, work[0] contains the optimal work size value. /// This is equivalent to the GESVD LAPACK routine. - public void SingularValueDecomposition(bool computeVectors, Complex32[] a, int aRows, int aColumns, Complex32[] s, Complex32[] u, Complex32[] vt, Complex32[] work) + public void SingularValueDecomposition(bool computeVectors, Complex32[] a, int rowsA, int columnsA, Complex32[] s, Complex32[] u, Complex32[] vt, Complex32[] work) { throw new NotImplementedException(); } @@ -7932,15 +9207,15 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra /// Solves A*X=B for X using the singular value decomposition of A. /// /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// The singular values of A in ascending value. /// On exit U contains the left singular vectors. /// On exit VT contains the transposed right singular vectors. /// The B matrix. - /// The number of columns of B. + /// The number of columns of B. /// On exit, the solution matrix. - public void SvdSolve(Complex32[] a, int aRows, int aColumns, Complex32[] s, Complex32[] u, Complex32[] vt, Complex32[] b, int bColumns, Complex32[] x) + public void SvdSolve(Complex32[] a, int rowsA, int columnsA, Complex32[] s, Complex32[] u, Complex32[] vt, Complex32[] b, int columnsB, Complex32[] x) { throw new NotImplementedException(); } @@ -7949,18 +9224,18 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra /// Solves A*X=B for X using the singular value decomposition of A. /// /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// The singular values of A in ascending value. /// On exit U contains the left singular vectors. /// On exit VT contains the transposed right singular vectors. /// The B matrix. - /// The number of columns of B. + /// The number of columns of B. /// On exit, the solution matrix. /// The work array. For real matrices, the work array should be at least /// Max(3*Min(M, N) + Max(M, N), 5*Min(M,N)). For complex matrices, 2*Min(M, N) + Max(M, N). /// On exit, work[0] contains the optimal work size value. - public void SvdSolve(Complex32[] a, int aRows, int aColumns, Complex32[] s, Complex32[] u, Complex32[] vt, Complex32[] b, int bColumns, Complex32[] x, Complex32[] work) + public void SvdSolve(Complex32[] a, int rowsA, int columnsA, Complex32[] s, Complex32[] u, Complex32[] vt, Complex32[] b, int columnsB, Complex32[] x, Complex32[] work) { throw new NotImplementedException(); } @@ -7968,15 +9243,15 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra /// /// Solves A*X=B for X using a previously SVD decomposed matrix. /// - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// The s values returned by . /// The left singular vectors returned by . /// The right singular vectors returned by . /// The B matrix. - /// The number of columns of B. + /// The number of columns of B. /// On exit, the solution matrix. - public void SvdSolveFactored(int aRows, int aColumns, Complex32[] s, Complex32[] u, Complex32[] vt, Complex32[] b, int bColumns, Complex32[] x) + public void SvdSolveFactored(int rowsA, int columnsA, Complex32[] s, Complex32[] u, Complex32[] vt, Complex32[] b, int columnsB, Complex32[] x) { throw new NotImplementedException(); } diff --git a/src/Numerics/Algorithms/LinearAlgebra/Mkl/MklLinearAlgebraProvider.cs b/src/Numerics/Algorithms/LinearAlgebra/Mkl/MklLinearAlgebraProvider.cs index 5094a665..fdca19cc 100644 --- a/src/Numerics/Algorithms/LinearAlgebra/Mkl/MklLinearAlgebraProvider.cs +++ b/src/Numerics/Algorithms/LinearAlgebra/Mkl/MklLinearAlgebraProvider.cs @@ -266,17 +266,17 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl /// Multiples two matrices. result = x * y /// /// The x matrix. - /// The number of rows in the x matrix. - /// The number of columns in the x matrix. + /// The number of rows in the x matrix. + /// The number of columns in the x matrix. /// The y matrix. - /// The number of rows in the y matrix. - /// The number of columns in the y matrix. + /// The number of rows in the y matrix. + /// The number of columns in the y matrix. /// Where to store the result of the multiplication. /// This is a simplified version of the BLAS GEMM routine with alpha /// set to 1.0 and beta set to 0.0, and x and y are not transposed. - public void MatrixMultiply(double[] x, int xRows, int xColumns, double[] y, int yRows, int yColumns, double[] result) + public void MatrixMultiply(double[] x, int rowsX, int columnsX, double[] y, int rowsY, int columnsY, double[] result) { - MatrixMultiplyWithUpdate(Transpose.DontTranspose, Transpose.DontTranspose, 1.0, x, xRows, xColumns, y, yRows, yColumns, 0.0, result); + MatrixMultiplyWithUpdate(Transpose.DontTranspose, Transpose.DontTranspose, 1.0, x, rowsX, columnsX, y, rowsY, columnsY, 0.0, result); } /// @@ -286,15 +286,15 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl /// How to transpose the matrix. /// The value to scale matrix. /// The a matrix. - /// The number of rows in the matrix. - /// The number of columns in the matrix. + /// The number of rows in the matrix. + /// The number of columns in the matrix. /// The b matrix - /// The number of rows in the matrix. - /// The number of columns in the matrix. + /// The number of rows in the matrix. + /// The number of columns in the matrix. /// The value to scale the matrix. /// The c matrix. public void MatrixMultiplyWithUpdate(Transpose transposeA, Transpose transposeB, double alpha, double[] a, - int aRows, int aColumns, double[] b, int bRows, int bColumns, double beta, double[] c) + int rowsA, int columnsA, double[] b, int rowsB, int columnsB, double beta, double[] c) { if (a == null) { @@ -311,16 +311,16 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl throw new ArgumentNullException("c"); } - var m = transposeA == Transpose.DontTranspose ? aRows : aColumns; - var n = transposeB == Transpose.DontTranspose ? bColumns : bRows; - var k = transposeA == Transpose.DontTranspose ? aColumns : aRows; + var m = transposeA == Transpose.DontTranspose ? rowsA : columnsA; + var n = transposeB == Transpose.DontTranspose ? columnsB : rowsB; + var k = transposeA == Transpose.DontTranspose ? columnsA : rowsA; - if( c.Length != aRows * bColumns) + if( c.Length != rowsA * columnsB) { throw new ArgumentException(Resources.ArgumentMatrixDimensions); } - if (aColumns != bRows) + if (columnsA != rowsB) { throw new ArgumentException(Resources.ArgumentMatrixDimensions); } @@ -476,13 +476,13 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl /// Solves A*X=B for X using Cholesky factorization. /// /// The square, positive definite matrix A. - /// The number of rows and columns in A. + /// The number of rows and columns in A. /// The B matrix. - /// The number of rows in the B matrix. - /// The number of columns in the B matrix. + /// The number of rows in the B matrix. + /// The number of columns in the B matrix. /// This is equivalent to the POTRF add POTRS LAPACK routines. /// - public void CholeskySolve(double[] a, int aOrder, double[] b, int bRows, int bColumns) + public void CholeskySolve(double[] a, int orderA, double[] b, int rowsB, int columnsB) { throw new NotImplementedException(); } @@ -491,12 +491,12 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl /// Solves A*X=B for X using a previously factored A matrix. /// /// The square, positive definite matrix A. - /// The number of rows and columns in A. + /// The number of rows and columns in A. /// The B matrix. - /// The number of rows in the B matrix. - /// The number of columns in the B matrix. + /// 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) + public void CholeskySolveFactored(double[] a, int orderA, double[] b, int rowsB, int columnsB) { throw new NotImplementedException(); } @@ -506,12 +506,12 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl /// /// On entry, it is the M by N A matrix to factor. On exit, /// it is overwritten with the R matrix of the QR factorization. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// On exit, A M by M matrix that holds the Q matrix of the /// QR factorization. /// This is similar to the GEQRF and ORGQR LAPACK routines. - public void QRFactor(double[] r, int rRows, int rColumns, double[] q) + public void QRFactor(double[] r, int rowsR, int columnsR, double[] q) { throw new NotImplementedException(); } @@ -521,15 +521,15 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl /// /// On entry, it is the M by N A matrix to factor. On exit, /// it is overwritten with the R matrix of the QR factorization. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// On exit, A M by M matrix that holds the Q matrix of the /// QR factorization. /// The work array. The array must have a length of at least N, /// but should be N*blocksize. The blocksize is machine dependent. On exit, work[0] contains the optimal /// work size value. /// This is similar to the GEQRF and ORGQR LAPACK routines. - public void QRFactor(double[] r, int rRows, int rColumns, double[] q, double[] work) + public void QRFactor(double[] r, int rowsR, int columnsR, double[] q, double[] work) { throw new NotImplementedException(); } @@ -539,14 +539,14 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl /// /// On entry, it is the M by N A matrix to factor. On exit, /// it is overwritten with the R matrix of the QR factorization. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// On exit, A M by M matrix that holds the Q matrix of the /// QR factorization. /// The B matrix. - /// The number of columns of B. + /// The number of columns of B. /// On exit, the solution matrix. - public void QRSolve(double[] r, int rRows, int rColumns, double[] q, double[] b, int bColumns, double[] x) + public void QRSolve(double[] r, int rowsR, int columnsR, double[] q, double[] b, int columnsB, double[] x) { throw new NotImplementedException(); } @@ -556,17 +556,17 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl /// /// On entry, it is the M by N A matrix to factor. On exit, /// it is overwritten with the R matrix of the QR factorization. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// On exit, A M by M matrix that holds the Q matrix of the /// QR factorization. /// The B matrix. - /// The number of columns of B. + /// The number of columns of B. /// On exit, the solution matrix. /// The work array. The array must have a length of at least N, /// but should be N*blocksize. The blocksize is machine dependent. On exit, work[0] contains the optimal /// work size value. - public void QRSolve(double[] r, int rRows, int rColumns, double[] q, double[] b, int bColumns, double[] x, double[] work) + public void QRSolve(double[] r, int rowsR, int columnsR, double[] q, double[] b, int columnsB, double[] x, double[] work) { throw new NotImplementedException(); } @@ -576,12 +576,12 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl /// /// The Q matrix obtained by calling . /// The R matrix obtained by calling . - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// The B matrix. - /// The number of columns of B. + /// The number of columns of B. /// On exit, the solution matrix. - public void QRSolveFactored(double[] q, double[] r, int rRows, int rColumns, double[] b, int bColumns, double[] x) + public void QRSolveFactored(double[] q, double[] r, int rowsR, int columnsR, double[] b, int columnsB, double[] x) { throw new NotImplementedException(); } @@ -591,15 +591,15 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl /// /// Compute the singular U and VT vectors or not. /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// The singular values of A in ascending value. /// If is true, on exit U contains the left /// singular vectors. /// If is true, on exit VT contains the transposed /// right singular vectors. /// This is equivalent to the GESVD LAPACK routine. - public void SingularValueDecomposition(bool computeVectors, double[] a, int aRows, int aColumns, double[] s, double[] u, double[] vt) + public void SingularValueDecomposition(bool computeVectors, double[] a, int rowsA, int columnsA, double[] s, double[] u, double[] vt) { throw new NotImplementedException(); } @@ -609,8 +609,8 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl /// /// Compute the singular U and VT vectors or not. /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// The singular values of A in ascending value. /// If is true, on exit U contains the left /// singular vectors. @@ -620,7 +620,7 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl /// Max(3*Min(M, N) + Max(M, N), 5*Min(M,N)). For complex matrices, 2*Min(M, N) + Max(M, N). /// On exit, work[0] contains the optimal work size value. /// This is equivalent to the GESVD LAPACK routine. - public void SingularValueDecomposition(bool computeVectors, double[] a, int aRows, int aColumns, double[] s, double[] u, double[] vt, double[] work) + public void SingularValueDecomposition(bool computeVectors, double[] a, int rowsA, int columnsA, double[] s, double[] u, double[] vt, double[] work) { throw new NotImplementedException(); } @@ -629,15 +629,15 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl /// Solves A*X=B for X using the singular value decomposition of A. /// /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// The singular values of A in ascending value. /// On exit U contains the left singular vectors. /// On exit VT contains the transposed right singular vectors. /// The B matrix. - /// The number of columns of B. + /// The number of columns of B. /// On exit, the solution matrix. - public void SvdSolve(double[] a, int aRows, int aColumns, double[] s, double[] u, double[] vt, double[] b, int bColumns, double[] x) + public void SvdSolve(double[] a, int rowsA, int columnsA, double[] s, double[] u, double[] vt, double[] b, int columnsB, double[] x) { throw new NotImplementedException(); } @@ -646,18 +646,18 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl /// Solves A*X=B for X using the singular value decomposition of A. /// /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// The singular values of A in ascending value. /// On exit U contains the left singular vectors. /// On exit VT contains the transposed right singular vectors. /// The B matrix. - /// The number of columns of B. + /// The number of columns of B. /// On exit, the solution matrix. /// The work array. For real matrices, the work array should be at least /// Max(3*Min(M, N) + Max(M, N), 5*Min(M,N)). For complex matrices, 2*Min(M, N) + Max(M, N). /// On exit, work[0] contains the optimal work size value. - public void SvdSolve(double[] a, int aRows, int aColumns, double[] s, double[] u, double[] vt, double[] b, int bColumns, double[] x, double[] work) + public void SvdSolve(double[] a, int rowsA, int columnsA, double[] s, double[] u, double[] vt, double[] b, int columnsB, double[] x, double[] work) { throw new NotImplementedException(); } @@ -665,15 +665,15 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl /// /// Solves A*X=B for X using a previously SVD decomposed matrix. /// - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// The s values returned by . /// The left singular vectors returned by . /// The right singular vectors returned by . /// The B matrix. - /// The number of columns of B. + /// The number of columns of B. /// On exit, the solution matrix. - public void SvdSolveFactored(int aRows, int aColumns, double[] s, double[] u, double[] vt, double[] b, int bColumns, double[] x) + public void SvdSolveFactored(int rowsA, int columnsA, double[] s, double[] u, double[] vt, double[] b, int columnsB, double[] x) { throw new NotImplementedException(); } @@ -903,17 +903,17 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl /// Multiples two matrices. result = x * y /// /// The x matrix. - /// The number of rows in the x matrix. - /// The number of columns in the x matrix. + /// The number of rows in the x matrix. + /// The number of columns in the x matrix. /// The y matrix. - /// The number of rows in the y matrix. - /// The number of columns in the y matrix. + /// The number of rows in the y matrix. + /// The number of columns in the y matrix. /// Where to store the result of the multiplication. /// This is a simplified version of the BLAS GEMM routine with alpha /// set to 1.0 and beta set to 0.0, and x and y are not transposed. - public void MatrixMultiply(float[] x, int xRows, int xColumns, float[] y, int yRows, int yColumns, float[] result) + public void MatrixMultiply(float[] x, int rowsX, int columnsX, float[] y, int rowsY, int columnsY, float[] result) { - MatrixMultiplyWithUpdate(Transpose.DontTranspose, Transpose.DontTranspose, 1.0f, x, xRows, xColumns, y, yRows, yColumns, 0.0f, result); + MatrixMultiplyWithUpdate(Transpose.DontTranspose, Transpose.DontTranspose, 1.0f, x, rowsX, columnsX, y, rowsY, columnsY, 0.0f, result); } /// @@ -923,15 +923,15 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl /// How to transpose the matrix. /// The value to scale matrix. /// The a matrix. - /// The number of rows in the matrix. - /// The number of columns in the matrix. + /// The number of rows in the matrix. + /// The number of columns in the matrix. /// The b matrix - /// The number of rows in the matrix. - /// The number of columns in the matrix. + /// The number of rows in the matrix. + /// The number of columns in the matrix. /// The value to scale the matrix. /// The c matrix. public void MatrixMultiplyWithUpdate(Transpose transposeA, Transpose transposeB, float alpha, float[] a, - int aRows, int aColumns, float[] b, int bRows, int bColumns, float beta, float[] c) + int rowsA, int columnsA, float[] b, int rowsB, int columnsB, float beta, float[] c) { if (a == null) { @@ -948,16 +948,16 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl throw new ArgumentNullException("c"); } - var m = transposeA == Transpose.DontTranspose ? aRows : aColumns; - var n = transposeB == Transpose.DontTranspose ? bColumns : bRows; - var k = transposeA == Transpose.DontTranspose ? aColumns : aRows; + var m = transposeA == Transpose.DontTranspose ? rowsA : columnsA; + var n = transposeB == Transpose.DontTranspose ? columnsB : rowsB; + var k = transposeA == Transpose.DontTranspose ? columnsA : rowsA; - if( c.Length != aRows * bColumns) + if( c.Length != rowsA * columnsB) { throw new ArgumentException(Resources.ArgumentMatrixDimensions); } - if (aColumns != bRows) + if (columnsA != rowsB) { throw new ArgumentException(Resources.ArgumentMatrixDimensions); } @@ -1113,12 +1113,12 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl /// Solves A*X=B for X using Cholesky factorization. /// /// The square, positive definite matrix A. - /// The number of rows and columns in A. + /// The number of rows and columns in A. /// The B matrix. - /// The number of rows in the B matrix. - /// The number of columns in the B matrix. + /// The number of rows in the B matrix. + /// The number of columns in the B matrix. /// This is equivalent to the POTRF add POTRS LAPACK routines. - public void CholeskySolve(float[] a, int aOrder, float[] b, int bRows, int bColumns) + public void CholeskySolve(float[] a, int orderA, float[] b, int rowsB, int columnsB) { throw new NotImplementedException(); } @@ -1127,12 +1127,12 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl /// Solves A*X=B for X using a previously factored A matrix. /// /// The square, positive definite matrix A. - /// The number of rows and columns in A. + /// The number of rows and columns in A. /// The B matrix. - /// The number of rows in the B matrix. - /// The number of columns in the B matrix. + /// 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(float[] a, int aOrder, float[] b, int bRows, int bColumns) + public void CholeskySolveFactored(float[] a, int orderA, float[] b, int rowsB, int columnsB) { throw new NotImplementedException(); } @@ -1142,12 +1142,12 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl /// /// On entry, it is the M by N A matrix to factor. On exit, /// it is overwritten with the R matrix of the QR factorization. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// On exit, A M by M matrix that holds the Q matrix of the /// QR factorization. /// This is similar to the GEQRF and ORGQR LAPACK routines. - public void QRFactor(float[] r, int rRows, int rColumns, float[] q) + public void QRFactor(float[] r, int rowsR, int columnsR, float[] q) { throw new NotImplementedException(); } @@ -1157,15 +1157,15 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl /// /// On entry, it is the M by N A matrix to factor. On exit, /// it is overwritten with the R matrix of the QR factorization. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// On exit, A M by M matrix that holds the Q matrix of the /// QR factorization. /// The work array. The array must have a length of at least N, /// but should be N*blocksize. The blocksize is machine dependent. On exit, work[0] contains the optimal /// work size value. /// This is similar to the GEQRF and ORGQR LAPACK routines. - public void QRFactor(float[] r, int rRows, int rColumns, float[] q, float[] work) + public void QRFactor(float[] r, int rowsR, int columnsR, float[] q, float[] work) { throw new NotImplementedException(); } @@ -1175,14 +1175,14 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl /// /// On entry, it is the M by N A matrix to factor. On exit, /// it is overwritten with the R matrix of the QR factorization. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// On exit, A M by M matrix that holds the Q matrix of the /// QR factorization. /// The B matrix. - /// The number of columns of B. + /// The number of columns of B. /// On exit, the solution matrix. - public void QRSolve(float[] r, int rRows, int rColumns, float[] q, float[] b, int bColumns, float[] x) + public void QRSolve(float[] r, int rowsR, int columnsR, float[] q, float[] b, int columnsB, float[] x) { throw new NotImplementedException(); } @@ -1192,17 +1192,17 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl /// /// On entry, it is the M by N A matrix to factor. On exit, /// it is overwritten with the R matrix of the QR factorization. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// On exit, A M by M matrix that holds the Q matrix of the /// QR factorization. /// The B matrix. - /// The number of columns of B. + /// The number of columns of B. /// On exit, the solution matrix. /// The work array. The array must have a length of at least N, /// but should be N*blocksize. The blocksize is machine dependent. On exit, work[0] contains the optimal /// work size value. - public void QRSolve(float[] r, int rRows, int rColumns, float[] q, float[] b, int bColumns, float[] x, float[] work) + public void QRSolve(float[] r, int rowsR, int columnsR, float[] q, float[] b, int columnsB, float[] x, float[] work) { throw new NotImplementedException(); } @@ -1212,12 +1212,12 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl /// /// The Q matrix obtained by calling . /// The R matrix obtained by calling . - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// The B matrix. - /// The number of columns of B. + /// The number of columns of B. /// On exit, the solution matrix. - public void QRSolveFactored(float[] q, float[] r, int rRows, int rColumns, float[] b, int bColumns, float[] x) + public void QRSolveFactored(float[] q, float[] r, int rowsR, int columnsR, float[] b, int columnsB, float[] x) { throw new NotImplementedException(); } @@ -1227,15 +1227,15 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl /// /// Compute the singular U and VT vectors or not. /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// The singular values of A in ascending value. /// If is true, on exit U contains the left /// singular vectors. /// If is true, on exit VT contains the transposed /// right singular vectors. /// This is equivalent to the GESVD LAPACK routine. - public void SingularValueDecomposition(bool computeVectors, float[] a, int aRows, int aColumns, float[] s, float[] u, float[] vt) + public void SingularValueDecomposition(bool computeVectors, float[] a, int rowsA, int columnsA, float[] s, float[] u, float[] vt) { throw new NotImplementedException(); } @@ -1245,8 +1245,8 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl /// /// Compute the singular U and VT vectors or not. /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// The singular values of A in ascending value. /// If is true, on exit U contains the left /// singular vectors. @@ -1256,7 +1256,7 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl /// Max(3*Min(M, N) + Max(M, N), 5*Min(M,N)). For complex matrices, 2*Min(M, N) + Max(M, N). /// On exit, work[0] contains the optimal work size value. /// This is equivalent to the GESVD LAPACK routine. - public void SingularValueDecomposition(bool computeVectors, float[] a, int aRows, int aColumns, float[] s, float[] u, float[] vt, float[] work) + public void SingularValueDecomposition(bool computeVectors, float[] a, int rowsA, int columnsA, float[] s, float[] u, float[] vt, float[] work) { throw new NotImplementedException(); } @@ -1265,15 +1265,15 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl /// Solves A*X=B for X using the singular value decomposition of A. /// /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// The singular values of A in ascending value. /// On exit U contains the left singular vectors. /// On exit VT contains the transposed right singular vectors. /// The B matrix. - /// The number of columns of B. + /// The number of columns of B. /// On exit, the solution matrix. - public void SvdSolve(float[] a, int aRows, int aColumns, float[] s, float[] u, float[] vt, float[] b, int bColumns, float[] x) + public void SvdSolve(float[] a, int rowsA, int columnsA, float[] s, float[] u, float[] vt, float[] b, int columnsB, float[] x) { throw new NotImplementedException(); } @@ -1282,18 +1282,18 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl /// Solves A*X=B for X using the singular value decomposition of A. /// /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// The singular values of A in ascending value. /// On exit U contains the left singular vectors. /// On exit VT contains the transposed right singular vectors. /// The B matrix. - /// The number of columns of B. + /// The number of columns of B. /// On exit, the solution matrix. /// The work array. For real matrices, the work array should be at least /// Max(3*Min(M, N) + Max(M, N), 5*Min(M,N)). For complex matrices, 2*Min(M, N) + Max(M, N). /// On exit, work[0] contains the optimal work size value. - public void SvdSolve(float[] a, int aRows, int aColumns, float[] s, float[] u, float[] vt, float[] b, int bColumns, float[] x, float[] work) + public void SvdSolve(float[] a, int rowsA, int columnsA, float[] s, float[] u, float[] vt, float[] b, int columnsB, float[] x, float[] work) { throw new NotImplementedException(); } @@ -1301,15 +1301,15 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl /// /// Solves A*X=B for X using a previously SVD decomposed matrix. /// - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// The s values returned by . /// The left singular vectors returned by . /// The right singular vectors returned by . /// The B matrix. - /// The number of columns of B. + /// The number of columns of B. /// On exit, the solution matrix. - public void SvdSolveFactored(int aRows, int aColumns, float[] s, float[] u, float[] vt, float[] b, int bColumns, float[] x) + public void SvdSolveFactored(int rowsA, int columnsA, float[] s, float[] u, float[] vt, float[] b, int columnsB, float[] x) { throw new NotImplementedException(); } @@ -1539,17 +1539,17 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl /// Multiples two matrices. result = x * y /// /// The x matrix. - /// The number of rows in the x matrix. - /// The number of columns in the x matrix. + /// The number of rows in the x matrix. + /// The number of columns in the x matrix. /// The y matrix. - /// The number of rows in the y matrix. - /// The number of columns in the y matrix. + /// The number of rows in the y matrix. + /// The number of columns in the y matrix. /// Where to store the result of the multiplication. /// This is a simplified version of the BLAS GEMM routine with alpha /// set to 1.0 and beta set to 0.0, and x and y are not transposed. - public void MatrixMultiply(Complex[] x, int xRows, int xColumns, Complex[] y, int yRows, int yColumns, Complex[] result) + public void MatrixMultiply(Complex[] x, int rowsX, int columnsX, Complex[] y, int rowsY, int columnsY, Complex[] result) { - MatrixMultiplyWithUpdate(Transpose.DontTranspose, Transpose.DontTranspose, Complex.One, x, xRows, xColumns, y, yRows, yColumns, Complex.Zero, result); + MatrixMultiplyWithUpdate(Transpose.DontTranspose, Transpose.DontTranspose, Complex.One, x, rowsX, columnsX, y, rowsY, columnsY, Complex.Zero, result); } /// @@ -1559,15 +1559,15 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl /// How to transpose the matrix. /// The value to scale matrix. /// The a matrix. - /// The number of rows in the matrix. - /// The number of columns in the matrix. + /// The number of rows in the matrix. + /// The number of columns in the matrix. /// The b matrix - /// The number of rows in the matrix. - /// The number of columns in the matrix. + /// The number of rows in the matrix. + /// The number of columns in the matrix. /// The value to scale the matrix. /// The c matrix. public void MatrixMultiplyWithUpdate(Transpose transposeA, Transpose transposeB, Complex alpha, Complex[] a, - int aRows, int aColumns, Complex[] b, int bRows, int bColumns, Complex beta, Complex[] c) + int rowsA, int columnsA, Complex[] b, int rowsB, int columnsB, Complex beta, Complex[] c) { if (a == null) { @@ -1584,16 +1584,16 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl throw new ArgumentNullException("c"); } - var m = transposeA == Transpose.DontTranspose ? aRows : aColumns; - var n = transposeB == Transpose.DontTranspose ? bColumns : bRows; - var k = transposeA == Transpose.DontTranspose ? aColumns : aRows; + var m = transposeA == Transpose.DontTranspose ? rowsA : columnsA; + var n = transposeB == Transpose.DontTranspose ? columnsB : rowsB; + var k = transposeA == Transpose.DontTranspose ? columnsA : rowsA; - if( c.Length != aRows * bColumns) + if( c.Length != rowsA * columnsB) { throw new ArgumentException(Resources.ArgumentMatrixDimensions); } - if (aColumns != bRows) + if (columnsA != rowsB) { throw new ArgumentException(Resources.ArgumentMatrixDimensions); } @@ -1749,12 +1749,12 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl /// Solves A*X=B for X using Cholesky factorization. /// /// The square, positive definite matrix A. - /// The number of rows and columns in A. + /// The number of rows and columns in A. /// The B matrix. - /// The number of rows in the B matrix. - /// The number of columns in the B matrix. + /// The number of rows in the B matrix. + /// The number of columns in the B matrix. /// This is equivalent to the POTRF add POTRS LAPACK routines. - public void CholeskySolve(Complex[] a, int aOrder, Complex[] b, int bRows, int bColumns) + public void CholeskySolve(Complex[] a, int orderA, Complex[] b, int rowsB, int columnsB) { throw new NotImplementedException(); } @@ -1763,12 +1763,12 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl /// Solves A*X=B for X using a previously factored A matrix. /// /// The square, positive definite matrix A. - /// The number of rows and columns in A. + /// The number of rows and columns in A. /// The B matrix. - /// The number of rows in the B matrix. - /// The number of columns in the B matrix. + /// 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(Complex[] a, int aOrder, Complex[] b, int bRows, int bColumns) + public void CholeskySolveFactored(Complex[] a, int orderA, Complex[] b, int rowsB, int columnsB) { throw new NotImplementedException(); } @@ -1778,12 +1778,12 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl /// /// On entry, it is the M by N A matrix to factor. On exit, /// it is overwritten with the R matrix of the QR factorization. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// On exit, A M by M matrix that holds the Q matrix of the /// QR factorization. /// This is similar to the GEQRF and ORGQR LAPACK routines. - public void QRFactor(Complex[] r, int rRows, int rColumns, Complex[] q) + public void QRFactor(Complex[] r, int rowsR, int columnsR, Complex[] q) { throw new NotImplementedException(); } @@ -1793,15 +1793,15 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl /// /// On entry, it is the M by N A matrix to factor. On exit, /// it is overwritten with the R matrix of the QR factorization. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// On exit, A M by M matrix that holds the Q matrix of the /// QR factorization. /// The work array. The array must have a length of at least N, /// but should be N*blocksize. The blocksize is machine dependent. On exit, work[0] contains the optimal /// work size value. /// This is similar to the GEQRF and ORGQR LAPACK routines. - public void QRFactor(Complex[] r, int rRows, int rColumns, Complex[] q, Complex[] work) + public void QRFactor(Complex[] r, int rowsR, int columnsR, Complex[] q, Complex[] work) { throw new NotImplementedException(); } @@ -1811,14 +1811,14 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl /// /// On entry, it is the M by N A matrix to factor. On exit, /// it is overwritten with the R matrix of the QR factorization. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// On exit, A M by M matrix that holds the Q matrix of the /// QR factorization. /// The B matrix. - /// The number of columns of B. + /// The number of columns of B. /// On exit, the solution matrix. - public void QRSolve(Complex[] r, int rRows, int rColumns, Complex[] q, Complex[] b, int bColumns, Complex[] x) + public void QRSolve(Complex[] r, int rowsR, int columnsR, Complex[] q, Complex[] b, int columnsB, Complex[] x) { throw new NotImplementedException(); } @@ -1828,17 +1828,17 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl /// /// On entry, it is the M by N A matrix to factor. On exit, /// it is overwritten with the R matrix of the QR factorization. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// On exit, A M by M matrix that holds the Q matrix of the /// QR factorization. /// The B matrix. - /// The number of columns of B. + /// The number of columns of B. /// On exit, the solution matrix. /// The work array. The array must have a length of at least N, /// but should be N*blocksize. The blocksize is machine dependent. On exit, work[0] contains the optimal /// work size value. - public void QRSolve(Complex[] r, int rRows, int rColumns, Complex[] q, Complex[] b, int bColumns, Complex[] x, Complex[] work) + public void QRSolve(Complex[] r, int rowsR, int columnsR, Complex[] q, Complex[] b, int columnsB, Complex[] x, Complex[] work) { throw new NotImplementedException(); } @@ -1848,12 +1848,12 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl /// /// The Q matrix obtained by calling . /// The R matrix obtained by calling . - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// The B matrix. - /// The number of columns of B. + /// The number of columns of B. /// On exit, the solution matrix. - public void QRSolveFactored(Complex[] q, Complex[] r, int rRows, int rColumns, Complex[] b, int bColumns, Complex[] x) + public void QRSolveFactored(Complex[] q, Complex[] r, int rowsR, int columnsR, Complex[] b, int columnsB, Complex[] x) { throw new NotImplementedException(); } @@ -1863,15 +1863,15 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl /// /// Compute the singular U and VT vectors or not. /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// The singular values of A in ascending value. /// If is true, on exit U contains the left /// singular vectors. /// If is true, on exit VT contains the transposed /// right singular vectors. /// This is equivalent to the GESVD LAPACK routine. - public void SingularValueDecomposition(bool computeVectors, Complex[] a, int aRows, int aColumns, Complex[] s, Complex[] u, Complex[] vt) + public void SingularValueDecomposition(bool computeVectors, Complex[] a, int rowsA, int columnsA, Complex[] s, Complex[] u, Complex[] vt) { throw new NotImplementedException(); } @@ -1881,8 +1881,8 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl /// /// Compute the singular U and VT vectors or not. /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// The singular values of A in ascending value. /// If is true, on exit U contains the left /// singular vectors. @@ -1892,7 +1892,7 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl /// Max(3*Min(M, N) + Max(M, N), 5*Min(M,N)). For complex matrices, 2*Min(M, N) + Max(M, N). /// On exit, work[0] contains the optimal work size value. /// This is equivalent to the GESVD LAPACK routine. - public void SingularValueDecomposition(bool computeVectors, Complex[] a, int aRows, int aColumns, Complex[] s, Complex[] u, Complex[] vt, Complex[] work) + public void SingularValueDecomposition(bool computeVectors, Complex[] a, int rowsA, int columnsA, Complex[] s, Complex[] u, Complex[] vt, Complex[] work) { throw new NotImplementedException(); } @@ -1901,15 +1901,15 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl /// Solves A*X=B for X using the singular value decomposition of A. /// /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// The singular values of A in ascending value. /// On exit U contains the left singular vectors. /// On exit VT contains the transposed right singular vectors. /// The B matrix. - /// The number of columns of B. + /// The number of columns of B. /// On exit, the solution matrix. - public void SvdSolve(Complex[] a, int aRows, int aColumns, Complex[] s, Complex[] u, Complex[] vt, Complex[] b, int bColumns, Complex[] x) + public void SvdSolve(Complex[] a, int rowsA, int columnsA, Complex[] s, Complex[] u, Complex[] vt, Complex[] b, int columnsB, Complex[] x) { throw new NotImplementedException(); } @@ -1918,18 +1918,18 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl /// Solves A*X=B for X using the singular value decomposition of A. /// /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// The singular values of A in ascending value. /// On exit U contains the left singular vectors. /// On exit VT contains the transposed right singular vectors. /// The B matrix. - /// The number of columns of B. + /// The number of columns of B. /// On exit, the solution matrix. /// The work array. For real matrices, the work array should be at least /// Max(3*Min(M, N) + Max(M, N), 5*Min(M,N)). For complex matrices, 2*Min(M, N) + Max(M, N). /// On exit, work[0] contains the optimal work size value. - public void SvdSolve(Complex[] a, int aRows, int aColumns, Complex[] s, Complex[] u, Complex[] vt, Complex[] b, int bColumns, Complex[] x, Complex[] work) + public void SvdSolve(Complex[] a, int rowsA, int columnsA, Complex[] s, Complex[] u, Complex[] vt, Complex[] b, int columnsB, Complex[] x, Complex[] work) { throw new NotImplementedException(); } @@ -1937,15 +1937,15 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl /// /// Solves A*X=B for X using a previously SVD decomposed matrix. /// - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// The s values returned by . /// The left singular vectors returned by . /// The right singular vectors returned by . /// The B matrix. - /// The number of columns of B. + /// The number of columns of B. /// On exit, the solution matrix. - public void SvdSolveFactored(int aRows, int aColumns, Complex[] s, Complex[] u, Complex[] vt, Complex[] b, int bColumns, Complex[] x) + public void SvdSolveFactored(int rowsA, int columnsA, Complex[] s, Complex[] u, Complex[] vt, Complex[] b, int columnsB, Complex[] x) { throw new NotImplementedException(); } @@ -2175,17 +2175,17 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl /// Multiples two matrices. result = x * y /// /// The x matrix. - /// The number of rows in the x matrix. - /// The number of columns in the x matrix. + /// The number of rows in the x matrix. + /// The number of columns in the x matrix. /// The y matrix. - /// The number of rows in the y matrix. - /// The number of columns in the y matrix. + /// The number of rows in the y matrix. + /// The number of columns in the y matrix. /// Where to store the result of the multiplication. /// This is a simplified version of the BLAS GEMM routine with alpha /// set to 1.0 and beta set to 0.0, and x and y are not transposed. - public void MatrixMultiply(Complex32[] x, int xRows, int xColumns, Complex32[] y, int yRows, int yColumns, Complex32[] result) + public void MatrixMultiply(Complex32[] x, int rowsX, int columnsX, Complex32[] y, int rowsY, int columnsY, Complex32[] result) { - MatrixMultiplyWithUpdate(Transpose.DontTranspose, Transpose.DontTranspose, Complex32.One, x, xRows, xColumns, y, yRows, yColumns, Complex32.Zero, result); + MatrixMultiplyWithUpdate(Transpose.DontTranspose, Transpose.DontTranspose, Complex32.One, x, rowsX, columnsX, y, rowsY, columnsY, Complex32.Zero, result); } /// @@ -2195,15 +2195,15 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl /// How to transpose the matrix. /// The value to scale matrix. /// The a matrix. - /// The number of rows in the matrix. - /// The number of columns in the matrix. + /// The number of rows in the matrix. + /// The number of columns in the matrix. /// The b matrix - /// The number of rows in the matrix. - /// The number of columns in the matrix. + /// The number of rows in the matrix. + /// The number of columns in the matrix. /// The value to scale the matrix. /// The c matrix. public void MatrixMultiplyWithUpdate(Transpose transposeA, Transpose transposeB, Complex32 alpha, Complex32[] a, - int aRows, int aColumns, Complex32[] b, int bRows, int bColumns, Complex32 beta, Complex32[] c) + int rowsA, int columnsA, Complex32[] b, int rowsB, int columnsB, Complex32 beta, Complex32[] c) { if (a == null) { @@ -2220,16 +2220,16 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl throw new ArgumentNullException("c"); } - var m = transposeA == Transpose.DontTranspose ? aRows : aColumns; - var n = transposeB == Transpose.DontTranspose ? bColumns : bRows; - var k = transposeA == Transpose.DontTranspose ? aColumns : aRows; + var m = transposeA == Transpose.DontTranspose ? rowsA : columnsA; + var n = transposeB == Transpose.DontTranspose ? columnsB : rowsB; + var k = transposeA == Transpose.DontTranspose ? columnsA : rowsA; - if( c.Length != aRows * bColumns) + if( c.Length != rowsA * columnsB) { throw new ArgumentException(Resources.ArgumentMatrixDimensions); } - if (aColumns != bRows) + if (columnsA != rowsB) { throw new ArgumentException(Resources.ArgumentMatrixDimensions); } @@ -2385,12 +2385,12 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl /// Solves A*X=B for X using Cholesky factorization. /// /// The square, positive definite matrix A. - /// The number of rows and columns in A. + /// The number of rows and columns in A. /// The B matrix. - /// The number of rows in the B matrix. - /// The number of columns in the B matrix. + /// The number of rows in the B matrix. + /// The number of columns in the B matrix. /// This is equivalent to the POTRF add POTRS LAPACK routines. - public void CholeskySolve(Complex32[] a, int aOrder, Complex32[] b, int bRows, int bColumns) + public void CholeskySolve(Complex32[] a, int orderA, Complex32[] b, int rowsB, int columnsB) { throw new NotImplementedException(); } @@ -2399,12 +2399,12 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl /// Solves A*X=B for X using a previously factored A matrix. /// /// The square, positive definite matrix A. - /// The number of rows and columns in A. + /// The number of rows and columns in A. /// The B matrix. - /// The number of rows in the B matrix. - /// The number of columns in the B matrix. + /// 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(Complex32[] a, int aOrder, Complex32[] b, int bRows, int bColumns) + public void CholeskySolveFactored(Complex32[] a, int orderA, Complex32[] b, int rowsB, int columnsB) { throw new NotImplementedException(); } @@ -2414,12 +2414,12 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl /// /// On entry, it is the M by N A matrix to factor. On exit, /// it is overwritten with the R matrix of the QR factorization. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// On exit, A M by M matrix that holds the Q matrix of the /// QR factorization. /// This is similar to the GEQRF and ORGQR LAPACK routines. - public void QRFactor(Complex32[] r, int rRows, int rColumns, Complex32[] q) + public void QRFactor(Complex32[] r, int rowsR, int columnsR, Complex32[] q) { throw new NotImplementedException(); } @@ -2429,15 +2429,15 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl /// /// On entry, it is the M by N A matrix to factor. On exit, /// it is overwritten with the R matrix of the QR factorization. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// On exit, A M by M matrix that holds the Q matrix of the /// QR factorization. /// The work array. The array must have a length of at least N, /// but should be N*blocksize. The blocksize is machine dependent. On exit, work[0] contains the optimal /// work size value. /// This is similar to the GEQRF and ORGQR LAPACK routines. - public void QRFactor(Complex32[] r, int rRows, int rColumns, Complex32[] q, Complex32[] work) + public void QRFactor(Complex32[] r, int rowsR, int columnsR, Complex32[] q, Complex32[] work) { throw new NotImplementedException(); } @@ -2447,14 +2447,14 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl /// /// On entry, it is the M by N A matrix to factor. On exit, /// it is overwritten with the R matrix of the QR factorization. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// On exit, A M by M matrix that holds the Q matrix of the /// QR factorization. /// The B matrix. - /// The number of columns of B. + /// The number of columns of B. /// On exit, the solution matrix. - public void QRSolve(Complex32[] r, int rRows, int rColumns, Complex32[] q, Complex32[] b, int bColumns, Complex32[] x) + public void QRSolve(Complex32[] r, int rowsR, int columnsR, Complex32[] q, Complex32[] b, int columnsB, Complex32[] x) { throw new NotImplementedException(); } @@ -2464,17 +2464,17 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl /// /// On entry, it is the M by N A matrix to factor. On exit, /// it is overwritten with the R matrix of the QR factorization. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// On exit, A M by M matrix that holds the Q matrix of the /// QR factorization. /// The B matrix. - /// The number of columns of B. + /// The number of columns of B. /// On exit, the solution matrix. /// The work array. The array must have a length of at least N, /// but should be N*blocksize. The blocksize is machine dependent. On exit, work[0] contains the optimal /// work size value. - public void QRSolve(Complex32[] r, int rRows, int rColumns, Complex32[] q, Complex32[] b, int bColumns, Complex32[] x, Complex32[] work) + public void QRSolve(Complex32[] r, int rowsR, int columnsR, Complex32[] q, Complex32[] b, int columnsB, Complex32[] x, Complex32[] work) { throw new NotImplementedException(); } @@ -2484,12 +2484,12 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl /// /// The Q matrix obtained by calling . /// The R matrix obtained by calling . - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// The B matrix. - /// The number of columns of B. + /// The number of columns of B. /// On exit, the solution matrix. - public void QRSolveFactored(Complex32[] q, Complex32[] r, int rRows, int rColumns, Complex32[] b, int bColumns, Complex32[] x) + public void QRSolveFactored(Complex32[] q, Complex32[] r, int rowsR, int columnsR, Complex32[] b, int columnsB, Complex32[] x) { throw new NotImplementedException(); } @@ -2499,15 +2499,15 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl /// /// Compute the singular U and VT vectors or not. /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// The singular values of A in ascending value. /// If is true, on exit U contains the left /// singular vectors. /// If is true, on exit VT contains the transposed /// right singular vectors. /// This is equivalent to the GESVD LAPACK routine. - public void SingularValueDecomposition(bool computeVectors, Complex32[] a, int aRows, int aColumns, Complex32[] s, Complex32[] u, Complex32[] vt) + public void SingularValueDecomposition(bool computeVectors, Complex32[] a, int rowsA, int columnsA, Complex32[] s, Complex32[] u, Complex32[] vt) { throw new NotImplementedException(); } @@ -2517,8 +2517,8 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl /// /// Compute the singular U and VT vectors or not. /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// The singular values of A in ascending value. /// If is true, on exit U contains the left /// singular vectors. @@ -2528,7 +2528,7 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl /// Max(3*Min(M, N) + Max(M, N), 5*Min(M,N)). For complex matrices, 2*Min(M, N) + Max(M, N). /// On exit, work[0] contains the optimal work size value. /// This is equivalent to the GESVD LAPACK routine. - public void SingularValueDecomposition(bool computeVectors, Complex32[] a, int aRows, int aColumns, Complex32[] s, Complex32[] u, Complex32[] vt, Complex32[] work) + public void SingularValueDecomposition(bool computeVectors, Complex32[] a, int rowsA, int columnsA, Complex32[] s, Complex32[] u, Complex32[] vt, Complex32[] work) { throw new NotImplementedException(); } @@ -2537,15 +2537,15 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl /// Solves A*X=B for X using the singular value decomposition of A. /// /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// The singular values of A in ascending value. /// On exit U contains the left singular vectors. /// On exit VT contains the transposed right singular vectors. /// The B matrix. - /// The number of columns of B. + /// The number of columns of B. /// On exit, the solution matrix. - public void SvdSolve(Complex32[] a, int aRows, int aColumns, Complex32[] s, Complex32[] u, Complex32[] vt, Complex32[] b, int bColumns, Complex32[] x) + public void SvdSolve(Complex32[] a, int rowsA, int columnsA, Complex32[] s, Complex32[] u, Complex32[] vt, Complex32[] b, int columnsB, Complex32[] x) { throw new NotImplementedException(); } @@ -2554,18 +2554,18 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl /// Solves A*X=B for X using the singular value decomposition of A. /// /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// The singular values of A in ascending value. /// On exit U contains the left singular vectors. /// On exit VT contains the transposed right singular vectors. /// The B matrix. - /// The number of columns of B. + /// The number of columns of B. /// On exit, the solution matrix. /// The work array. For real matrices, the work array should be at least /// Max(3*Min(M, N) + Max(M, N), 5*Min(M,N)). For complex matrices, 2*Min(M, N) + Max(M, N). /// On exit, work[0] contains the optimal work size value. - public void SvdSolve(Complex32[] a, int aRows, int aColumns, Complex32[] s, Complex32[] u, Complex32[] vt, Complex32[] b, int bColumns, Complex32[] x, Complex32[] work) + public void SvdSolve(Complex32[] a, int rowsA, int columnsA, Complex32[] s, Complex32[] u, Complex32[] vt, Complex32[] b, int columnsB, Complex32[] x, Complex32[] work) { throw new NotImplementedException(); } @@ -2573,15 +2573,15 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl /// /// Solves A*X=B for X using a previously SVD decomposed matrix. /// - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. /// The s values returned by . /// The left singular vectors returned by . /// The right singular vectors returned by . /// The B matrix. - /// The number of columns of B. + /// The number of columns of B. /// On exit, the solution matrix. - public void SvdSolveFactored(int aRows, int aColumns, Complex32[] s, Complex32[] u, Complex32[] vt, Complex32[] b, int bColumns, Complex32[] x) + public void SvdSolveFactored(int rowsA, int columnsA, Complex32[] s, Complex32[] u, Complex32[] vt, Complex32[] b, int columnsB, Complex32[] x) { throw new NotImplementedException(); } diff --git a/src/Numerics/LinearAlgebra/Complex/DenseMatrix.cs b/src/Numerics/LinearAlgebra/Complex/DenseMatrix.cs index 5367bf76..c970b583 100644 --- a/src/Numerics/LinearAlgebra/Complex/DenseMatrix.cs +++ b/src/Numerics/LinearAlgebra/Complex/DenseMatrix.cs @@ -718,16 +718,6 @@ namespace MathNet.Numerics.LinearAlgebra.Complex return val1 / val2; } - /// - /// Is equal to one? - /// - /// Value to check - /// True if one; otherwise false - protected sealed override bool IsOneT(Complex val1) - { - return val1.AlmostEqual(Complex.One); - } - /// /// Take absolute value /// diff --git a/src/Numerics/LinearAlgebra/Complex/DenseVector.cs b/src/Numerics/LinearAlgebra/Complex/DenseVector.cs index 52f9c5ba..335686da 100644 --- a/src/Numerics/LinearAlgebra/Complex/DenseVector.cs +++ b/src/Numerics/LinearAlgebra/Complex/DenseVector.cs @@ -1438,10 +1438,18 @@ namespace MathNet.Numerics.LinearAlgebra.Complex tmp.CopyTo(target); } - CommonParallel.For( - 0, - Count, - index => target[index] = this[index].Conjugate()); + var otherVector = target as DenseVector; + if (otherVector == null) + { + base.Conjugate(target); + } + else + { + CommonParallel.For( + 0, + Count, + index => otherVector.Data[index] = Data[index].Conjugate()); + } } #region Simple arithmetic of type T @@ -1489,16 +1497,6 @@ namespace MathNet.Numerics.LinearAlgebra.Complex return val1 / val2; } - /// - /// Is equal to one? - /// - /// Value to check - /// True if one; otherwise false - protected sealed override bool IsOneT(Complex val1) - { - return Complex.One.AlmostEqual(val1); - } - /// /// Take absolute value /// diff --git a/src/Numerics/LinearAlgebra/Complex/DiagonalMatrix.cs b/src/Numerics/LinearAlgebra/Complex/DiagonalMatrix.cs index 0bf51c79..ca00ec8a 100644 --- a/src/Numerics/LinearAlgebra/Complex/DiagonalMatrix.cs +++ b/src/Numerics/LinearAlgebra/Complex/DiagonalMatrix.cs @@ -1597,16 +1597,8 @@ namespace MathNet.Numerics.LinearAlgebra.Complex } var matrix = CreateMatrix(numberOfRows, numberOfColumns); - CommonParallel.For( - 0, - ColumnCount, - j => - { - for (var i = 0; i < matrix.RowCount; i++) - { - matrix[i, j] = new Complex(distribution.Sample(), distribution.Sample()); - } - }); + var mn = Math.Min(numberOfRows, numberOfColumns); + CommonParallel.For(0, mn, i => matrix[i, i] = new Complex(distribution.Sample(), distribution.Sample())); return matrix; } @@ -1635,16 +1627,8 @@ namespace MathNet.Numerics.LinearAlgebra.Complex } var matrix = CreateMatrix(numberOfRows, numberOfColumns); - CommonParallel.For( - 0, - ColumnCount, - j => - { - for (var i = 0; i < matrix.RowCount; i++) - { - matrix[i, j] = new Complex(distribution.Sample(), distribution.Sample()); - } - }); + var mn = Math.Min(numberOfRows, numberOfColumns); + CommonParallel.For(0, mn, i => matrix[i, i] = new Complex(distribution.Sample(), distribution.Sample())); return matrix; } @@ -1694,16 +1678,6 @@ namespace MathNet.Numerics.LinearAlgebra.Complex return val1 / val2; } - /// - /// Is equal to one? - /// - /// Value to check - /// True if one; otherwise false - protected sealed override bool IsOneT(Complex val1) - { - return Complex.One.AlmostEqual(val1); - } - /// /// Take absolute value /// diff --git a/src/Numerics/LinearAlgebra/Complex/Factorization/DenseCholesky.cs b/src/Numerics/LinearAlgebra/Complex/Factorization/DenseCholesky.cs index 374a8a64..69ccf087 100644 --- a/src/Numerics/LinearAlgebra/Complex/Factorization/DenseCholesky.cs +++ b/src/Numerics/LinearAlgebra/Complex/Factorization/DenseCholesky.cs @@ -209,15 +209,6 @@ namespace MathNet.Numerics.LinearAlgebra.Complex.Factorization { return val1.NaturalLogarithm(); } - - /// - /// Get value of type T equal to one - /// - /// One value - protected sealed override Complex OneValueT - { - get { return Complex.One; } - } #endregion } } diff --git a/src/Numerics/LinearAlgebra/Complex/Factorization/DenseLU.cs b/src/Numerics/LinearAlgebra/Complex/Factorization/DenseLU.cs index baf323fb..058e4742 100644 --- a/src/Numerics/LinearAlgebra/Complex/Factorization/DenseLU.cs +++ b/src/Numerics/LinearAlgebra/Complex/Factorization/DenseLU.cs @@ -201,24 +201,6 @@ namespace MathNet.Numerics.LinearAlgebra.Complex.Factorization { return val1 * val2; } - - /// - /// Get value of type T equal to one - /// - /// One value - protected sealed override Complex OneValueT - { - get { return Complex.One; } - } - - /// - /// Get value of type T equal to minus one - /// - /// One value - protected sealed override Complex MinusOneValueT - { - get { return -Complex.One; } - } #endregion } } diff --git a/src/Numerics/LinearAlgebra/Complex/Factorization/DenseQR.cs b/src/Numerics/LinearAlgebra/Complex/Factorization/DenseQR.cs index dec02790..1fa36654 100644 --- a/src/Numerics/LinearAlgebra/Complex/Factorization/DenseQR.cs +++ b/src/Numerics/LinearAlgebra/Complex/Factorization/DenseQR.cs @@ -190,14 +190,6 @@ namespace MathNet.Numerics.LinearAlgebra.Complex.Factorization return val1.Magnitude; } - /// - /// Get value of type T equal to one - /// - /// One value - protected sealed override Complex OneValueT - { - get { return Complex.One; } - } #endregion } } diff --git a/src/Numerics/LinearAlgebra/Complex/Factorization/DenseSvd.cs b/src/Numerics/LinearAlgebra/Complex/Factorization/DenseSvd.cs index 88fa07c6..aa98288e 100644 --- a/src/Numerics/LinearAlgebra/Complex/Factorization/DenseSvd.cs +++ b/src/Numerics/LinearAlgebra/Complex/Factorization/DenseSvd.cs @@ -202,16 +202,6 @@ namespace MathNet.Numerics.LinearAlgebra.Complex.Factorization { return val1.Magnitude; } - - /// - /// Get value of type T equal to one - /// - /// One value - protected sealed override Complex OneValueT - { - get { return Complex.One; } - } - #endregion } } diff --git a/src/Numerics/LinearAlgebra/Complex/Factorization/GramSchmidt.cs b/src/Numerics/LinearAlgebra/Complex/Factorization/GramSchmidt.cs index 5e0c85ad..58ded73f 100644 --- a/src/Numerics/LinearAlgebra/Complex/Factorization/GramSchmidt.cs +++ b/src/Numerics/LinearAlgebra/Complex/Factorization/GramSchmidt.cs @@ -312,14 +312,6 @@ namespace MathNet.Numerics.LinearAlgebra.Complex.Factorization return val1.Magnitude; } - /// - /// Get value of type T equal to one - /// - /// One value - protected sealed override Complex OneValueT - { - get { return Complex.One; } - } #endregion } } diff --git a/src/Numerics/LinearAlgebra/Complex/Factorization/UserCholesky.cs b/src/Numerics/LinearAlgebra/Complex/Factorization/UserCholesky.cs index 1417c631..1b40a88a 100644 --- a/src/Numerics/LinearAlgebra/Complex/Factorization/UserCholesky.cs +++ b/src/Numerics/LinearAlgebra/Complex/Factorization/UserCholesky.cs @@ -254,15 +254,6 @@ namespace MathNet.Numerics.LinearAlgebra.Complex.Factorization { return val1.NaturalLogarithm(); } - - /// - /// Get value of type T equal to one - /// - /// One value - protected sealed override Complex OneValueT - { - get { return Complex.One; } - } #endregion } } diff --git a/src/Numerics/LinearAlgebra/Complex/Factorization/UserLU.cs b/src/Numerics/LinearAlgebra/Complex/Factorization/UserLU.cs index a0768b70..03e17611 100644 --- a/src/Numerics/LinearAlgebra/Complex/Factorization/UserLU.cs +++ b/src/Numerics/LinearAlgebra/Complex/Factorization/UserLU.cs @@ -312,24 +312,6 @@ namespace MathNet.Numerics.LinearAlgebra.Complex.Factorization { return val1 * val2; } - - /// - /// Get value of type T equal to one - /// - /// One value - protected sealed override Complex OneValueT - { - get { return Complex.One; } - } - - /// - /// Get value of type T equal to minus one - /// - /// One value - protected sealed override Complex MinusOneValueT - { - get { return -Complex.One; } - } #endregion } } diff --git a/src/Numerics/LinearAlgebra/Complex/Factorization/UserQR.cs b/src/Numerics/LinearAlgebra/Complex/Factorization/UserQR.cs index 293fb4f5..dd64b30f 100644 --- a/src/Numerics/LinearAlgebra/Complex/Factorization/UserQR.cs +++ b/src/Numerics/LinearAlgebra/Complex/Factorization/UserQR.cs @@ -352,15 +352,6 @@ namespace MathNet.Numerics.LinearAlgebra.Complex.Factorization { return val1.Magnitude; } - - /// - /// Get value of type T equal to one - /// - /// One value - protected sealed override Complex OneValueT - { - get { return Complex.One; } - } #endregion } } diff --git a/src/Numerics/LinearAlgebra/Complex/Factorization/UserSvd.cs b/src/Numerics/LinearAlgebra/Complex/Factorization/UserSvd.cs index 0b2ab5fa..172de227 100644 --- a/src/Numerics/LinearAlgebra/Complex/Factorization/UserSvd.cs +++ b/src/Numerics/LinearAlgebra/Complex/Factorization/UserSvd.cs @@ -960,15 +960,6 @@ namespace MathNet.Numerics.LinearAlgebra.Complex.Factorization return val1.Magnitude; } - /// - /// Get value of type T equal to one - /// - /// One value - protected sealed override Complex OneValueT - { - get { return Complex.One; } - } - #endregion } } diff --git a/src/Numerics/LinearAlgebra/Complex/SparseMatrix.cs b/src/Numerics/LinearAlgebra/Complex/SparseMatrix.cs index 1d556ad3..9adc572d 100644 --- a/src/Numerics/LinearAlgebra/Complex/SparseMatrix.cs +++ b/src/Numerics/LinearAlgebra/Complex/SparseMatrix.cs @@ -1686,16 +1686,6 @@ namespace MathNet.Numerics.LinearAlgebra.Complex return val1 / val2; } - /// - /// Is equal to one? - /// - /// Value to check - /// True if one; otherwise false - protected sealed override bool IsOneT(Complex val1) - { - return Complex.One.AlmostEqual(val1); - } - /// /// Take absolute value /// diff --git a/src/Numerics/LinearAlgebra/Complex/SparseVector.cs b/src/Numerics/LinearAlgebra/Complex/SparseVector.cs index ad0e11cd..c93190e9 100644 --- a/src/Numerics/LinearAlgebra/Complex/SparseVector.cs +++ b/src/Numerics/LinearAlgebra/Complex/SparseVector.cs @@ -376,10 +376,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex var otherVector = target as SparseVector; if (otherVector == null) { - CommonParallel.For( - 0, - Count, - index => target[index] = this[index].Conjugate()); + base.Conjugate(target); } else { @@ -1671,16 +1668,6 @@ namespace MathNet.Numerics.LinearAlgebra.Complex return val1 / val2; } - /// - /// Is equal to one? - /// - /// Value to check - /// True if one; otherwise false - protected sealed override bool IsOneT(Complex val1) - { - return Complex.One.AlmostEqual(val1); - } - /// /// Take absolute value /// diff --git a/src/Numerics/LinearAlgebra/Double/DenseMatrix.cs b/src/Numerics/LinearAlgebra/Double/DenseMatrix.cs index 35709121..ad8acd72 100644 --- a/src/Numerics/LinearAlgebra/Double/DenseMatrix.cs +++ b/src/Numerics/LinearAlgebra/Double/DenseMatrix.cs @@ -651,15 +651,6 @@ namespace MathNet.Numerics.LinearAlgebra.Double return matrix; } - /// - /// Returns the conjugate transpose of this matrix. - /// - /// The conjugate transpose of this matrix. - public override Matrix ConjugateTranspose() - { - throw new NotSupportedException("ConjugateTranspose is not supported for real matricies"); - } - #region Simple arithmetic of type T /// /// Add two values T+T @@ -705,16 +696,6 @@ namespace MathNet.Numerics.LinearAlgebra.Double return val1 / val2; } - /// - /// Is equal to one? - /// - /// Value to check - /// True if one; otherwise false - protected sealed override bool IsOneT(double val1) - { - return 1.0.AlmostEqualInDecimalPlaces(val1, 15); - } - /// /// Take absolute value /// diff --git a/src/Numerics/LinearAlgebra/Double/DenseVector.cs b/src/Numerics/LinearAlgebra/Double/DenseVector.cs index b95fe041..89fe5674 100644 --- a/src/Numerics/LinearAlgebra/Double/DenseVector.cs +++ b/src/Numerics/LinearAlgebra/Double/DenseVector.cs @@ -1498,15 +1498,6 @@ namespace MathNet.Numerics.LinearAlgebra.Double Array.Clear(Data, 0, Data.Length); } - /// - /// Conjugates vector and save result to - /// - /// Target vector - public override void Conjugate(Vector target) - { - throw new NotSupportedException("Conjugate is not supported for real vectors"); - } - #region Simple arithmetic of type T /// /// Add two values T+T @@ -1552,16 +1543,6 @@ namespace MathNet.Numerics.LinearAlgebra.Double return val1 / val2; } - /// - /// Is equal to one? - /// - /// Value to check - /// True if one; otherwise false - protected sealed override bool IsOneT(double val1) - { - return val1 == 1.0; - } - /// /// Take absolute value /// diff --git a/src/Numerics/LinearAlgebra/Double/DiagonalMatrix.cs b/src/Numerics/LinearAlgebra/Double/DiagonalMatrix.cs index 20e94025..1c50acca 100644 --- a/src/Numerics/LinearAlgebra/Double/DiagonalMatrix.cs +++ b/src/Numerics/LinearAlgebra/Double/DiagonalMatrix.cs @@ -1584,16 +1584,8 @@ namespace MathNet.Numerics.LinearAlgebra.Double } var matrix = CreateMatrix(numberOfRows, numberOfColumns); - CommonParallel.For( - 0, - ColumnCount, - j => - { - for (var i = 0; i < matrix.RowCount; i++) - { - matrix[i, j] = distribution.Sample(); - } - }); + var mn = Math.Min(numberOfRows, numberOfColumns); + CommonParallel.For(0, mn, i => matrix[i, i] = distribution.Sample()); return matrix; } @@ -1622,16 +1614,8 @@ namespace MathNet.Numerics.LinearAlgebra.Double } var matrix = CreateMatrix(numberOfRows, numberOfColumns); - CommonParallel.For( - 0, - ColumnCount, - j => - { - for (var i = 0; i < matrix.RowCount; i++) - { - matrix[i, j] = distribution.Sample(); - } - }); + var mn = Math.Min(numberOfRows, numberOfColumns); + CommonParallel.For(0, mn, i => matrix[i, i] = distribution.Sample()); return matrix; } @@ -1671,15 +1655,6 @@ namespace MathNet.Numerics.LinearAlgebra.Double return stringBuilder.ToString(); } - /// - /// Returns the conjugate transpose of this matrix. - /// - /// The conjugate transpose of this matrix. - public override Matrix ConjugateTranspose() - { - throw new NotSupportedException("ConjugateTranspose is not supported for real matricies"); - } - #region Simple arithmetic of type T /// /// Add two values T+T @@ -1725,16 +1700,6 @@ namespace MathNet.Numerics.LinearAlgebra.Double return val1 / val2; } - /// - /// Is equal to one? - /// - /// Value to check - /// True if one; otherwise false - protected sealed override bool IsOneT(double val1) - { - return 1.0.AlmostEqualInDecimalPlaces(val1, 15); - } - /// /// Take absolute value /// diff --git a/src/Numerics/LinearAlgebra/Double/Factorization/DenseCholesky.cs b/src/Numerics/LinearAlgebra/Double/Factorization/DenseCholesky.cs index f927d00d..32a60253 100644 --- a/src/Numerics/LinearAlgebra/Double/Factorization/DenseCholesky.cs +++ b/src/Numerics/LinearAlgebra/Double/Factorization/DenseCholesky.cs @@ -207,15 +207,6 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Factorization { return Math.Log(val1); } - - /// - /// Get value of type T equal to one - /// - /// One value - protected sealed override double OneValueT - { - get { return 1.0; } - } #endregion } } diff --git a/src/Numerics/LinearAlgebra/Double/Factorization/DenseLU.cs b/src/Numerics/LinearAlgebra/Double/Factorization/DenseLU.cs index a09477bc..b0bcacbe 100644 --- a/src/Numerics/LinearAlgebra/Double/Factorization/DenseLU.cs +++ b/src/Numerics/LinearAlgebra/Double/Factorization/DenseLU.cs @@ -199,24 +199,6 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Factorization { return val1 * val2; } - - /// - /// Get value of type T equal to one - /// - /// One value - protected sealed override double OneValueT - { - get { return 1.0; } - } - - /// - /// Get value of type T equal to minus one - /// - /// One value - protected sealed override double MinusOneValueT - { - get { return -1.0; } - } #endregion } } diff --git a/src/Numerics/LinearAlgebra/Double/Factorization/DenseQR.cs b/src/Numerics/LinearAlgebra/Double/Factorization/DenseQR.cs index 10e9192e..0d559497 100644 --- a/src/Numerics/LinearAlgebra/Double/Factorization/DenseQR.cs +++ b/src/Numerics/LinearAlgebra/Double/Factorization/DenseQR.cs @@ -189,14 +189,6 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Factorization return Math.Abs(val1); } - /// - /// Get value of type T equal to one - /// - /// One value - protected sealed override double OneValueT - { - get { return 1.0; } - } #endregion } } diff --git a/src/Numerics/LinearAlgebra/Double/Factorization/DenseSvd.cs b/src/Numerics/LinearAlgebra/Double/Factorization/DenseSvd.cs index 39ed780a..17845971 100644 --- a/src/Numerics/LinearAlgebra/Double/Factorization/DenseSvd.cs +++ b/src/Numerics/LinearAlgebra/Double/Factorization/DenseSvd.cs @@ -201,16 +201,6 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Factorization { return Math.Abs(val1); } - - /// - /// Get value of type T equal to one - /// - /// One value - protected sealed override double OneValueT - { - get { return 1.0; } - } - #endregion } } diff --git a/src/Numerics/LinearAlgebra/Double/Factorization/GramSchmidt.cs b/src/Numerics/LinearAlgebra/Double/Factorization/GramSchmidt.cs index a906bbb9..7fdc0cd4 100644 --- a/src/Numerics/LinearAlgebra/Double/Factorization/GramSchmidt.cs +++ b/src/Numerics/LinearAlgebra/Double/Factorization/GramSchmidt.cs @@ -305,15 +305,6 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Factorization { return Math.Abs(val1); } - - /// - /// Get value of type T equal to one - /// - /// One value - protected sealed override double OneValueT - { - get { return 1.0; } - } #endregion } } diff --git a/src/Numerics/LinearAlgebra/Double/Factorization/SparseCholesky.cs b/src/Numerics/LinearAlgebra/Double/Factorization/SparseCholesky.cs index dc949953..4ef7ddfc 100644 --- a/src/Numerics/LinearAlgebra/Double/Factorization/SparseCholesky.cs +++ b/src/Numerics/LinearAlgebra/Double/Factorization/SparseCholesky.cs @@ -253,16 +253,6 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Factorization { return Math.Log(val1); } - - /// - /// Get value of type T equal to one - /// - /// One value - protected sealed override double OneValueT - { - get { return 1.0; } - } - #endregion } } diff --git a/src/Numerics/LinearAlgebra/Double/Factorization/SparseLU.cs b/src/Numerics/LinearAlgebra/Double/Factorization/SparseLU.cs index eabffe81..45d8ef9c 100644 --- a/src/Numerics/LinearAlgebra/Double/Factorization/SparseLU.cs +++ b/src/Numerics/LinearAlgebra/Double/Factorization/SparseLU.cs @@ -312,23 +312,6 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Factorization return val1 * val2; } - /// - /// Get value of type T equal to one - /// - /// One value - protected sealed override double OneValueT - { - get { return 1.0; } - } - - /// - /// Get value of type T equal to minus one - /// - /// One value - protected sealed override double MinusOneValueT - { - get { return -1.0; } - } #endregion } } diff --git a/src/Numerics/LinearAlgebra/Double/Factorization/SparseQR.cs b/src/Numerics/LinearAlgebra/Double/Factorization/SparseQR.cs index 95b4d28f..70a8dced 100644 --- a/src/Numerics/LinearAlgebra/Double/Factorization/SparseQR.cs +++ b/src/Numerics/LinearAlgebra/Double/Factorization/SparseQR.cs @@ -353,15 +353,6 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Factorization { return Math.Abs(val1); } - - /// - /// Get value of type T equal to one - /// - /// One value - protected sealed override double OneValueT - { - get { return 1.0; } - } #endregion } } diff --git a/src/Numerics/LinearAlgebra/Double/Factorization/SparseSvd.cs b/src/Numerics/LinearAlgebra/Double/Factorization/SparseSvd.cs index bf698f5e..00c87a56 100644 --- a/src/Numerics/LinearAlgebra/Double/Factorization/SparseSvd.cs +++ b/src/Numerics/LinearAlgebra/Double/Factorization/SparseSvd.cs @@ -945,15 +945,6 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Factorization return Math.Abs(val1); } - /// - /// Get value of type T equal to one - /// - /// One value - protected sealed override double OneValueT - { - get { return 1.0; } - } - #endregion } } diff --git a/src/Numerics/LinearAlgebra/Double/Factorization/UserCholesky.cs b/src/Numerics/LinearAlgebra/Double/Factorization/UserCholesky.cs index 4728cf6b..91dacd86 100644 --- a/src/Numerics/LinearAlgebra/Double/Factorization/UserCholesky.cs +++ b/src/Numerics/LinearAlgebra/Double/Factorization/UserCholesky.cs @@ -98,40 +98,6 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Factorization } } - /// - /// Gets the determinant of the matrix for which the Cholesky matrix was computed. - /// - public override double Determinant - { - get - { - var det = 1.0; - for (var j = 0; j < CholeskyFactor.RowCount; j++) - { - det *= CholeskyFactor[j, j] * CholeskyFactor[j, j]; - } - - return det; - } - } - - /// - /// Gets the log determinant of the matrix for which the Cholesky matrix was computed. - /// - public override double DeterminantLn - { - get - { - var det = 0.0; - for (var j = 0; j < CholeskyFactor.RowCount; j++) - { - det += 2.0 * Math.Log(CholeskyFactor[j, j]); - } - - return det; - } - } - /// /// Solves a system of linear equations, AX = B, with A Cholesky factorized. /// @@ -287,16 +253,6 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Factorization { return Math.Log(val1); } - - /// - /// Get value of type T equal to one - /// - /// One value - protected sealed override double OneValueT - { - get { return 1.0; } - } - #endregion } } diff --git a/src/Numerics/LinearAlgebra/Double/Factorization/UserLU.cs b/src/Numerics/LinearAlgebra/Double/Factorization/UserLU.cs index a3b49e61..e29c68cb 100644 --- a/src/Numerics/LinearAlgebra/Double/Factorization/UserLU.cs +++ b/src/Numerics/LinearAlgebra/Double/Factorization/UserLU.cs @@ -312,23 +312,6 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Factorization return val1 * val2; } - /// - /// Get value of type T equal to one - /// - /// One value - protected sealed override double OneValueT - { - get { return 1.0; } - } - - /// - /// Get value of type T equal to minus one - /// - /// One value - protected sealed override double MinusOneValueT - { - get { return -1.0; } - } #endregion } } diff --git a/src/Numerics/LinearAlgebra/Double/Factorization/UserQR.cs b/src/Numerics/LinearAlgebra/Double/Factorization/UserQR.cs index ee3fcfb2..f3f349c2 100644 --- a/src/Numerics/LinearAlgebra/Double/Factorization/UserQR.cs +++ b/src/Numerics/LinearAlgebra/Double/Factorization/UserQR.cs @@ -352,15 +352,6 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Factorization { return Math.Abs(val1); } - - /// - /// Get value of type T equal to one - /// - /// One value - protected sealed override double OneValueT - { - get { return 1.0; } - } #endregion } } diff --git a/src/Numerics/LinearAlgebra/Double/Factorization/UserSvd.cs b/src/Numerics/LinearAlgebra/Double/Factorization/UserSvd.cs index 0526535b..1eed31dd 100644 --- a/src/Numerics/LinearAlgebra/Double/Factorization/UserSvd.cs +++ b/src/Numerics/LinearAlgebra/Double/Factorization/UserSvd.cs @@ -945,15 +945,6 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Factorization return Math.Abs(val1); } - /// - /// Get value of type T equal to one - /// - /// One value - protected sealed override double OneValueT - { - get { return 1.0; } - } - #endregion } } diff --git a/src/Numerics/LinearAlgebra/Double/SparseMatrix.cs b/src/Numerics/LinearAlgebra/Double/SparseMatrix.cs index 44b804e8..3715ebe8 100644 --- a/src/Numerics/LinearAlgebra/Double/SparseMatrix.cs +++ b/src/Numerics/LinearAlgebra/Double/SparseMatrix.cs @@ -1648,15 +1648,6 @@ namespace MathNet.Numerics.LinearAlgebra.Double return stringBuilder.ToString(); } - /// - /// Returns the conjugate transpose of this matrix. - /// - /// The conjugate transpose of this matrix. - public override Matrix ConjugateTranspose() - { - throw new NotSupportedException("ConjugateTranspose is not supported for real matricies"); - } - #region Simple arithmetic of type T /// /// Add two values T+T @@ -1702,16 +1693,6 @@ namespace MathNet.Numerics.LinearAlgebra.Double return val1 / val2; } - /// - /// Is equal to one? - /// - /// Value to check - /// True if one; otherwise false - protected sealed override bool IsOneT(double val1) - { - return 1.0.AlmostEqualInDecimalPlaces(val1, 15); - } - /// /// Take absolute value /// diff --git a/src/Numerics/LinearAlgebra/Double/SparseVector.cs b/src/Numerics/LinearAlgebra/Double/SparseVector.cs index 66e3854e..da281ae0 100644 --- a/src/Numerics/LinearAlgebra/Double/SparseVector.cs +++ b/src/Numerics/LinearAlgebra/Double/SparseVector.cs @@ -1618,15 +1618,6 @@ namespace MathNet.Numerics.LinearAlgebra.Double return true; } - /// - /// Conjugates vector and save result to - /// - /// Target vector - public override void Conjugate(Vector target) - { - throw new NotSupportedException("Conjugate is not supported for real vectors"); - } - #region Simple arithmetic of type T /// /// Add two values T+T @@ -1672,16 +1663,6 @@ namespace MathNet.Numerics.LinearAlgebra.Double return val1 / val2; } - /// - /// Is equal to one? - /// - /// Value to check - /// True if one; otherwise false - protected sealed override bool IsOneT(double val1) - { - return val1 == 1.0; - } - /// /// Take absolute value /// diff --git a/src/Numerics/LinearAlgebra/Generic/Factorization/Cholesky.cs b/src/Numerics/LinearAlgebra/Generic/Factorization/Cholesky.cs index 5031cdda..4bb40314 100644 --- a/src/Numerics/LinearAlgebra/Generic/Factorization/Cholesky.cs +++ b/src/Numerics/LinearAlgebra/Generic/Factorization/Cholesky.cs @@ -65,6 +65,17 @@ namespace MathNet.Numerics.LinearAlgebra.Generic.Factorization return new LinearAlgebra.Double.Factorization.UserCholesky(matrix as Matrix) as Cholesky; } + if (typeof(T) == typeof(float)) + { + var dense = matrix as LinearAlgebra.Single.DenseMatrix; + if (dense != null) + { + return new LinearAlgebra.Single.Factorization.DenseCholesky(dense) as Cholesky; + } + + return new LinearAlgebra.Single.Factorization.UserCholesky(matrix as Matrix) as Cholesky; + } + if (typeof(T) == typeof(Complex)) { var dense = matrix as LinearAlgebra.Complex.DenseMatrix; @@ -213,9 +224,36 @@ namespace MathNet.Numerics.LinearAlgebra.Generic.Factorization /// Gets value of type T equal to one /// /// One value - protected abstract T OneValueT + private static T OneValueT { - get; + get + { + if (typeof(T) == typeof(Complex)) + { + object one = Complex.One; + return (T)one; + } + + if (typeof(T) == typeof(Complex32)) + { + object one = Complex32.One; + return (T)one; + } + + if (typeof(T) == typeof(double)) + { + object one = 1.0d; + return (T)one; + } + + if (typeof(T) == typeof(float)) + { + object one = 1.0f; + return (T)one; + } + + throw new NotSupportedException(); + } } #endregion } diff --git a/src/Numerics/LinearAlgebra/Generic/Factorization/ExtensionMethods.cs b/src/Numerics/LinearAlgebra/Generic/Factorization/ExtensionMethods.cs index abab7755..32acc809 100644 --- a/src/Numerics/LinearAlgebra/Generic/Factorization/ExtensionMethods.cs +++ b/src/Numerics/LinearAlgebra/Generic/Factorization/ExtensionMethods.cs @@ -85,6 +85,11 @@ namespace MathNet.Numerics.LinearAlgebra.Generic.Factorization return new LinearAlgebra.Double.Factorization.GramSchmidt(matrix as Matrix) as QR; } + if (typeof(T) == typeof(float)) + { + return new LinearAlgebra.Single.Factorization.GramSchmidt(matrix as Matrix) as QR; + } + if (typeof(T) == typeof(Complex)) { return new LinearAlgebra.Complex.Factorization.GramSchmidt(matrix as Matrix) as QR; diff --git a/src/Numerics/LinearAlgebra/Generic/Factorization/LU.cs b/src/Numerics/LinearAlgebra/Generic/Factorization/LU.cs index 60f34bb4..811f062f 100644 --- a/src/Numerics/LinearAlgebra/Generic/Factorization/LU.cs +++ b/src/Numerics/LinearAlgebra/Generic/Factorization/LU.cs @@ -80,6 +80,17 @@ namespace MathNet.Numerics.LinearAlgebra.Generic.Factorization return new LinearAlgebra.Double.Factorization.UserLU(matrix as Matrix) as LU; } + if (typeof(T) == typeof(float)) + { + var dense = matrix as LinearAlgebra.Single.DenseMatrix; + if (dense != null) + { + return new LinearAlgebra.Single.Factorization.DenseLU(dense) as LU; + } + + return new LinearAlgebra.Single.Factorization.UserLU(matrix as Matrix) as LU; + } + if (typeof(T) == typeof(Complex)) { var dense = matrix as LinearAlgebra.Complex.DenseMatrix; @@ -227,18 +238,72 @@ namespace MathNet.Numerics.LinearAlgebra.Generic.Factorization /// Gets value of type T equal to one /// /// One value - protected abstract T OneValueT + private static T OneValueT { - get; + get + { + if (typeof(T) == typeof(Complex)) + { + object one = Complex.One; + return (T)one; + } + + if (typeof(T) == typeof(Complex32)) + { + object one = Complex32.One; + return (T)one; + } + + if (typeof(T) == typeof(double)) + { + object one = 1.0d; + return (T)one; + } + + if (typeof(T) == typeof(float)) + { + object one = 1.0f; + return (T)one; + } + + throw new NotSupportedException(); + } } /// /// Gets value of type T equal to one /// /// One value - protected abstract T MinusOneValueT + private static T MinusOneValueT { - get; + get + { + if (typeof(T) == typeof(Complex)) + { + object one = -Complex.One; + return (T)one; + } + + if (typeof(T) == typeof(Complex32)) + { + object one = -Complex32.One; + return (T)one; + } + + if (typeof(T) == typeof(double)) + { + object one = -1.0d; + return (T)one; + } + + if (typeof(T) == typeof(float)) + { + object one = -1.0f; + return (T)one; + } + + throw new NotSupportedException(); + } } #endregion diff --git a/src/Numerics/LinearAlgebra/Generic/Factorization/QR.cs b/src/Numerics/LinearAlgebra/Generic/Factorization/QR.cs index a973eb4b..23ea413e 100644 --- a/src/Numerics/LinearAlgebra/Generic/Factorization/QR.cs +++ b/src/Numerics/LinearAlgebra/Generic/Factorization/QR.cs @@ -80,6 +80,17 @@ namespace MathNet.Numerics.LinearAlgebra.Generic.Factorization return new LinearAlgebra.Double.Factorization.UserQR(matrix as Matrix) as QR; } + if (typeof(T) == typeof(float)) + { + var dense = matrix as LinearAlgebra.Single.DenseMatrix; + if (dense != null) + { + return new LinearAlgebra.Single.Factorization.DenseQR(dense) as QR; + } + + return new LinearAlgebra.Single.Factorization.UserQR(matrix as Matrix) as QR; + } + if (typeof(T) == typeof(Complex)) { var dense = matrix as LinearAlgebra.Complex.DenseMatrix; @@ -132,7 +143,7 @@ namespace MathNet.Numerics.LinearAlgebra.Generic.Factorization for (var i = 0; i < MatrixR.ColumnCount; i++) { det = MultiplyT(det, MatrixR.At(i, i)); - if (AbsoluteT(MatrixR.At(i, i)).AlmostEqualInDecimalPlaces(0.0, 15)) + if (AbsoluteT(MatrixR.At(i, i)).AlmostEqualInDecimalPlaces(0.0, (typeof(T) == typeof(float)) ? 7 : 15)) { return 0; } @@ -152,7 +163,7 @@ namespace MathNet.Numerics.LinearAlgebra.Generic.Factorization { for (var i = 0; i < MatrixR.ColumnCount; i++) { - if (AbsoluteT(MatrixR.At(i, i)).AlmostEqualInDecimalPlaces(0.0, 15)) + if (AbsoluteT(MatrixR.At(i, i)).AlmostEqualInDecimalPlaces(0.0, (typeof(T) == typeof(float)) ? 7 : 15)) { return false; } @@ -232,9 +243,36 @@ namespace MathNet.Numerics.LinearAlgebra.Generic.Factorization /// Gets value of type T equal to one /// /// One value - protected abstract T OneValueT + private static T OneValueT { - get; + get + { + if (typeof(T) == typeof(Complex)) + { + object one = Complex.One; + return (T)one; + } + + if (typeof(T) == typeof(Complex32)) + { + object one = Complex32.One; + return (T)one; + } + + if (typeof(T) == typeof(double)) + { + object one = 1.0d; + return (T)one; + } + + if (typeof(T) == typeof(float)) + { + object one = 1.0f; + return (T)one; + } + + throw new NotSupportedException(); + } } #endregion diff --git a/src/Numerics/LinearAlgebra/Generic/Factorization/Svd.cs b/src/Numerics/LinearAlgebra/Generic/Factorization/Svd.cs index c4713a9a..9294c215 100644 --- a/src/Numerics/LinearAlgebra/Generic/Factorization/Svd.cs +++ b/src/Numerics/LinearAlgebra/Generic/Factorization/Svd.cs @@ -31,6 +31,7 @@ namespace MathNet.Numerics.LinearAlgebra.Generic.Factorization { using System; + using System.Linq; using System.Numerics; using Generic; using Properties; @@ -97,19 +98,7 @@ namespace MathNet.Numerics.LinearAlgebra.Generic.Factorization { get { - var eps = Math.Pow(2.0, -52.0); - var tol = Math.Max(MatrixU.RowCount, MatrixVT.ColumnCount) * AbsoluteT(VectorS[0]) * eps; - var nm = Math.Min(MatrixU.RowCount, MatrixVT.ColumnCount); - var rank = 0; - for (var h = 0; h < nm; h++) - { - if (AbsoluteT(VectorS[h]) > tol) - { - rank++; - } - } - - return rank; + return VectorS.Count(t => !AbsoluteT(t).AlmostEqualInDecimalPlaces(0.0, (typeof(T) == typeof(float)) ? 7 : 15)); } } @@ -132,6 +121,17 @@ namespace MathNet.Numerics.LinearAlgebra.Generic.Factorization return new LinearAlgebra.Double.Factorization.UserSvd(matrix as Matrix, computeVectors) as Svd; } + if (typeof(T) == typeof(float)) + { + var dense = matrix as LinearAlgebra.Single.DenseMatrix; + if (dense != null) + { + return new LinearAlgebra.Single.Factorization.DenseSvd(dense, computeVectors) as Svd; + } + + return new LinearAlgebra.Single.Factorization.UserSvd(matrix as Matrix, computeVectors) as Svd; + } + if (typeof(T) == typeof(Complex)) { var dense = matrix as LinearAlgebra.Complex.DenseMatrix; @@ -187,7 +187,7 @@ namespace MathNet.Numerics.LinearAlgebra.Generic.Factorization for (var i = 0; i < VectorS.Count; i++) { det = MultiplyT(det, VectorS[i]); - if (AbsoluteT(VectorS[i]).AlmostEqualInDecimalPlaces(0.0, 15)) + if (AbsoluteT(VectorS[i]).AlmostEqualInDecimalPlaces(0.0, (typeof(T) == typeof(float)) ? 7 : 15)) { return 0; } @@ -320,9 +320,36 @@ namespace MathNet.Numerics.LinearAlgebra.Generic.Factorization /// Gets value of type T equal to one /// /// One value - protected abstract T OneValueT + private static T OneValueT { - get; + get + { + if (typeof(T) == typeof(Complex)) + { + object one = Complex.One; + return (T)one; + } + + if (typeof(T) == typeof(Complex32)) + { + object one = Complex32.One; + return (T)one; + } + + if (typeof(T) == typeof(double)) + { + object one = 1.0d; + return (T)one; + } + + if (typeof(T) == typeof(float)) + { + object one = 1.0f; + return (T)one; + } + + throw new NotSupportedException(); + } } #endregion diff --git a/src/Numerics/LinearAlgebra/Generic/Matrix.cs b/src/Numerics/LinearAlgebra/Generic/Matrix.cs index 3d4cd00a..378819ef 100644 --- a/src/Numerics/LinearAlgebra/Generic/Matrix.cs +++ b/src/Numerics/LinearAlgebra/Generic/Matrix.cs @@ -1491,7 +1491,25 @@ namespace MathNet.Numerics.LinearAlgebra.Generic /// Returns the conjugate transpose of this matrix. /// /// The conjugate transpose of this matrix. - public abstract Matrix ConjugateTranspose(); + public virtual Matrix ConjugateTranspose() + { + // In case of real return regulart transpose + if ((typeof(T) == typeof(double)) || ((typeof(T) == typeof(float)))) + { + return Transpose(); + } + + var ret = CreateMatrix(ColumnCount, RowCount); + for (var j = 0; j < ColumnCount; j++) + { + for (var i = 0; i < RowCount; i++) + { + ret.At(j, i, ConjugateT(At(i, j))); + } + } + + return ret; + } /// /// Permute the rows of a matrix according to a permutation. @@ -1866,19 +1884,80 @@ namespace MathNet.Numerics.LinearAlgebra.Generic /// Result of divide protected abstract T DivideT(T val1, T val2); + /// + /// Take absolute value + /// + /// Source alue + /// True if one; otherwise false + protected abstract double AbsoluteT(T val1); + /// /// Is equal to one? /// /// Value to check /// True if one; otherwise false - protected abstract bool IsOneT(T val1); + private static bool IsOneT(T val1) + { + if (typeof(T) == typeof(Complex)) + { + object obj1 = val1; + return Complex.One.AlmostEqual((Complex)obj1); + } + + if (typeof(T) == typeof(Complex32)) + { + object obj1 = val1; + return Complex32.One.AlmostEqual((Complex32)obj1); + } + + if (typeof(T) == typeof(double)) + { + object obj1 = val1; + return 1.0.AlmostEqualInDecimalPlaces((double)obj1, 15); + } + + if (typeof(T) == typeof(float)) + { + object obj1 = val1; + return 1.0f.AlmostEqualInDecimalPlaces((float)obj1, 7); + } + + throw new NotSupportedException(); + } /// - /// Take absolute value + /// Conjugate complex value. In real case the same value is returned /// - /// Source alue - /// True if one; otherwise false - protected abstract double AbsoluteT(T val1); + /// Value to conjugate + /// Conjugated value (complex) or the same (real) + private static T ConjugateT(T val1) + { + if (typeof(T) == typeof(Complex)) + { + object obj = val1; + object conj = Complex.Conjugate((Complex)obj); + return (T)(conj); + } + + if (typeof(T) == typeof(Complex32)) + { + object obj = val1; + object conj = ((Complex32)obj).Conjugate(); + return (T)(conj); + } + + if (typeof(T) == typeof(double)) + { + return val1; + } + + if (typeof(T) == typeof(float)) + { + return val1; + } + + throw new NotSupportedException(); + } #endregion } } diff --git a/src/Numerics/LinearAlgebra/Generic/Vector.cs b/src/Numerics/LinearAlgebra/Generic/Vector.cs index 739076ff..2d2127e3 100644 --- a/src/Numerics/LinearAlgebra/Generic/Vector.cs +++ b/src/Numerics/LinearAlgebra/Generic/Vector.cs @@ -1121,7 +1121,37 @@ namespace MathNet.Numerics.LinearAlgebra.Generic /// Conjugates vector and save result to /// /// Target vector - public abstract void Conjugate(Vector target); + public virtual void Conjugate(Vector target) + { + // In case of real return copy of vector + if ((typeof(T) == typeof(double)) || ((typeof(T) == typeof(float)))) + { + CopyTo(target); + return; + } + + if (target == null) + { + throw new ArgumentNullException("target"); + } + + if (Count != target.Count) + { + throw new ArgumentException(Resources.ArgumentVectorsSameLength, "target"); + } + + if (ReferenceEquals(this, target)) + { + var tmp = CreateVector(Count); + Conjugate(tmp); + tmp.CopyTo(target); + } + + CommonParallel.For( + 0, + Count, + index => target[index] = ConjugateT(this[index])); + } #region Copying and Conversion @@ -1607,19 +1637,80 @@ namespace MathNet.Numerics.LinearAlgebra.Generic /// Result of divide protected abstract T DivideT(T val1, T val2); + /// + /// Take absolute value + /// + /// Source alue + /// True if one; otherwise false + protected abstract double AbsoluteT(T val1); + /// /// Is equal to one? /// /// Value to check /// True if one; otherwise false - protected abstract bool IsOneT(T val1); + private static bool IsOneT(T val1) + { + if (typeof(T) == typeof(Complex)) + { + object obj1 = val1; + return Complex.One.AlmostEqual((Complex)obj1); + } + + if (typeof(T) == typeof(Complex32)) + { + object obj1 = val1; + return Complex32.One.AlmostEqual((Complex32)obj1); + } + + if (typeof(T) == typeof(double)) + { + object obj1 = val1; + return 1.0.AlmostEqualInDecimalPlaces((double)obj1, 15); + } + + if (typeof(T) == typeof(float)) + { + object obj1 = val1; + return 1.0f.AlmostEqualInDecimalPlaces((float)obj1, 7); + } + + throw new NotSupportedException(); + } /// - /// Take absolute value + /// Conjugate complex value. In real case the same value is returned /// - /// Source alue - /// True if one; otherwise false - protected abstract double AbsoluteT(T val1); + /// Value to conjugate + /// Conjugated value (complex) or the same (real) + private static T ConjugateT(T val1) + { + if (typeof(T) == typeof(Complex)) + { + object obj = val1; + object conj = Complex.Conjugate((Complex)obj); + return (T)(conj); + } + + if (typeof(T) == typeof(Complex32)) + { + object obj = val1; + object conj = ((Complex32)obj).Conjugate(); + return (T)(conj); + } + + if (typeof(T) == typeof(double)) + { + return val1; + } + + if (typeof(T) == typeof(float)) + { + return val1; + } + + throw new NotSupportedException(); + } #endregion } } diff --git a/src/Numerics/LinearAlgebra/Single/DenseMatrix.cs b/src/Numerics/LinearAlgebra/Single/DenseMatrix.cs new file mode 100644 index 00000000..4fab2249 --- /dev/null +++ b/src/Numerics/LinearAlgebra/Single/DenseMatrix.cs @@ -0,0 +1,710 @@ +// +// 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. +// + +namespace MathNet.Numerics.LinearAlgebra.Single +{ + using System; + using Distributions; + using Generic; + using Properties; + using Threading; + + /// + /// A Matrix class with dense storage. The underlying storage is a one dimensional array in column-major order. + /// + public class DenseMatrix : Matrix + { + /// + /// Initializes a new instance of the class. This matrix is square with a given size. + /// + /// the size of the square matrix. + /// + /// If is less than one. + /// + public DenseMatrix(int order) + : base(order) + { + Data = new float[order * order]; + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// The number of rows. + /// + /// + /// The number of columns. + /// + public DenseMatrix(int rows, int columns) + : base(rows, columns) + { + Data = new float[rows * columns]; + } + + /// + /// Initializes a new instance of the class with all entries set to a particular value. + /// + /// + /// The number of rows. + /// + /// + /// The number of columns. + /// + /// The value which we assign to each element of the matrix. + 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; + } + } + + /// + /// Initializes a new instance of the class from a one dimensional array. This constructor + /// will reference the one dimensional array and not copy it. + /// + /// The number of rows. + /// The number of columns. + /// The one dimensional array to create this matrix from. This array should store the matrix in column-major order. + public DenseMatrix(int rows, int columns, float[] array) + : base(rows, columns) + { + Data = array; + } + + /// + /// Initializes a new instance of the class from a 2D array. This constructor + /// will allocate a completely new memory block for storing the dense matrix. + /// + /// The 2D array to create this matrix from. + 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]; + } + } + } + + /// + /// Gets the matrix's data. + /// + /// The matrix's data. + internal float[] Data + { + get; + private set; + } + + /// + /// Creates a DenseMatrix for the given number of rows and columns. + /// + /// + /// The number of rows. + /// + /// + /// The number of columns. + /// + /// + /// A DenseMatrix with the given dimensions. + /// + public override Matrix CreateMatrix(int numberOfRows, int numberOfColumns) + { + return new DenseMatrix(numberOfRows, numberOfColumns); + } + + /// + /// Creates a with a the given dimension. + /// + /// The size of the vector. + /// + /// A with the given dimension. + /// + public override Vector CreateVector(int size) + { + return new DenseVector(size); + } + + /// + /// Retrieves the requested element without range checking. + /// + /// + /// The row of the element. + /// + /// + /// The column of the element. + /// + /// + /// The requested element. + /// + public override float At(int row, int column) + { + return Data[(column * RowCount) + row]; + } + + /// + /// Sets the value of the given element. + /// + /// + /// The row of the element. + /// + /// + /// The column of the element. + /// + /// + /// The value to set the element to. + /// + public override void At(int row, int column, float value) + { + Data[(column * RowCount) + row] = value; + } + + /// + /// Sets all values to zero. + /// + public override void Clear() + { + Array.Clear(Data, 0, Data.Length); + } + + /// + /// Returns the transpose of this matrix. + /// + /// The transpose of this matrix. + public override Matrix 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; + } + + /// Calculates the L1 norm. + /// The L1 norm of the matrix. + 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; + } + + /// Calculates the Frobenius norm of this matrix. + /// The Frobenius norm of this matrix. + 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; + } + + /// Calculates the infinity norm of this matrix. + /// The infinity norm of this matrix. + 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 + + /// + /// Adds another matrix to this matrix. The result will be written into this matrix. + /// + /// The matrix to add to this matrix. + /// If the other matrix is . + /// If the two matrices don't have the same dimensions. + public override void Add(Matrix other) + { + var m = other as DenseMatrix; + if (m == null) + { + base.Add(other); + } + else + { + Add(m); + } + } + + /// + /// Adds another to this matrix. The result will be written into this matrix. + /// + /// The to add to this matrix. + /// If the other matrix is . + /// If the two matrices don't have the same dimensions. + 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); + } + + /// + /// Subtracts another matrix from this matrix. The result will be written into this matrix. + /// + /// The matrix to subtract. + /// If the other matrix is . + /// If the two matrices don't have the same dimensions. + public override void Subtract(Matrix other) + { + var m = other as DenseMatrix; + if (m == null) + { + base.Subtract(other); + } + else + { + Subtract(m); + } + } + + /// + /// Subtracts another from this matrix. The result will be written into this matrix. + /// + /// The to subtract. + /// If the other matrix is . + /// If the two matrices don't have the same dimensions. + 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); + } + + /// + /// Multiplies each element of this matrix with a scalar. + /// + /// The scalar to multiply with. + public override void Multiply(float scalar) + { + Control.LinearAlgebraProvider.ScaleArray(scalar, Data); + } + + /// + /// Multiplies this dense matrix with another dense matrix and places the results into the result dense matrix. + /// + /// The matrix to multiply with. + /// The result of the multiplication. + /// If the other matrix is . + /// If the result matrix is . + /// If this.Columns != other.Rows. + /// If the result matrix's dimensions are not the this.Rows x other.Columns. + public override void Multiply(Matrix other, Matrix 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); + } + } + + /// + /// Multiplies this matrix with another matrix and returns the result. + /// + /// The matrix to multiply with. + /// If this.Columns != other.Rows. + /// If the other matrix is . + /// The result of multiplication. + public override Matrix Multiply(Matrix 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; + } + + /// + /// Multiplies this dense matrix with transpose of another dense matrix and places the results into the result dense matrix. + /// + /// The matrix to multiply with. + /// The result of the multiplication. + /// If the other matrix is . + /// If the result matrix is . + /// If this.Columns != other.Rows. + /// If the result matrix's dimensions are not the this.Rows x other.Columns. + public override void TransposeAndMultiply(Matrix other, Matrix 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); + } + + /// + /// Multiplies this matrix with transpose of another matrix and returns the result. + /// + /// The matrix to multiply with. + /// If this.Columns != other.Rows. + /// If the other matrix is . + /// The result of multiplication. + public override Matrix TransposeAndMultiply(Matrix 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; + } + + /// + /// Multiplies two dense matrices. + /// + /// The left matrix to multiply. + /// The right matrix to multiply. + /// The result of multiplication. + /// If or is . + /// If the dimensions of or don't conform. + 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. + + /// + /// Initializes a square with all zero's except for ones on the diagonal. + /// + /// the size of the square matrix. + /// A dense identity matrix. + /// + /// If is less than one. + /// + 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 + + /// + /// Negates each element of this matrix. + /// + public override void Negate() + { + Multiply(-1); + } + + /// + /// Generates matrix with random elements. + /// + /// Number of rows. + /// Number of columns. + /// Continuous Random Distribution or Source + /// + /// An numberOfRows-by-numberOfColumns matrix with elements distributed according to the provided distribution. + /// + /// If the parameter is not positive. + /// If the parameter is not positive. + public override Matrix 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; + } + + /// + /// Generates matrix with random elements. + /// + /// Number of rows. + /// Number of columns. + /// Continuous Random Distribution or Source + /// + /// An numberOfRows-by-numberOfColumns matrix with elements distributed according to the provided distribution. + /// + /// If the parameter is not positive. + /// If the parameter is not positive. + public override Matrix 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 + /// + /// Add two values T+T + /// + /// Left operand value + /// Right operand value + /// Result of addition + protected sealed override float AddT(float val1, float val2) + { + return val1 + val2; + } + + /// + /// Subtract two values T-T + /// + /// Left operand value + /// Right operand value + /// Result of subtract + protected sealed override float SubtractT(float val1, float val2) + { + return val1 - val2; + } + + /// + /// Multiply two values T*T + /// + /// Left operand value + /// Right operand value + /// Result of multiplication + protected sealed override float MultiplyT(float val1, float val2) + { + return val1 * val2; + } + + /// + /// Divide two values T/T + /// + /// Left operand value + /// Right operand value + /// Result of divide + protected sealed override float DivideT(float val1, float val2) + { + return val1 / val2; + } + + /// + /// Take absolute value + /// + /// Source alue + /// True if one; otherwise false + protected sealed override double AbsoluteT(float val1) + { + return Math.Abs(val1); + } + #endregion + } +} diff --git a/src/Numerics/LinearAlgebra/Single/DenseVector.cs b/src/Numerics/LinearAlgebra/Single/DenseVector.cs new file mode 100644 index 00000000..f1459ece --- /dev/null +++ b/src/Numerics/LinearAlgebra/Single/DenseVector.cs @@ -0,0 +1,1557 @@ +// +// 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. +// + +namespace MathNet.Numerics.LinearAlgebra.Single +{ + using System; + using System.Collections.Generic; + using System.Globalization; + using System.Linq; + using Distributions; + using Generic; + using NumberTheory; + using Properties; + using Threading; + + /// + /// A vector using dense storage. + /// + public class DenseVector : Vector + { + /// + /// Initializes a new instance of the class with a given size. + /// + /// + /// the size of the vector. + /// + /// + /// If is less than one. + /// + public DenseVector(int size) + : base(size) + { + Data = new float[size]; + } + + /// + /// Initializes a new instance of the class with a given size + /// and each element set to the given value; + /// + /// + /// the size of the vector. + /// + /// + /// the value to set each element to. + /// + /// + /// If is less than one. + /// + public DenseVector(int size, float value) + : this(size) + { + for (var index = 0; index < Data.Length; index++) + { + Data[index] = value; + } + } + + /// + /// Initializes a new instance of the class by + /// copying the values from another. + /// + /// + /// The vector to create the new vector from. + /// + public DenseVector(Vector other) + : this(other.Count) + { + var vector = other as DenseVector; + if (vector == null) + { + CommonParallel.For( + 0, + Data.Length, + index => this[index] = other[index]); + } + else + { + Buffer.BlockCopy(vector.Data, 0, Data, 0, Data.Length * Constants.SizeOfFloat); + } + } + + /// + /// Initializes a new instance of the class by + /// copying the values from another. + /// + /// + /// The vector to create the new vector from. + /// + public DenseVector(DenseVector other) + : this(other.Count) + { + Buffer.BlockCopy(other.Data, 0, Data, 0, Data.Length * Constants.SizeOfFloat); + } + + /// + /// Initializes a new instance of the class for an array. + /// + /// The array to create this vector from. + /// The vector does not copy the array, but keeps a reference to it. Any + /// changes to the vector will also change the array. + public DenseVector(float[] array) : base(array.Length) + { + Data = array; + } + + /// + /// Gets the vector's internal data. + /// + /// The vector's internal data. + /// Changing values in the array also changes the corresponding value in vector. Use with care. + internal float[] Data + { + get; + private set; + } + + /// + /// Returns a reference to the internal data structure. + /// + /// The DenseVector whose internal data we are + /// returning. + /// + /// A reference to the internal date of the given vector. + /// + public static implicit operator float[](DenseVector vector) + { + if (vector == null) + { + throw new ArgumentNullException(); + } + + return vector.Data; + } + + /// + /// Returns a vector bound directly to a reference of the provided array. + /// + /// The array to bind to the DenseVector object. + /// + /// A DenseVector whose values are bound to the given array. + /// + public static implicit operator DenseVector(float[] array) + { + if (array == null) + { + throw new ArgumentNullException(); + } + + return new DenseVector(array); + } + + /// + /// Create a matrix based on this vector in column form (one single column). + /// + /// This vector as a column matrix. + public override Matrix ToColumnMatrix() + { + var matrix = new DenseMatrix(Count, 1); + for (var i = 0; i < Data.Length; i++) + { + matrix[i, 0] = Data[i]; + } + + return matrix; + } + + /// + /// Create a matrix based on this vector in row form (one single row). + /// + /// This vector as a row matrix. + public override Matrix ToRowMatrix() + { + var matrix = new DenseMatrix(1, Count); + for (var i = 0; i < Data.Length; i++) + { + matrix[0, i] = Data[i]; + } + + return matrix; + } + + /// Gets or sets the value at the given . + /// The index of the value to get or set. + /// The value of the vector at the given . + /// If is negative or + /// greater than the size of the vector. + public override float this[int index] + { + get + { + return Data[index]; + } + + set + { + Data[index] = value; + } + } + + /// + /// Creates a matrix with the given dimensions using the same storage type + /// as this vector. + /// + /// + /// The number of rows. + /// + /// + /// The number of columns. + /// + /// + /// A matrix with the given dimensions. + /// + public override Matrix CreateMatrix(int rows, int columns) + { + return new DenseMatrix(rows, columns); + } + + /// + /// Creates a Vector of the given size using the same storage type + /// as this vector. + /// + /// + /// The size of the Vector to create. + /// + /// + /// The new Vector. + /// + public override Vector CreateVector(int size) + { + return new DenseVector(size); + } + + /// + /// Copies the values of this vector into the target vector. + /// + /// + /// The vector to copy elements into. + /// + /// + /// If is . + /// + /// + /// If is not the same size as this vector. + /// + public override void CopyTo(Vector target) + { + if (target == null) + { + throw new ArgumentNullException("target"); + } + + if (Count != target.Count) + { + throw new ArgumentException(Resources.ArgumentVectorsSameLength, "target"); + } + + if (ReferenceEquals(this, target)) + { + return; + } + + var otherVector = target as DenseVector; + if (otherVector == null) + { + CommonParallel.For( + 0, + Data.Length, + index => target[index] = Data[index]); + } + else + { + Buffer.BlockCopy(Data, 0, otherVector.Data, 0, Data.Length * Constants.SizeOfFloat); + } + } + + /// + /// Adds a scalar to each element of the vector. + /// + /// The scalar to add. + /// A copy of the vector with the scalar added. + public override Vector Add(float scalar) + { + if (scalar == 0.0) + { + return Clone(); + } + + var copy = (DenseVector)Clone(); + CommonParallel.For( + 0, + Data.Length, + index => copy.Data[index] += scalar); + return copy; + } + + /// + /// Adds a scalar to each element of the vector and stores the result in the result vector. + /// + /// The scalar to add. + /// The vector to store the result of the addition. + /// If the result vector is . + /// If this vector and are not the same size. + public override void Add(float scalar, Vector result) + { + if (result == null) + { + throw new ArgumentNullException("result"); + } + + if (Count != result.Count) + { + throw new ArgumentException(Resources.ArgumentVectorsSameLength, "result"); + } + + var dense = result as DenseVector; + if (dense == null) + { + base.Add(scalar, result); + } + else + { + CommonParallel.For( + 0, + Data.Length, + index => dense.Data[index] = Data[index] + scalar); + } + } + + /// + /// Adds another vector to this vector. + /// + /// The vector to add to this one. + /// A new vector containing the sum of both vectors. + /// If the other vector is . + /// If this vector and are not the same size. + public override Vector Add(Vector other) + { + if (other == null) + { + throw new ArgumentNullException("other"); + } + + if (Count != other.Count) + { + throw new ArgumentException(Resources.ArgumentVectorsSameLength, "other"); + } + + var denseVector = other as DenseVector; + + if (denseVector == null) + { + return base.Add(other); + } + + var copy = (DenseVector)Clone(); + Control.LinearAlgebraProvider.AddVectorToScaledVector(copy.Data, 1.0f, denseVector.Data); + return copy; + } + + /// + /// Adds another vector to this vector and stores the result into the result vector. + /// + /// The vector to add to this one. + /// The vector to store the result of the addition. + /// If the other vector is . + /// If the result vector is . + /// If this vector and are not the same size. + /// If this vector and are not the same size. + public override void Add(Vector other, Vector result) + { + if (result == null) + { + throw new ArgumentNullException("result"); + } + + if (Count != other.Count) + { + throw new ArgumentException(Resources.ArgumentVectorsSameLength, "other"); + } + + if (Count != result.Count) + { + throw new ArgumentException(Resources.ArgumentVectorsSameLength, "result"); + } + + if (ReferenceEquals(this, result) || ReferenceEquals(other, result)) + { + var tmp = Add(other); + tmp.CopyTo(result); + } + else + { + var rdense = result as DenseVector; + var odense = other as DenseVector; + if (rdense != null && odense != null) + { + CopyTo(result); + Control.LinearAlgebraProvider.AddVectorToScaledVector(rdense.Data, 1.0f, odense.Data); + } + else + { + CommonParallel.For( + 0, + Data.Length, + index => result[index] = Data[index] + other[index]); + } + } + } + + /// + /// Returns a Vector containing the same values of . + /// + /// This method is included for completeness. + /// The vector to get the values from. + /// A vector containing a the same values as . + /// If is . + public static Vector operator +(DenseVector rightSide) + { + if (rightSide == null) + { + throw new ArgumentNullException("rightSide"); + } + + return rightSide.Plus(); + } + + /// + /// Adds two Vectors together and returns the results. + /// + /// One of the vectors to add. + /// The other vector to add. + /// The result of the addition. + /// If and are not the same size. + /// If or is . + public static Vector operator +(DenseVector leftSide, DenseVector rightSide) + { + if (rightSide == null) + { + throw new ArgumentNullException("rightSide"); + } + + if (leftSide == null) + { + throw new ArgumentNullException("leftSide"); + } + + if (leftSide.Count != rightSide.Count) + { + throw new ArgumentException(Resources.ArgumentVectorsSameLength, "rightSide"); + } + + return leftSide.Add(rightSide); + } + + /// + /// Subtracts a scalar from each element of the vector. + /// + /// The scalar to subtract. + /// A new vector containing the subtraction of this vector and the scalar. + public override Vector Subtract(float scalar) + { + if (scalar == 0.0) + { + return Clone(); + } + + var copy = (DenseVector)Clone(); + CommonParallel.For( + 0, + Data.Length, + index => copy.Data[index] -= scalar); + return copy; + } + + /// + /// Subtracts a scalar from each element of the vector and stores the result in the result vector. + /// + /// The scalar to subtract. + /// The vector to store the result of the subtraction. + /// If the result vector is . + /// If this vector and are not the same size. + public override void Subtract(float scalar, Vector result) + { + if (result == null) + { + throw new ArgumentNullException("result"); + } + + if (Count != result.Count) + { + throw new ArgumentException(Resources.ArgumentVectorsSameLength, "result"); + } + + var dense = result as DenseVector; + if (dense == null) + { + base.Subtract(scalar, result); + } + else + { + CommonParallel.For( + 0, + Data.Length, + index => dense.Data[index] = Data[index] - scalar); + } + } + + /// + /// Subtracts another vector from this vector. + /// + /// The vector to subtract from this one. + /// A new vector containing the subtraction of the the two vectors. + /// If the other vector is . + /// If this vector and are not the same size. + public override Vector Subtract(Vector other) + { + if (other == null) + { + throw new ArgumentNullException("other"); + } + + if (Count != other.Count) + { + throw new ArgumentException(Resources.ArgumentVectorsSameLength, "other"); + } + + var denseVector = other as DenseVector; + + if (denseVector == null) + { + return base.Subtract(other); + } + + var copy = (DenseVector)Clone(); + Control.LinearAlgebraProvider.AddVectorToScaledVector(copy.Data, -1.0f, denseVector.Data); + return copy; + } + + /// + /// Subtracts another vector to this vector and stores the result into the result vector. + /// + /// The vector to subtract from this one. + /// The vector to store the result of the subtraction. + /// If the other vector is . + /// If the result vector is . + /// If this vector and are not the same size. + /// If this vector and are not the same size. + public override void Subtract(Vector other, Vector result) + { + if (result == null) + { + throw new ArgumentNullException("result"); + } + + if (Count != other.Count) + { + throw new ArgumentException(Resources.ArgumentVectorsSameLength, "other"); + } + + if (Count != result.Count) + { + throw new ArgumentException(Resources.ArgumentVectorsSameLength, "result"); + } + + if (ReferenceEquals(this, result) || ReferenceEquals(other, result)) + { + var tmp = Subtract(other); + tmp.CopyTo(result); + } + else + { + var rdense = result as DenseVector; + var odense = other as DenseVector; + if (rdense != null && odense != null) + { + CopyTo(result); + Control.LinearAlgebraProvider.AddVectorToScaledVector(rdense.Data, -1.0f, odense.Data); + } + else + { + CommonParallel.For( + 0, + Data.Length, + index => result[index] = Data[index] - other[index]); + } + } + } + + /// + /// Returns a Vector containing the negated values of . + /// + /// The vector to get the values from. + /// A vector containing the negated values as . + /// If is . + public static Vector operator -(DenseVector rightSide) + { + if (rightSide == null) + { + throw new ArgumentNullException("rightSide"); + } + + return rightSide.Negate(); + } + + /// + /// Subtracts two Vectors and returns the results. + /// + /// The vector to subtract from. + /// The vector to subtract. + /// The result of the subtraction. + /// If and are not the same size. + /// If or is . + public static Vector operator -(DenseVector leftSide, DenseVector rightSide) + { + if (rightSide == null) + { + throw new ArgumentNullException("rightSide"); + } + + if (leftSide == null) + { + throw new ArgumentNullException("leftSide"); + } + + if (leftSide.Count != rightSide.Count) + { + throw new ArgumentException(Resources.ArgumentVectorsSameLength, "rightSide"); + } + + return leftSide.Subtract(rightSide); + } + + /// + /// Returns a negated vector. + /// + /// The negated vector. + /// Added as an alternative to the unary negation operator. + public override Vector Negate() + { + var result = new DenseVector(Count); + CommonParallel.For( + 0, + Data.Length, + index => result[index] = -Data[index]); + + return result; + } + + /// + /// Multiplies a scalar to each element of the vector. + /// + /// The scalar to multiply. + /// A new vector that is the multiplication of the vector and the scalar. + public override Vector Multiply(float scalar) + { + if (scalar == 1.0) + { + return Clone(); + } + + var copy = (DenseVector)Clone(); + Control.LinearAlgebraProvider.ScaleArray(scalar, copy.Data); + return copy; + } + + /// + /// Computes the dot product between this vector and another vector. + /// + /// The other vector to add. + /// The result of the addition. + /// If is not of the same size. + /// If is . + public override float DotProduct(Vector other) + { + if (other == null) + { + throw new ArgumentNullException("other"); + } + + if (Count != other.Count) + { + throw new ArgumentException(Resources.ArgumentVectorsSameLength, "other"); + } + + var denseVector = other as DenseVector; + + if (denseVector == null) + { + return base.DotProduct(other); + } + + return Control.LinearAlgebraProvider.DotProduct(Data, denseVector.Data); + } + + /// + /// Multiplies a vector with a scalar. + /// + /// The vector to scale. + /// The scalar value. + /// The result of the multiplication. + /// If is . + public static DenseVector operator *(DenseVector leftSide, float rightSide) + { + if (leftSide == null) + { + throw new ArgumentNullException("leftSide"); + } + + return (DenseVector)leftSide.Multiply(rightSide); + } + + /// + /// Multiplies a vector with a scalar. + /// + /// The scalar value. + /// The vector to scale. + /// The result of the multiplication. + /// If is . + public static DenseVector operator *(float leftSide, DenseVector rightSide) + { + if (rightSide == null) + { + throw new ArgumentNullException("rightSide"); + } + + return (DenseVector)rightSide.Multiply(leftSide); + } + + /// + /// Computes the dot product between two Vectors. + /// + /// The left row vector. + /// The right column vector. + /// The dot product between the two vectors. + /// If and are not the same size. + /// If or is . + public static float operator *(DenseVector leftSide, DenseVector rightSide) + { + if (rightSide == null) + { + throw new ArgumentNullException("rightSide"); + } + + if (leftSide == null) + { + throw new ArgumentNullException("leftSide"); + } + + if (leftSide.Count != rightSide.Count) + { + throw new ArgumentException(Resources.ArgumentVectorsSameLength, "rightSide"); + } + + return Control.LinearAlgebraProvider.DotProduct(leftSide.Data, rightSide.Data); + } + + /// + /// Divides a vector with a scalar. + /// + /// The vector to divide. + /// The scalar value. + /// The result of the division. + /// If is . + public static DenseVector operator /(DenseVector leftSide, float rightSide) + { + if (leftSide == null) + { + throw new ArgumentNullException("leftSide"); + } + + return (DenseVector)leftSide.Multiply(1f / rightSide); + } + + /// + /// Returns the index of the absolute minimum element. + /// + /// The index of absolute minimum element. + public override int AbsoluteMinimumIndex() + { + var index = 0; + var min = Math.Abs(Data[index]); + for (var i = 1; i < Count; i++) + { + var test = Math.Abs(Data[i]); + if (test < min) + { + index = i; + min = test; + } + } + + return index; + } + + /// + /// Returns the value of the absolute minimum element. + /// + /// The value of the absolute minimum element. + public override double AbsoluteMinimum() + { + return Math.Abs(Data[AbsoluteMinimumIndex()]); + } + + /// + /// Returns the value of the absolute maximum element. + /// + /// The value of the absolute maximum element. + public override double AbsoluteMaximum() + { + return Math.Abs(Data[AbsoluteMaximumIndex()]); + } + + /// + /// Returns the index of the absolute maximum element. + /// + /// The index of absolute maximum element. + public override int AbsoluteMaximumIndex() + { + var index = 0; + var max = Math.Abs(Data[index]); + for (var i = 1; i < Count; i++) + { + var test = Math.Abs(Data[i]); + if (test > max) + { + index = i; + max = test; + } + } + + return index; + } + + /// + /// Creates a vector containing specified elements. + /// + /// The first element to begin copying from. + /// The number of elements to copy. + /// A vector containing a copy of the specified elements. + /// If is not positive or + /// greater than or equal to the size of the vector. + /// If + is greater than or equal to the size of the vector. + /// + /// If is not positive. + public override Vector SubVector(int index, int length) + { + if (index < 0 || index >= Count) + { + throw new ArgumentOutOfRangeException("index"); + } + + if (length <= 0) + { + throw new ArgumentOutOfRangeException("length"); + } + + if (index + length > Count) + { + throw new ArgumentOutOfRangeException("length"); + } + + var result = new DenseVector(length); + + CommonParallel.For( + index, + index + length, + i => result.Data[i - index] = Data[i]); + return result; + } + + /// + /// Set the values of this vector to the given values. + /// + /// The array containing the values to use. + /// If is . + /// If is not the same size as this vector. + public override void SetValues(float[] values) + { + if (values == null) + { + throw new ArgumentNullException("values"); + } + + if (values.Length != Count) + { + throw new ArgumentException(Resources.ArgumentVectorsSameLength, "values"); + } + + CommonParallel.For( + 0, + values.Length, + i => Data[i] = values[i]); + } + + /// + /// Returns the index of the absolute maximum element. + /// + /// The index of absolute maximum element. + public override int MaximumIndex() + { + var index = 0; + var max = Data[0]; + for (var i = 1; i < Count; i++) + { + if (max < Data[i]) + { + index = i; + max = Data[i]; + } + } + + return index; + } + + /// + /// Returns the index of the minimum element. + /// + /// The index of minimum element. + public override int MinimumIndex() + { + var index = 0; + var min = Data[0]; + for (var i = 1; i < Count; i++) + { + if (min > Data[i]) + { + index = i; + min = Data[i]; + } + } + + return index; + } + + /// + /// Computes the sum of the vector's elements. + /// + /// The sum of the vector's elements. + public override float Sum() + { + float result = 0; + for (var i = 0; i < Count; i++) + { + result += Data[i]; + } + + return result; + } + + /// + /// Computes the sum of the absolute value of the vector's elements. + /// + /// The sum of the absolute value of the vector's elements. + public override double SumMagnitudes() + { + float result = 0; + for (var i = 0; i < Count; i++) + { + result += Math.Abs(Data[i]); + } + + return result; + } + + /// + /// Pointwise multiplies this vector with another vector. + /// + /// The vector to pointwise multiply with this one. + /// A new vector which is the pointwise multiplication of the two vectors. + /// If the other vector is . + /// If this vector and are not the same size. + public override Vector PointwiseMultiply(Vector other) + { + if (other == null) + { + throw new ArgumentNullException("other"); + } + + if (Count != other.Count) + { + throw new ArgumentException(Resources.ArgumentVectorsSameLength, "other"); + } + + var denseVector = other as DenseVector; + + if (denseVector == null) + { + return base.PointwiseMultiply(other); + } + + var copy = (DenseVector)Clone(); + CommonParallel.For( + 0, + Count, + index => copy[index] *= other[index]); + return copy; + } + + /// + /// Pointwise multiplies this vector with another vector and stores the result into the result vector. + /// + /// The vector to pointwise multiply with this one. + /// The vector to store the result of the pointwise multiplication. + /// If the other vector is . + /// If the result vector is . + /// If this vector and are not the same size. + /// If this vector and are not the same size. + public override void PointwiseMultiply(Vector other, Vector result) + { + if (result == null) + { + throw new ArgumentNullException("result"); + } + + if (other == null) + { + throw new ArgumentNullException("other"); + } + + if (Count != other.Count) + { + throw new ArgumentException(Resources.ArgumentVectorsSameLength, "other"); + } + + if (Count != result.Count) + { + throw new ArgumentException(Resources.ArgumentVectorsSameLength, "result"); + } + + if (ReferenceEquals(this, result) || ReferenceEquals(other, result)) + { + var tmp = PointwiseMultiply(other); + tmp.CopyTo(result); + } + else + { + var dense = result as DenseVector; + if (dense == null) + { + base.PointwiseMultiply(other, result); + } + else + { + CommonParallel.For( + 0, + Data.Length, + index => dense.Data[index] = Data[index] * other[index]); + } + } + } + + /// + /// Pointwise divide this vector with another vector. + /// + /// The vector to pointwise divide this one by. + /// A new vector which is the pointwise division of the two vectors. + /// If the other vector is . + /// If this vector and are not the same size. + public override Vector PointwiseDivide(Vector other) + { + if (other == null) + { + throw new ArgumentNullException("other"); + } + + if (Count != other.Count) + { + throw new ArgumentException(Resources.ArgumentVectorsSameLength, "other"); + } + + var denseVector = other as DenseVector; + + if (denseVector == null) + { + return base.PointwiseMultiply(other); + } + + var copy = (DenseVector)Clone(); + CommonParallel.For( + 0, + Count, + index => copy[index] /= other[index]); + return copy; + } + + /// + /// Pointwise divide this vector with another vector and stores the result into the result vector. + /// + /// The vector to pointwise divide this one by. + /// The vector to store the result of the pointwise division. + /// If the other vector is . + /// If the result vector is . + /// If this vector and are not the same size. + /// If this vector and are not the same size. + public override void PointwiseDivide(Vector other, Vector result) + { + if (result == null) + { + throw new ArgumentNullException("result"); + } + + if (other == null) + { + throw new ArgumentNullException("other"); + } + + if (Count != other.Count) + { + throw new ArgumentException(Resources.ArgumentVectorsSameLength, "other"); + } + + if (Count != result.Count) + { + throw new ArgumentException(Resources.ArgumentVectorsSameLength, "result"); + } + + if (ReferenceEquals(this, result) || ReferenceEquals(other, result)) + { + var tmp = PointwiseDivide(other); + tmp.CopyTo(result); + } + else + { + var dense = result as DenseVector; + if (dense == null) + { + base.PointwiseDivide(other, result); + } + else + { + CommonParallel.For( + 0, + Data.Length, + index => dense.Data[index] = Data[index] / other[index]); + } + } + } + + /// + /// Outer product of two vectors + /// + /// First vector + /// Second vector + /// Matrix M[i,j] = u[i]*v[j] + /// If the u vector is . + /// If the v vector is . + public static DenseMatrix OuterProduct(DenseVector u, DenseVector v) + { + if (u == null) + { + throw new ArgumentNullException("u"); + } + + if (v == null) + { + throw new ArgumentNullException("v"); + } + + var matrix = new DenseMatrix(u.Count, v.Count); + CommonParallel.For( + 0, + u.Count, + i => + { + for (var j = 0; j < v.Count; j++) + { + matrix.At(i, j, u.Data[i] * v.Data[j]); + } + }); + return matrix; + } + + /// + /// Generates a vector with random elements + /// + /// Number of elements in the vector. + /// Continuous Random Distribution or Source + /// + /// A vector with n-random elements distributed according + /// to the specified random distribution. + /// + /// If the n vector is non positive. + public override Vector Random(int length, IContinuousDistribution randomDistribution) + { + if (length < 1) + { + throw new ArgumentException(Resources.ArgumentMustBePositive, "length"); + } + + var v = (DenseVector)CreateVector(length); + for (var index = 0; index < v.Data.Length; index++) + { + v.Data[index] = (float)randomDistribution.Sample(); + } + + return v; + } + + /// + /// Generates a vector with random elements + /// + /// Number of elements in the vector. + /// Continuous Random Distribution or Source + /// + /// A vector with n-random elements distributed according + /// to the specified random distribution. + /// + /// If the n vector is non positive. + public override Vector Random(int length, IDiscreteDistribution randomDistribution) + { + if (length < 1) + { + throw new ArgumentException(Resources.ArgumentMustBePositive, "length"); + } + + var v = (DenseVector)CreateVector(length); + for (var index = 0; index < v.Data.Length; index++) + { + v.Data[index] = randomDistribution.Sample(); + } + + return v; + } + + /// + /// Tensor Product (Dyadic) of this and another vector. + /// + /// The vector to operate on. + /// + /// Matrix M[i,j] = this[i] * v[j]. + /// + /// + public Matrix TensorMultiply(DenseVector v) + { + return OuterProduct(this, v); + } + + #region Vector Norms + + /// + /// Computes the p-Norm. + /// + /// The p value. + /// Scalar ret = (sum(abs(this[i])^p))^(1/p) + public override double Norm(double p) + { + if (p < 0.0) + { + throw new ArgumentOutOfRangeException("p"); + } + + if (1.0 == p) + { + return CommonParallel.Aggregate( + 0, + Count, + index => Math.Abs(Data[index])); + } + + if (2.0 == p) + { + return Data.Aggregate(0f, SpecialFunctions.Hypotenuse); + } + + if (Double.IsPositiveInfinity(p)) + { + return CommonParallel.Select( + 0, + Count, + (index, localData) => Math.Max(localData, Math.Abs(Data[index])), + Math.Max); + } + + var sum = CommonParallel.Aggregate( + 0, + Count, + index => Math.Pow(Math.Abs(Data[index]), p)); + + return Math.Pow(sum, 1.0 / p); + } + + /// + /// Normalizes this vector to a unit vector with respect to the p-norm. + /// + /// + /// The p value. + /// + /// + /// This vector normalized to a unit vector with respect to the p-norm. + /// + public override Vector Normalize(double p) + { + if (p < 0.0) + { + throw new ArgumentOutOfRangeException("p"); + } + + var norm = Norm(p); + var clone = Clone(); + if (norm == 0.0) + { + return clone; + } + + clone.Multiply(1.0f / (float)norm, clone); + + return clone; + } + #endregion + + #region Parse Functions + + /// + /// Creates a float dense vector based on a string. The string can be in the following formats (without the + /// quotes): 'n', 'n,n,..', '(n,n,..)', '[n,n,...]', where n is a float. + /// + /// + /// A float dense vector containing the values specified by the given string. + /// + /// + /// The string to parse. + /// + public static DenseVector Parse(string value) + { + return Parse(value, null); + } + + /// + /// Creates a float dense vector based on a string. The string can be in the following formats (without the + /// quotes): 'n', 'n,n,..', '(n,n,..)', '[n,n,...]', where n is a float. + /// + /// + /// A float dense vector containing the values specified by the given string. + /// + /// + /// the string to parse. + /// + /// + /// An that supplies culture-specific formatting information. + /// + public static DenseVector Parse(string value, IFormatProvider formatProvider) + { + if (value == null) + { + throw new ArgumentNullException(value); + } + + value = value.Trim(); + if (value.Length == 0) + { + throw new FormatException(); + } + + // strip out parens + if (value.StartsWith("(", StringComparison.Ordinal)) + { + if (!value.EndsWith(")", StringComparison.Ordinal)) + { + throw new FormatException(); + } + + value = value.Substring(1, value.Length - 2).Trim(); + } + + if (value.StartsWith("[", StringComparison.Ordinal)) + { + if (!value.EndsWith("]", StringComparison.Ordinal)) + { + throw new FormatException(); + } + + value = value.Substring(1, value.Length - 2).Trim(); + } + + // keywords + var textInfo = formatProvider.GetTextInfo(); + var keywords = new[] { textInfo.ListSeparator }; + + // lexing + var tokens = new LinkedList(); + GlobalizationHelper.Tokenize(tokens.AddFirst(value), keywords, 0); + var token = tokens.First; + + if (token == null || tokens.Count.IsEven()) + { + throw new FormatException(); + } + + // parsing + var data = new float[(tokens.Count + 1) >> 1]; + for (var i = 0; i < data.Length; i++) + { + if (token == null || token.Value == textInfo.ListSeparator) + { + throw new FormatException(); + } + + data[i] = float.Parse(token.Value, NumberStyles.Any, formatProvider); + + token = token.Next; + if (token != null) + { + token = token.Next; + } + } + + return new DenseVector(data); + } + + /// + /// Converts the string representation of a real dense vector to float-precision dense vector equivalent. + /// A return value indicates whether the conversion succeeded or failed. + /// + /// + /// A string containing a real vector to convert. + /// + /// + /// The parsed value. + /// + /// + /// If the conversion succeeds, the result will contain a complex number equivalent to value. + /// Otherwise the result will be null. + /// + public static bool TryParse(string value, out DenseVector result) + { + return TryParse(value, null, out result); + } + + /// + /// Converts the string representation of a real dense vector to float-precision dense vector equivalent. + /// A return value indicates whether the conversion succeeded or failed. + /// + /// + /// A string containing a real vector to convert. + /// + /// + /// An that supplies culture-specific formatting information about value. + /// + /// + /// The parsed value. + /// + /// + /// If the conversion succeeds, the result will contain a complex number equivalent to value. + /// Otherwise the result will be null. + /// + public static bool TryParse(string value, IFormatProvider formatProvider, out DenseVector result) + { + bool ret; + try + { + result = Parse(value, formatProvider); + ret = true; + } + catch (ArgumentNullException) + { + result = null; + ret = false; + } + catch (FormatException) + { + result = null; + ret = false; + } + + return ret; + } + + #endregion + + /// + /// Resets all values to zero. + /// + public override void Clear() + { + Array.Clear(Data, 0, Data.Length); + } + + #region Simple arithmetic of type T + /// + /// Add two values T+T + /// + /// Left operand value + /// Right operand value + /// Result of addition + protected sealed override float AddT(float val1, float val2) + { + return val1 + val2; + } + + /// + /// Subtract two values T-T + /// + /// Left operand value + /// Right operand value + /// Result of subtract + protected sealed override float SubtractT(float val1, float val2) + { + return val1 - val2; + } + + /// + /// Multiply two values T*T + /// + /// Left operand value + /// Right operand value + /// Result of multiplication + protected sealed override float MultiplyT(float val1, float val2) + { + return val1 * val2; + } + + /// + /// Divide two values T/T + /// + /// Left operand value + /// Right operand value + /// Result of divide + protected sealed override float DivideT(float val1, float val2) + { + return val1 / val2; + } + + /// + /// Take absolute value + /// + /// Source alue + /// True if one; otherwise false + protected sealed override double AbsoluteT(float val1) + { + return Math.Abs(val1); + } + #endregion + } +} diff --git a/src/Numerics/LinearAlgebra/Single/DiagonalMatrix.cs b/src/Numerics/LinearAlgebra/Single/DiagonalMatrix.cs new file mode 100644 index 00000000..d4b1decd --- /dev/null +++ b/src/Numerics/LinearAlgebra/Single/DiagonalMatrix.cs @@ -0,0 +1,1723 @@ +// +// 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. +// + +namespace MathNet.Numerics.LinearAlgebra.Single +{ + using System; + using System.Linq; + using System.Text; + using Distributions; + using Generic; + using Properties; + using Threading; + + /// + /// A matrix type for diagonal matrices. + /// + /// + /// Diagonal matrices can be non-square matrices but the diagonal always starts + /// at element 0,0. A diagonal matrix will throw an exception if non diagonal + /// entries are set. The exception to this is when the off diagonal elements are + /// 0.0 or NaN; these settings will cause no change to the diagonal matrix. + /// + public class DiagonalMatrix : Matrix + { + /// + /// Initializes a new instance of the class. This matrix is square with a given size. + /// + /// the size of the square matrix. + /// + /// If is less than one. + /// + public DiagonalMatrix(int order) : base(order) + { + Data = new float[order * order]; + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// The number of rows. + /// + /// + /// The number of columns. + /// + public DiagonalMatrix(int rows, int columns) : base(rows, columns) + { + Data = new float[Math.Min(rows, columns)]; + } + + /// + /// Initializes a new instance of the class with all entries set to a particular value. + /// + /// + /// The number of rows. + /// + /// + /// The number of columns. + /// + /// The value which we assign to each element of the matrix. + public DiagonalMatrix(int rows, int columns, float value) : base(rows, columns) + { + Data = new float[Math.Min(rows, columns)]; + for (var i = 0; i < Data.Length; i++) + { + Data[i] = value; + } + } + + /// + /// Initializes a new instance of the class from a one dimensional array with diagonal elements. This constructor + /// will reference the one dimensional array and not copy it. + /// + /// The number of rows. + /// The number of columns. + /// The one dimensional array which contain diagonal elements. + public DiagonalMatrix(int rows, int columns, float[] diagonalArray) : base(rows, columns) + { + Data = diagonalArray; + } + + /// + /// Initializes a new instance of the class from a 2D array. + /// + /// The 2D array to create this matrix from. + /// When contains an off-diagonal element. + /// Depending on the implementation, an + /// may be thrown if one of the indices is outside the dimensions of the matrix. + public DiagonalMatrix(float[,] array) : this(array.GetLength(0), array.GetLength(1)) + { + var rows = array.GetLength(0); + var columns = array.GetLength(1); + + for (var i = 0; i < rows; i++) + { + for (var j = 0; j < columns; j++) + { + if (i == j) + { + Data[i] = array[i, j]; + } + else if (array[i, j] != 0.0 && !Double.IsNaN(array[i, j])) + { + throw new IndexOutOfRangeException("Cannot set an off-diagonal element in a diagonal matrix."); + } + } + } + } + + /// + /// Gets the matrix's data. + /// + /// The matrix's data. + internal float[] Data + { + get; + private set; + } + + /// + /// Retrieves the requested element without range checking. + /// + /// + /// The row of the element. + /// + /// + /// The column of the element. + /// + /// + /// The requested element. + /// + /// Depending on the implementation, an + /// may be thrown if one of the indices is outside the dimensions of the matrix. + public override float At(int row, int column) + { + return row == column ? Data[row] : 0.0f; + } + + /// + /// Sets the value of the given element. + /// + /// + /// The row of the element. + /// + /// + /// The column of the element. + /// + /// + /// The value to set the element to. + /// + /// When trying to set an off diagonal element. + /// Depending on the implementation, an + /// may be thrown if one of the indices is outside the dimensions of the matrix. + public override void At(int row, int column, float value) + { + if (row == column) + { + Data[row] = value; + } + else if (value != 0.0 && !Double.IsNaN(value)) + { + throw new IndexOutOfRangeException("Cannot set an off-diagonal element in a diagonal matrix."); + } + } + + /// + /// Creates a DiagonalMatrix for the given number of rows and columns. + /// + /// + /// The number of rows. + /// + /// + /// The number of columns. + /// + /// + /// A DiagonalMatrix with the given dimensions. + /// + public override Matrix CreateMatrix(int numberOfRows, int numberOfColumns) + { + return new DiagonalMatrix(numberOfRows, numberOfColumns); + } + + /// + /// Creates a with a the given dimension. + /// + /// The size of the vector. + /// + /// A with the given dimension. + /// + public override Vector CreateVector(int size) + { + return new SparseVector(size); + } + + /// + /// Sets all values to zero. + /// + public override void Clear() + { + Array.Clear(Data, 0, Data.Length); + } + + /// + /// Indicates whether the current object is equal to another object of the same type. + /// + /// + /// An object to compare with this object. + /// + /// + /// true if the current object is equal to the parameter; otherwise, false. + /// + public override bool Equals(object obj) + { + var diagonalMatrix = obj as DiagonalMatrix; + + if (diagonalMatrix == null) + { + return base.Equals(obj); + } + + // Accept if the argument is the same object as this + if (ReferenceEquals(this, diagonalMatrix)) + { + return true; + } + + if (diagonalMatrix.Data.Length != Data.Length) + { + return false; + } + + // If all else fails, perform element wise comparison. + return !Data.Where((t, i) => t != diagonalMatrix.Data[i]).Any(); + } + + /// + /// Returns a hash code for this instance. + /// + /// + /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. + /// + public override int GetHashCode() + { + var hashNum = Math.Min(Data.Length, 25); + long hash = 0; + for (var i = 0; i < hashNum; i++) + { +#if SILVERLIGHT + hash ^= Precision.DoubleToInt64Bits(Data[i]); +#else + hash ^= BitConverter.DoubleToInt64Bits(Data[i]); +#endif + } + + return BitConverter.ToInt32(BitConverter.GetBytes(hash), 4); + } + + #region Elementary operations + /// + /// Adds another matrix to this matrix. The result will be written into this matrix. + /// + /// The matrix to add to this matrix. + /// If the other matrix is . + /// If the two matrices don't have the same dimensions. + /// If is not . + public override void Add(Matrix other) + { + if (other == null) + { + throw new ArgumentNullException("other"); + } + + var m = other as DiagonalMatrix; + if (m == null) + { + throw new ArgumentException(Resources.ArgumentTypeMismatch); + } + + Add(m); + } + + /// + /// Adds another to this matrix. The result will be written into this matrix. + /// + /// The to add to this matrix. + /// If the other matrix is . + /// If the two matrices don't have the same dimensions. + public void Add(DiagonalMatrix 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); + } + + /// + /// Subtracts another matrix from this matrix. The result will be written into this matrix. + /// + /// The matrix to subtract. + /// If the other matrix is . + /// If the two matrices don't have the same dimensions. + /// If is not . + public override void Subtract(Matrix other) + { + if (other == null) + { + throw new ArgumentNullException("other"); + } + + var m = other as DiagonalMatrix; + if (m == null) + { + throw new ArgumentException(Resources.ArgumentTypeMismatch); + } + + Subtract(m); + } + + /// + /// Subtracts another from this matrix. The result will be written into this matrix. + /// + /// The to subtract. + /// If the other matrix is . + /// If the two matrices don't have the same dimensions. + public void Subtract(DiagonalMatrix 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); + } + + /// + /// Copies the values of the given array to the diagonal. + /// + /// The array to copy the values from. The length of the vector should be + /// Min(Rows, Columns). + /// If is . + /// If the length of does not + /// equal Min(Rows, Columns). + /// For non-square matrices, the elements of are copied to + /// this[i,i]. + public override void SetDiagonal(float[] source) + { + if (source == null) + { + throw new ArgumentNullException("source"); + } + + if (source.Length != Data.Length) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "source"); + } + + Buffer.BlockCopy(source, 0, Data, 0, source.Length * Constants.SizeOfFloat); + } + + /// + /// Copies the values of the given to the diagonal. + /// + /// The vector to copy the values from. The length of the vector should be + /// Min(Rows, Columns). + /// If is . + /// If the length of does not + /// equal Min(Rows, Columns). + /// For non-square matrices, the elements of are copied to + /// this[i,i]. + public override void SetDiagonal(Vector source) + { + var denseSource = source as DenseVector; + if (denseSource == null) + { + base.SetDiagonal(source); + return; + } + + if (Data.Length != denseSource.Data.Length) + { + throw new ArgumentException(Resources.ArgumentVectorsSameLength, "source"); + } + + Buffer.BlockCopy(denseSource.Data, 0, Data, 0, denseSource.Data.Length * Constants.SizeOfFloat); + } + + /// + /// Multiplies each element of this matrix with a scalar. + /// + /// The scalar to multiply with. + public override void Multiply(float scalar) + { + if (scalar == 0.0) + { + Clear(); + return; + } + + if (scalar == 1.0) + { + return; + } + + Control.LinearAlgebraProvider.ScaleArray(scalar, Data); + } + + /// + /// Multiplies this diagonal matrix with another diagonal matrix and places the results into the result diagonal matrix. + /// + /// The matrix to multiply with. + /// The result of the multiplication. + /// If the other matrix is . + /// If the result matrix is . + /// If this.Columns != other.Rows. + /// If the result matrix's dimensions are not the this.Rows x other.Columns. + public override void Multiply(Matrix other, Matrix 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 DiagonalMatrix; + var r = result as DiagonalMatrix; + + if (m == null || r == null) + { + base.Multiply(other, result); + } + else + { + var thisDataCopy = new float[r.Data.Length]; + var otherDataCopy = new float[r.Data.Length]; + Buffer.BlockCopy(Data, 0, thisDataCopy, 0, (r.Data.Length > Data.Length) ? Data.Length * Constants.SizeOfFloat : r.Data.Length * Constants.SizeOfFloat); + Buffer.BlockCopy(m.Data, 0, otherDataCopy, 0, (r.Data.Length > m.Data.Length) ? m.Data.Length * Constants.SizeOfFloat : r.Data.Length * Constants.SizeOfFloat); + + Control.LinearAlgebraProvider.PointWiseMultiplyArrays(thisDataCopy, otherDataCopy, r.Data); + } + } + + /// + /// Multiplies this matrix with another matrix and returns the result. + /// + /// The matrix to multiply with. + /// If this.Columns != other.Rows. + /// If the other matrix is . + /// The result of multiplication. + public override Matrix Multiply(Matrix other) + { + if (other == null) + { + throw new ArgumentNullException("other"); + } + + if (ColumnCount != other.RowCount) + { + throw new ArgumentException(Resources.ArgumentMatrixDimensions); + } + + var m = other as DiagonalMatrix; + if (m == null) + { + return base.Multiply(other); + } + + var result = (DiagonalMatrix)CreateMatrix(RowCount, other.ColumnCount); + Multiply(other, result); + return result; + } + + /// + /// Multiplies this matrix with a vector and places the results into the result matrix. + /// + /// The vector to multiply with. + /// The result of the multiplication. + /// If is . + /// If is . + /// If result.Count != this.RowCount. + /// If this.ColumnCount != .Count. + public override void Multiply(Vector rightSide, Vector result) + { + if (rightSide == null) + { + throw new ArgumentNullException("rightSide"); + } + + if (ColumnCount != rightSide.Count) + { + throw new ArgumentException(Resources.ArgumentMatrixDimensions, "rightSide"); + } + + if (result == null) + { + throw new ArgumentNullException("result"); + } + + if (RowCount != result.Count) + { + throw new ArgumentException(Resources.ArgumentMatrixDimensions, "result"); + } + + if (ReferenceEquals(rightSide, result)) + { + var tmp = result.CreateVector(result.Count); + Multiply(rightSide, tmp); + tmp.CopyTo(result); + } + else + { + // Clear the result vector + result.Clear(); + + // Multiply the elements in the vector with the corresponding diagonal element in this. + for (var r = 0; r < Data.Length; r++) + { + result[r] = Data[r] * rightSide[r]; + } + } + } + + /// + /// Left multiply a matrix with a vector ( = vector * matrix ) and place the result in the result vector. + /// + /// The vector to multiply with. + /// The result of the multiplication. + /// If is . + /// If the result matrix is . + /// If result.Count != this.ColumnCount. + /// If this.RowCount != .Count. + public override void LeftMultiply(Vector leftSide, Vector result) + { + if (leftSide == null) + { + throw new ArgumentNullException("leftSide"); + } + + if (RowCount != leftSide.Count) + { + throw new ArgumentException(Resources.ArgumentMatrixDimensions, "leftSide"); + } + + if (result == null) + { + throw new ArgumentNullException("result"); + } + + if (ColumnCount != result.Count) + { + throw new ArgumentException(Resources.ArgumentMatrixDimensions, "result"); + } + + if (ReferenceEquals(leftSide, result)) + { + var tmp = result.CreateVector(result.Count); + LeftMultiply(leftSide, tmp); + tmp.CopyTo(result); + } + else + { + // Clear the result vector + result.Clear(); + + // Multiply the elements in the vector with the corresponding diagonal element in this. + for (var r = 0; r < Data.Length; r++) + { + result[r] = Data[r] * leftSide[r]; + } + } + } + + /// + /// Computes the determinant of this matrix. + /// + /// The determinant of this matrix. + public override float Determinant() + { + if (RowCount != ColumnCount) + { + throw new ArgumentException(Resources.ArgumentMatrixSquare); + } + + return Data.Aggregate(1.0f, (current, t) => current * t); + } + + /// + /// Returns the elements of the diagonal in a . + /// + /// The elements of the diagonal. + /// For non-square matrices, the method returns Min(Rows, Columns) elements where + /// i == j (i is the row index, and j is the column index). + public override Vector Diagonal() + { + // TODO: Should we return reference to array? In current implementation we return copy of array, so changes in DenseVector will + // not influence onto diagonal elements + return new DenseVector((float[])Data.Clone()); + } + + /// + /// Multiplies this diagonal matrix with transpose of another diagonal matrix and places the results into the result diagonal matrix. + /// + /// The matrix to multiply with. + /// The result of the multiplication. + /// If the other matrix is . + /// If the result matrix is . + /// If this.Columns != other.Rows. + /// If the result matrix's dimensions are not the this.Rows x other.Columns. + public override void TransposeAndMultiply(Matrix other, Matrix result) + { + var otherDiagonal = other as DiagonalMatrix; + var resultDiagonal = result as DiagonalMatrix; + + if (otherDiagonal == null || resultDiagonal == null) + { + base.TransposeAndMultiply(other, result); + return; + } + + Multiply(otherDiagonal.Transpose(), result); + } + + /// + /// Multiplies this matrix with transpose of another matrix and returns the result. + /// + /// The matrix to multiply with. + /// If this.Columns != other.Rows. + /// If the other matrix is . + /// The result of multiplication. + public override Matrix TransposeAndMultiply(Matrix other) + { + var otherDiagonal = other as DiagonalMatrix; + if (otherDiagonal == null) + { + return base.TransposeAndMultiply(other); + } + + if (ColumnCount != otherDiagonal.ColumnCount) + { + throw new ArgumentException(Resources.ArgumentMatrixDimensions); + } + + var result = (DiagonalMatrix)CreateMatrix(RowCount, other.RowCount); + TransposeAndMultiply(other, result); + return result; + } + + /// + /// Multiplies two diagonal matrices. + /// + /// The left matrix to multiply. + /// The right matrix to multiply. + /// The result of multiplication. + /// If or is . + /// If the dimensions of or don't conform. + public static DiagonalMatrix operator *(DiagonalMatrix leftSide, DiagonalMatrix 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 (DiagonalMatrix)leftSide.Multiply(rightSide); + } + + #endregion + + /// + /// Copies the elements of this matrix to the given matrix. + /// + /// + /// The matrix to copy values into. + /// + /// + /// If target is . + /// + /// + /// If this and the target matrix do not have the same dimensions.. + /// + public override void CopyTo(Matrix target) + { + var diagonalTarget = target as DiagonalMatrix; + + if (diagonalTarget == null) + { + base.CopyTo(target); + return; + } + + if (ReferenceEquals(this, target)) + { + return; + } + + if (RowCount != target.RowCount || ColumnCount != target.ColumnCount) + { + throw new ArgumentException(Resources.ArgumentMatrixDimensions, "target"); + } + + Buffer.BlockCopy(Data, 0, diagonalTarget.Data, 0, Data.Length * Constants.SizeOfFloat); + } + + /// + /// Returns the transpose of this matrix. + /// + /// The transpose of this matrix. + public override Matrix Transpose() + { + var ret = new DiagonalMatrix(ColumnCount, RowCount); + Buffer.BlockCopy(Data, 0, ret.Data, 0, Data.Length * Constants.SizeOfFloat); + return ret; + } + + /// + /// Copies the requested column elements into the given vector. + /// + /// The column to copy elements from. + /// The row to start copying from. + /// The number of elements to copy. + /// The to copy the column into. + /// If the result is . + /// If is negative, + /// or greater than or equal to the number of columns. + /// If is negative, + /// or greater than or equal to the number of rows. + /// If + + /// is greater than or equal to the number of rows. + /// If is not positive. + /// If result.Count < length. + public override void Column(int columnIndex, int rowIndex, int length, Vector result) + { + if (result == null) + { + throw new ArgumentNullException("result"); + } + + if (columnIndex >= ColumnCount || columnIndex < 0) + { + throw new ArgumentOutOfRangeException("columnIndex"); + } + + if (rowIndex >= RowCount || rowIndex < 0) + { + throw new ArgumentOutOfRangeException("rowIndex"); + } + + if (rowIndex + length > RowCount) + { + throw new ArgumentOutOfRangeException("length"); + } + + if (length < 1) + { + throw new ArgumentException(Resources.ArgumentMustBePositive, "length"); + } + + if (result.Count < length) + { + throw new ArgumentException(Resources.ArgumentVectorsSameLength, "result"); + } + + // Clear the result and copy the diagonal entry. + result.Clear(); + if (columnIndex >= rowIndex && columnIndex < rowIndex + length && columnIndex < Data.Length) + { + result[columnIndex - rowIndex] = Data[columnIndex]; + } + } + + /// + /// Copies the requested row elements into a new . + /// + /// The row to copy elements from. + /// The column to start copying from. + /// The number of elements to copy. + /// The to copy the column into. + /// If the result is . + /// If is negative, + /// or greater than or equal to the number of columns. + /// If is negative, + /// or greater than or equal to the number of rows. + /// If + + /// is greater than or equal to the number of rows. + /// If is not positive. + /// If result.Count < length. + public override void Row(int rowIndex, int columnIndex, int length, Vector result) + { + if (result == null) + { + throw new ArgumentNullException("result"); + } + + if (rowIndex >= RowCount || rowIndex < 0) + { + throw new ArgumentOutOfRangeException("rowIndex"); + } + + if (columnIndex >= ColumnCount || columnIndex < 0) + { + throw new ArgumentOutOfRangeException("columnIndex"); + } + + if (columnIndex + length > ColumnCount) + { + throw new ArgumentOutOfRangeException("length"); + } + + if (length < 1) + { + throw new ArgumentException(Resources.ArgumentMustBePositive, "length"); + } + + if (result.Count < length) + { + throw new ArgumentException(Resources.ArgumentVectorsSameLength, "result"); + } + + // Clear the result and copy the diagonal entry. + result.Clear(); + if (rowIndex >= columnIndex && rowIndex < columnIndex + length && rowIndex < Data.Length) + { + result[rowIndex - columnIndex] = Data[rowIndex]; + } + } + + /// Calculates the L1 norm. + /// The L1 norm of the matrix. + public override double L1Norm() + { + return Data.Aggregate(float.NegativeInfinity, (current, t) => Math.Max(current, Math.Abs(t))); + } + + /// Calculates the L2 norm. + /// The L2 norm of the matrix. + public override double L2Norm() + { + return Data.Aggregate(float.NegativeInfinity, (current, t) => Math.Max(current, Math.Abs(t))); + } + + /// Calculates the Frobenius norm of this matrix. + /// The Frobenius norm of this matrix. + public override double FrobeniusNorm() + { + var norm = Data.Sum(t => t * t); + return Math.Sqrt(norm); + } + + /// Calculates the infinity norm of this matrix. + /// The infinity norm of this matrix. + public override double InfinityNorm() + { + return L1Norm(); + } + + /// Calculates the condition number of this matrix. + /// The condition number of the matrix. + public override double ConditionNumber() + { + var maxSv = float.NegativeInfinity; + var minSv = float.PositiveInfinity; + foreach (var t in Data) + { + maxSv = Math.Max(maxSv, Math.Abs(t)); + minSv = Math.Min(minSv, Math.Abs(t)); + } + + return maxSv / minSv; + } + + /// Computes the inverse of this matrix. + /// If is not a square matrix. + /// If is singular. + /// The inverse of this matrix. + public override Matrix Inverse() + { + if (RowCount != ColumnCount) + { + throw new ArgumentException(Resources.ArgumentMatrixSquare); + } + + var inverse = (DiagonalMatrix)Clone(); + for (var i = 0; i < Data.Length; i++) + { + if (Data[i] != 0.0) + { + inverse.Data[i] = 1.0f / Data[i]; + } + else + { + throw new ArgumentException(Resources.ArgumentMatrixNotSingular); + } + } + + return inverse; + } + + /// + /// Returns a new matrix containing the lower triangle of this matrix. + /// + /// The lower triangle of this matrix. + public override Matrix LowerTriangle() + { + return Clone(); + } + + /// + /// Puts the lower triangle of this matrix into the result matrix. + /// + /// Where to store the lower triangle. + /// If is . + /// If the result matrix's dimensions are not the same as this matrix. + public override void LowerTriangle(Matrix result) + { + if (result == null) + { + throw new ArgumentNullException("result"); + } + + if (result.RowCount != RowCount || result.ColumnCount != ColumnCount) + { + throw new ArgumentException(Resources.ArgumentMatrixDimensions, "result"); + } + + if (ReferenceEquals(this, result)) + { + return; + } + + result.Clear(); + for (var i = 0; i < Data.Length; i++) + { + result[i, i] = Data[i]; + } + } + + /// + /// Returns a new matrix containing the lower triangle of this matrix. The new matrix + /// does not contain the diagonal elements of this matrix. + /// + /// The lower triangle of this matrix. + public override Matrix StrictlyLowerTriangle() + { + return new DiagonalMatrix(RowCount, ColumnCount); + } + + /// + /// Puts the strictly lower triangle of this matrix into the result matrix. + /// + /// Where to store the lower triangle. + /// If is . + /// If the result matrix's dimensions are not the same as this matrix. + public override void StrictlyLowerTriangle(Matrix result) + { + if (result == null) + { + throw new ArgumentNullException("result"); + } + + if (result.RowCount != RowCount || result.ColumnCount != ColumnCount) + { + throw new ArgumentException(Resources.ArgumentMatrixDimensions, "result"); + } + + result.Clear(); + } + + /// + /// Returns a new matrix containing the upper triangle of this matrix. + /// + /// The upper triangle of this matrix. + public override Matrix UpperTriangle() + { + return Clone(); + } + + /// + /// Puts the upper triangle of this matrix into the result matrix. + /// + /// Where to store the lower triangle. + /// If is . + /// If the result matrix's dimensions are not the same as this matrix. + public override void UpperTriangle(Matrix result) + { + if (result == null) + { + throw new ArgumentNullException("result"); + } + + if (result.RowCount != RowCount || result.ColumnCount != ColumnCount) + { + throw new ArgumentException(Resources.ArgumentMatrixDimensions, "result"); + } + + result.Clear(); + for (var i = 0; i < Data.Length; i++) + { + result[i, i] = Data[i]; + } + } + + /// + /// Returns a new matrix containing the upper triangle of this matrix. The new matrix + /// does not contain the diagonal elements of this matrix. + /// + /// The upper triangle of this matrix. + public override Matrix StrictlyUpperTriangle() + { + return new DiagonalMatrix(RowCount, ColumnCount); + } + + /// + /// Puts the strictly upper triangle of this matrix into the result matrix. + /// + /// Where to store the lower triangle. + /// If is . + /// If the result matrix's dimensions are not the same as this matrix. + public override void StrictlyUpperTriangle(Matrix result) + { + if (result == null) + { + throw new ArgumentNullException("result"); + } + + if (result.RowCount != RowCount || result.ColumnCount != ColumnCount) + { + throw new ArgumentException(Resources.ArgumentMatrixDimensions, "result"); + } + + result.Clear(); + } + + /// + /// Creates a matrix that contains the values from the requested sub-matrix. + /// + /// The row to start copying from. + /// The number of rows to copy. Must be positive. + /// The column to start copying from. + /// The number of columns to copy. Must be positive. + /// The requested sub-matrix. + /// If: is + /// negative, or greater than or equal to the number of rows. + /// is negative, or greater than or equal to the number + /// of columns. + /// (columnIndex + columnLength) >= Columns + /// (rowIndex + rowLength) >= Rows + /// If or + /// is not positive. + public override Matrix SubMatrix(int rowIndex, int rowLength, int columnIndex, int columnLength) + { + if (rowIndex >= RowCount || rowIndex < 0) + { + throw new ArgumentOutOfRangeException("rowIndex"); + } + + if (columnIndex >= ColumnCount || columnIndex < 0) + { + throw new ArgumentOutOfRangeException("columnIndex"); + } + + if (rowLength < 1) + { + throw new ArgumentException(Resources.ArgumentMustBePositive, "rowLength"); + } + + if (columnLength < 1) + { + throw new ArgumentException(Resources.ArgumentMustBePositive, "columnLength"); + } + + var colMax = columnIndex + columnLength; + var rowMax = rowIndex + rowLength; + + if (rowMax > RowCount) + { + throw new ArgumentOutOfRangeException("rowLength"); + } + + if (colMax > ColumnCount) + { + throw new ArgumentOutOfRangeException("columnLength"); + } + + var result = new SparseMatrix(rowLength, columnLength); + + if (rowIndex > columnIndex && columnIndex + columnLength > rowIndex) + { + for (var i = 0; rowIndex - columnIndex + i < Math.Min(columnLength, rowLength); i++) + { + result[i, rowIndex - columnIndex + i] = Data[rowIndex + i]; + } + } + else if (rowIndex < columnIndex && rowIndex + rowLength > columnIndex) + { + for (var i = 0; rowIndex - columnIndex + i < Math.Min(columnLength, rowLength); i++) + { + result[columnIndex - rowIndex + i, i] = Data[columnIndex + i]; + } + } + else + { + for (var i = 0; i < Math.Min(columnLength, rowLength); i++) + { + result[i, i] = Data[rowIndex + i]; + } + } + + return result; + } + + /// + /// Returns this matrix as a multidimensional array. + /// + /// A multidimensional containing the values of this matrix. + public override float[,] ToArray() + { + var result = new float[RowCount, ColumnCount]; + for (var i = 0; i < Data.Length; i++) + { + result[i, i] = Data[i]; + } + + return result; + } + + /// + /// Creates a new and inserts the given column at the given index. + /// + /// The index of where to insert the column. + /// The column to insert. + /// A new with the inserted column. + /// If is . + /// If is < zero or > the number of columns. + /// If the size of != the number of rows. + public override Matrix InsertColumn(int columnIndex, Vector column) + { + if (column == null) + { + throw new ArgumentNullException("column"); + } + + if (columnIndex < 0 || columnIndex > ColumnCount) + { + throw new ArgumentOutOfRangeException("columnIndex"); + } + + if (column.Count != RowCount) + { + throw new ArgumentException(Resources.ArgumentMatrixSameRowDimension, "column"); + } + + var result = new SparseMatrix(RowCount, ColumnCount + 1); + + for (var i = 0; i < columnIndex; i++) + { + result.SetColumn(i, Column(i)); + } + + result.SetColumn(columnIndex, column); + + for (var i = columnIndex + 1; i < ColumnCount + 1; i++) + { + result.SetColumn(i, Column(i - 1)); + } + + return result; + } + + /// + /// Creates a new and inserts the given row at the given index. + /// + /// The index of where to insert the row. + /// The row to insert. + /// A new with the inserted column. + /// If is . + /// If is < zero or > the number of rows. + /// If the size of != the number of columns. + public override Matrix InsertRow(int rowIndex, Vector row) + { + if (row == null) + { + throw new ArgumentNullException("row"); + } + + if (rowIndex < 0 || rowIndex > RowCount) + { + throw new ArgumentOutOfRangeException("rowIndex"); + } + + if (row.Count != ColumnCount) + { + throw new ArgumentException(Resources.ArgumentMatrixSameRowDimension, "row"); + } + + var result = new SparseMatrix(RowCount + 1, ColumnCount); + + for (var i = 0; i < rowIndex; i++) + { + result.SetRow(i, Row(i)); + } + + result.SetRow(rowIndex, row); + + for (var i = rowIndex + 1; i < RowCount; i++) + { + result.SetRow(i, Row(i - 1)); + } + + return result; + } + + /// + /// Stacks this matrix on top of the given matrix and places the result into the result . + /// + /// The matrix to stack this matrix upon. + /// The combined . + /// If lower is . + /// If upper.Columns != lower.Columns. + public override Matrix Stack(Matrix lower) + { + if (lower == null) + { + throw new ArgumentNullException("lower"); + } + + if (lower.ColumnCount != ColumnCount) + { + throw new ArgumentException(Resources.ArgumentMatrixSameColumnDimension, "lower"); + } + + var result = new SparseMatrix(RowCount + lower.RowCount, ColumnCount); + Stack(lower, result); + return result; + } + + /// + /// Stacks this matrix on top of the given matrix and places the result into the result . + /// + /// The matrix to stack this matrix upon. + /// The combined . + /// If lower is . + /// If upper.Columns != lower.Columns. + public override void Stack(Matrix lower, Matrix result) + { + if (lower == null) + { + throw new ArgumentNullException("lower"); + } + + if (lower.ColumnCount != ColumnCount) + { + throw new ArgumentException(Resources.ArgumentMatrixSameColumnDimension, "lower"); + } + + if (result == null) + { + throw new ArgumentNullException("result"); + } + + if (result.RowCount != (RowCount + lower.RowCount) || result.ColumnCount != ColumnCount) + { + throw new ArgumentException(Resources.ArgumentMatrixDimensions, "result"); + } + + // Clear the result matrix + result.Clear(); + + // Copy the diagonal part into the result matrix. + for (var i = 0; i < Data.Length; i++) + { + result[i, i] = Data[i]; + } + + // Copy the lower matrix into the result matrix. + for (var i = 0; i < lower.RowCount; i++) + { + for (var j = 0; j < lower.ColumnCount; j++) + { + result[i + RowCount, j] = lower[i, j]; + } + } + } + + /// + /// Concatenates this matrix with the given matrix. + /// + /// The matrix to concatenate. + /// The combined . + public override Matrix Append(Matrix right) + { + if (right == null) + { + throw new ArgumentNullException("right"); + } + + if (right.RowCount != RowCount) + { + throw new ArgumentException(Resources.ArgumentMatrixSameRowDimension); + } + + var result = new SparseMatrix(RowCount, ColumnCount + right.ColumnCount); + Append(right, result); + return result; + } + + /// + /// Concatenates this matrix with the given matrix and places the result into the result . + /// + /// The matrix to concatenate. + /// The combined . + public override void Append(Matrix right, Matrix result) + { + if (right == null) + { + throw new ArgumentNullException("right"); + } + + if (right.RowCount != RowCount) + { + throw new ArgumentException(Resources.ArgumentMatrixSameRowDimension); + } + + if (result == null) + { + throw new ArgumentNullException("result"); + } + + if (result.ColumnCount != (ColumnCount + right.ColumnCount) || result.RowCount != RowCount) + { + throw new ArgumentException(Resources.ArgumentMatrixSameColumnDimension); + } + + // Clear the result matrix + result.Clear(); + + // Copy the diagonal part into the result matrix. + for (var i = 0; i < Data.Length; i++) + { + result[i, i] = Data[i]; + } + + // Copy the lower matrix into the result matrix. + for (var i = 0; i < right.RowCount; i++) + { + for (var j = 0; j < right.ColumnCount; j++) + { + result[i, j + RowCount] = right[i, j]; + } + } + } + + /// + /// Diagonally stacks his matrix on top of the given matrix. The new matrix is a M-by-N matrix, + /// where M = this.Rows + lower.Rows and N = this.Columns + lower.Columns. + /// The values of off the off diagonal matrices/blocks are set to zero. + /// + /// The lower, right matrix. + /// If lower is . + /// the combined matrix + public override Matrix DiagonalStack(Matrix lower) + { + if (lower == null) + { + throw new ArgumentNullException("lower"); + } + + var result = new SparseMatrix(RowCount + lower.RowCount, ColumnCount + lower.ColumnCount); + DiagonalStack(lower, result); + return result; + } + + /// + /// Diagonally stacks his matrix on top of the given matrix and places the combined matrix into the result matrix. + /// + /// The lower, right matrix. + /// The combined matrix + /// If lower is . + /// If the result matrix is . + /// If the result matrix's dimensions are not (this.Rows + lower.rows) x (this.Columns + lower.Columns). + public override void DiagonalStack(Matrix lower, Matrix result) + { + if (lower == null) + { + throw new ArgumentNullException("lower"); + } + + if (result == null) + { + throw new ArgumentNullException("result"); + } + + if (result.RowCount != RowCount + lower.RowCount || result.ColumnCount != ColumnCount + lower.ColumnCount) + { + throw new ArgumentException(Resources.ArgumentMatrixDimensions, "result"); + } + + // Clear the result matrix + result.Clear(); + + // Copy the diagonal part into the result matrix. + for (var i = 0; i < Data.Length; i++) + { + result[i, i] = Data[i]; + } + + // Copy the lower matrix into the result matrix. + CommonParallel.For(0, lower.RowCount, i => CommonParallel.For(0, lower.ColumnCount, j => result.At(i + RowCount, j + ColumnCount, lower.At(i, j)))); + } + + /// + /// Pointwise multiplies this matrix with another matrix and stores the result into the result matrix. + /// + /// The matrix to pointwise multiply with this one. + /// The matrix to store the result of the pointwise multiplication. + /// If the other matrix is . + /// If the result matrix is . + /// If this matrix and are not the same size. + /// If this matrix and are not the same size. + public override void PointwiseMultiply(Matrix other, Matrix result) + { + if (other == null) + { + throw new ArgumentNullException("other"); + } + + if (result == null) + { + throw new ArgumentNullException("result"); + } + + if (ColumnCount != other.ColumnCount || RowCount != other.RowCount) + { + throw new ArgumentException(Resources.ArgumentMatrixDimensions, "result"); + } + + if (ColumnCount != result.ColumnCount || RowCount != result.RowCount) + { + throw new ArgumentException(Resources.ArgumentMatrixDimensions, "result"); + } + + var m = other as DiagonalMatrix; + var r = result as DiagonalMatrix; + + if (m == null || r == null) + { + base.PointwiseMultiply(other, result); + } + else + { + Control.LinearAlgebraProvider.PointWiseMultiplyArrays(Data, m.Data, r.Data); + } + } + + /// + /// Permute the columns of a matrix according to a permutation. + /// + /// The column permutation to apply to this matrix. + /// Always thrown + /// Permutation in diagonal matrix are senseless, because of matrix nature + public override void PermuteColumns(Permutation p) + { + throw new InvalidOperationException("Permutations in diagonal matrix are not allowed"); + } + + /// + /// Permute the rows of a matrix according to a permutation. + /// + /// The row permutation to apply to this matrix. + /// Always thrown + /// Permutation in diagonal matrix are senseless, because of matrix nature + public override void PermuteRows(Permutation p) + { + throw new InvalidOperationException("Permutations in diagonal matrix are not allowed"); + } + #region Static constructors for special matrices. + + /// + /// Initializes a square with all zero's except for ones on the diagonal. + /// + /// the size of the square matrix. + /// A diagonal identity matrix. + /// + /// If is less than one. + /// + public static DiagonalMatrix Identity(int order) + { + var m = new DiagonalMatrix(order); + for (var i = 0; i < order; i++) + { + m.Data[i] = 1.0f; + } + + return m; + } + + #endregion + + /// + /// Negates each element of this matrix. + /// + public override void Negate() + { + Multiply(-1); + } + + /// + /// Generates matrix with random elements. + /// + /// Number of rows. + /// Number of columns. + /// Continuous Random Distribution or Source + /// + /// An numberOfRows-by-numberOfColumns matrix with elements distributed according to the provided distribution. + /// + /// If the parameter is not positive. + /// If the parameter is not positive. + public override Matrix 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); + var mn = Math.Min(numberOfRows, numberOfColumns); + CommonParallel.For(0, mn, i => matrix[i, i] = (float)distribution.Sample()); + + return matrix; + } + + /// + /// Generates matrix with random elements. + /// + /// Number of rows. + /// Number of columns. + /// Continuous Random Distribution or Source + /// + /// An numberOfRows-by-numberOfColumns matrix with elements distributed according to the provided distribution. + /// + /// If the parameter is not positive. + /// If the parameter is not positive. + public override Matrix 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; + } + + /// + /// Returns a that represents this instance. + /// + /// + /// The format to use. + /// + /// + /// The format provider to use. + /// + /// + /// A that represents this instance. + /// + public override string ToString(string format, IFormatProvider formatProvider) + { + var stringBuilder = new StringBuilder(); + for (var row = 0; row < RowCount; row++) + { + for (var column = 0; column < ColumnCount; column++) + { + stringBuilder.Append(At(row, column).ToString(format, formatProvider)); + if (column != ColumnCount - 1) + { + stringBuilder.Append(formatProvider.GetTextInfo().ListSeparator); + } + } + + if (row != RowCount - 1) + { + stringBuilder.Append(Environment.NewLine); + } + } + + return stringBuilder.ToString(); + } + + #region Simple arithmetic of type T + /// + /// Add two values T+T + /// + /// Left operand value + /// Right operand value + /// Result of addition + protected sealed override float AddT(float val1, float val2) + { + return val1 + val2; + } + + /// + /// Subtract two values T-T + /// + /// Left operand value + /// Right operand value + /// Result of subtract + protected sealed override float SubtractT(float val1, float val2) + { + return val1 - val2; + } + + /// + /// Multiply two values T*T + /// + /// Left operand value + /// Right operand value + /// Result of multiplication + protected sealed override float MultiplyT(float val1, float val2) + { + return val1 * val2; + } + + /// + /// Divide two values T/T + /// + /// Left operand value + /// Right operand value + /// Result of divide + protected sealed override float DivideT(float val1, float val2) + { + return val1 / val2; + } + + /// + /// Take absolute value + /// + /// Source alue + /// True if one; otherwise false + protected sealed override double AbsoluteT(float val1) + { + return Math.Abs(val1); + } + + #endregion + } +} diff --git a/src/Numerics/LinearAlgebra/Single/Factorization/DenseCholesky.cs b/src/Numerics/LinearAlgebra/Single/Factorization/DenseCholesky.cs new file mode 100644 index 00000000..2463a627 --- /dev/null +++ b/src/Numerics/LinearAlgebra/Single/Factorization/DenseCholesky.cs @@ -0,0 +1,212 @@ +// +// 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. +// + +namespace MathNet.Numerics.LinearAlgebra.Single.Factorization +{ + using System; + using Generic; + using Generic.Factorization; + using Properties; + + /// + /// A class which encapsulates the functionality of a Cholesky factorization for dense matrices. + /// For a symmetric, positive definite matrix A, the Cholesky factorization + /// is an lower triangular matrix L so that A = L*L'. + /// + /// + /// 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. + /// + public class DenseCholesky : Cholesky + { + /// + /// Initializes a new instance of the class. This object will compute the + /// Cholesky factorization when the constructor is called and cache it's factorization. + /// + /// The matrix to factor. + /// If is null. + /// If is not a square matrix. + /// If is not positive definite. + 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; + } + + /// + /// Solves a system of linear equations, AX = B, with A Cholesky factorized. + /// + /// The right hand side , B. + /// The left hand side , X. + 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"); + } + + // 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); + } + + /// + /// Solves a system of linear equations, Ax = b, with A Cholesky factorized. + /// + /// The right hand side vector, b. + /// The left hand side , x. + public override void Solve(Vector input, Vector 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 + /// + /// Add two values T+T + /// + /// Left operand value + /// Right operand value + /// Result of addition + protected sealed override float AddT(float val1, float val2) + { + return val1 + val2; + } + + /// + /// Multiply two values T*T + /// + /// Left operand value + /// Right operand value + /// Result of multiplication + protected sealed override float MultiplyT(float val1, float val2) + { + return val1 * val2; + } + + /// + /// Returns the natural (base e) logarithm of a specified number. + /// + /// A number whose logarithm is to be found + /// Natural (base e) logarithm + protected sealed override float LogT(float val1) + { + return (float)Math.Log(val1); + } + #endregion + } +} diff --git a/src/Numerics/LinearAlgebra/Single/Factorization/DenseLU.cs b/src/Numerics/LinearAlgebra/Single/Factorization/DenseLU.cs new file mode 100644 index 00000000..f4dcebe2 --- /dev/null +++ b/src/Numerics/LinearAlgebra/Single/Factorization/DenseLU.cs @@ -0,0 +1,204 @@ +// +// 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. +// + +namespace MathNet.Numerics.LinearAlgebra.Single.Factorization +{ + using System; + using Generic; + using Generic.Factorization; + using Properties; + + /// + /// A class which encapsulates the functionality of an LU factorization. + /// 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. + /// + /// + /// The computation of the LU factorization is done at construction time. + /// + public class DenseLU : LU + { + /// + /// Initializes a new instance of the class. This object will compute the + /// LU factorization when the constructor is called and cache it's factorization. + /// + /// The matrix to factor. + /// If is null. + /// If is not a square matrix. + 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; + } + + /// + /// Solves a system of linear equations, AX = B, with A LU factorized. + /// + /// The right hand side , B. + /// The left hand side , X. + 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"); + } + + // 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); + } + + /// + /// Solves a system of linear equations, Ax = b, with A LU factorized. + /// + /// The right hand side vector, b. + /// The left hand side , x. + public override void Solve(Vector input, Vector 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); + } + + /// + /// Returns the inverse of this matrix. The inverse is calculated using LU decomposition. + /// + /// The inverse of this matrix. + public override Matrix Inverse() + { + var result = (DenseMatrix)Factors.Clone(); + Control.LinearAlgebraProvider.LUInverseFactored(result.Data, result.RowCount, Pivots); + return result; + } + + #region Simple arithmetic of type T + + /// + /// Multiply two values T*T + /// + /// Left operand value + /// Right operand value + /// Result of multiplication + protected sealed override float MultiplyT(float val1, float val2) + { + return val1 * val2; + } + #endregion + } +} diff --git a/src/Numerics/LinearAlgebra/Single/Factorization/DenseQR.cs b/src/Numerics/LinearAlgebra/Single/Factorization/DenseQR.cs new file mode 100644 index 00000000..6936dd21 --- /dev/null +++ b/src/Numerics/LinearAlgebra/Single/Factorization/DenseQR.cs @@ -0,0 +1,194 @@ +// +// 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. +// + +namespace MathNet.Numerics.LinearAlgebra.Single.Factorization +{ + using System; + using Generic; + using Generic.Factorization; + using Properties; + + /// + /// A class which encapsulates the functionality of the QR decomposition. + /// 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). + /// + /// + /// The computation of the QR decomposition is done at construction time by Householder transformation. + /// + public class DenseQR : QR + { + /// + /// Initializes a new instance of the class. This object will compute the + /// QR factorization when the constructor is called and cache it's factorization. + /// + /// The matrix to factor. + /// If is null. + /// If row count is less then column count + 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); + } + + /// + /// Solves a system of linear equations, AX = B, with A QR factorized. + /// + /// The right hand side , B. + /// The left hand side , X. + 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."); + } + + Control.LinearAlgebraProvider.QRSolveFactored(((DenseMatrix)MatrixQ).Data, ((DenseMatrix)MatrixR).Data, MatrixR.RowCount, MatrixR.ColumnCount, dinput.Data, input.ColumnCount, dresult.Data); + } + + /// + /// Solves a system of linear equations, Ax = b, with A QR factorized. + /// + /// The right hand side vector, b. + /// The left hand side , x. + 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."); + } + + Control.LinearAlgebraProvider.QRSolveFactored(((DenseMatrix)MatrixQ).Data, ((DenseMatrix)MatrixR).Data, MatrixR.RowCount, MatrixR.ColumnCount, dinput.Data, 1, dresult.Data); + } + + #region Simple arithmetic of type T + + /// + /// Multiply two values T*T + /// + /// Left operand value + /// Right operand value + /// Result of multiplication + protected sealed override float MultiplyT(float val1, float val2) + { + return val1 * val2; + } + + /// + /// Returns the absolute value of a specified number. + /// + /// A number whose absolute is to be found + /// Absolute value + protected sealed override double AbsoluteT(float val1) + { + return Math.Abs(val1); + } + + #endregion + } +} diff --git a/src/Numerics/LinearAlgebra/Single/Factorization/DenseSvd.cs b/src/Numerics/LinearAlgebra/Single/Factorization/DenseSvd.cs new file mode 100644 index 00000000..d791c857 --- /dev/null +++ b/src/Numerics/LinearAlgebra/Single/Factorization/DenseSvd.cs @@ -0,0 +1,206 @@ +// +// 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. +// +namespace MathNet.Numerics.LinearAlgebra.Single.Factorization +{ + using System; + using Generic; + using Generic.Factorization; + using Properties; + + /// + /// A class which encapsulates the functionality of the singular value decomposition (SVD) for . + /// 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. + /// + /// + /// The computation of the singular value decomposition is done at construction time. + /// + public class DenseSvd : Svd + { + /// + /// Initializes a new instance of the class. This object will compute the + /// the singular value decomposition when the constructor is called and cache it's decomposition. + /// + /// The matrix to factor. + /// Compute the singular U and VT vectors or not. + /// If is null. + /// If SVD algorithm failed to converge with matrix . + 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); + } + + /// + /// Solves a system of linear equations, AX = B, with A SVD factorized. + /// + /// The right hand side , B. + /// The left hand side , X. + 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"); + } + + 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); + } + + /// + /// Solves a system of linear equations, Ax = b, with A SVD factorized. + /// + /// The right hand side vector, b. + /// The left hand side , x. + public override void Solve(Vector input, Vector 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 + + /// + /// Multiply two values T*T + /// + /// Left operand value + /// Right operand value + /// Result of multiplication + protected sealed override float MultiplyT(float val1, float val2) + { + return val1 * val2; + } + + /// + /// Returns the absolute value of a specified number. + /// + /// A number whose absolute is to be found + /// Absolute value + protected sealed override double AbsoluteT(float val1) + { + return Math.Abs(val1); + } + #endregion + } +} diff --git a/src/Numerics/LinearAlgebra/Single/Factorization/GramSchmidt.cs b/src/Numerics/LinearAlgebra/Single/Factorization/GramSchmidt.cs new file mode 100644 index 00000000..a84c7362 --- /dev/null +++ b/src/Numerics/LinearAlgebra/Single/Factorization/GramSchmidt.cs @@ -0,0 +1,311 @@ +// +// 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. +// + +namespace MathNet.Numerics.LinearAlgebra.Single.Factorization +{ + using System; + using Generic; + using Generic.Factorization; + using Properties; + + /// + /// A class which encapsulates the functionality of the QR decomposition Modified Gram-Schmidt Orthogonalization. + /// 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. + /// + /// + /// The computation of the QR decomposition is done at construction time by modified Gram-Schmidt Orthogonalization. + /// + public class GramSchmidt : QR + { + /// + /// Initializes a new instance of the class. This object creates an orthogonal matrix + /// using the modified Gram-Schmidt method. + /// + /// The matrix to factor. + /// If is null. + /// If row count is less then column count + /// If is rank deficient + public GramSchmidt(Matrix 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); + } + } + } + } + + /// + /// Gets a value indicating whether the matrix is full rank or not. + /// + /// true if the matrix is full rank; otherwise false. + public override bool IsFullRank + { + get + { + return true; + } + } + + /// + /// Gets the determinant of the matrix for which the QR matrix was computed. + /// + 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); + } + } + + /// + /// Solves a system of linear equations, AX = B, with A QR factorized. + /// + /// The right hand side , B. + /// The left hand side , X. + 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 (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)); + } + } + } + + /// + /// Solves a system of linear equations, Ax = b, with A QR factorized. + /// + /// The right hand side vector, b. + /// The left hand side , x. + 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 (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 + + /// + /// Multiply two values T*T + /// + /// Left operand value + /// Right operand value + /// Result of multiplication + protected sealed override float MultiplyT(float val1, float val2) + { + return val1 * val2; + } + + /// + /// Returns the absolute value of a specified number. + /// + /// A number whose absolute is to be found + /// Absolute value + protected sealed override double AbsoluteT(float val1) + { + return Math.Abs(val1); + } + + #endregion + } +} diff --git a/src/Numerics/LinearAlgebra/Single/Factorization/UserCholesky.cs b/src/Numerics/LinearAlgebra/Single/Factorization/UserCholesky.cs new file mode 100644 index 00000000..f2aa0719 --- /dev/null +++ b/src/Numerics/LinearAlgebra/Single/Factorization/UserCholesky.cs @@ -0,0 +1,259 @@ +// +// 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. +// + +namespace MathNet.Numerics.LinearAlgebra.Single.Factorization +{ + using System; + using Generic; + using Generic.Factorization; + using Properties; + + /// + /// A class which encapsulates the functionality of a Cholesky factorization for user matrices. + /// For a symmetric, positive definite matrix A, the Cholesky factorization + /// is an lower triangular matrix L so that A = L*L'. + /// + /// + /// 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. + /// + public class UserCholesky : Cholesky + { + /// + /// Initializes a new instance of the class. This object will compute the + /// Cholesky factorization when the constructor is called and cache it's factorization. + /// + /// The matrix to factor. + /// If is null. + /// If is not a square matrix. + /// If is not positive definite. + public UserCholesky(Matrix 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); + } + } + } + + /// + /// Solves a system of linear equations, AX = B, with A Cholesky factorized. + /// + /// The right hand side , B. + /// The left hand side , X. + public override void Solve(Matrix input, Matrix 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)); + } + } + } + + /// + /// Solves a system of linear equations, Ax = b, with A Cholesky factorized. + /// + /// The right hand side vector, b. + /// The left hand side , x. + public override void Solve(Vector input, Vector 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 + /// + /// Add two values T+T + /// + /// Left operand value + /// Right operand value + /// Result of addition + protected sealed override float AddT(float val1, float val2) + { + return val1 + val2; + } + + /// + /// Multiply two values T*T + /// + /// Left operand value + /// Right operand value + /// Result of multiplication + protected sealed override float MultiplyT(float val1, float val2) + { + return val1 * val2; + } + + /// + /// Returns the natural (base e) logarithm of a specified number. + /// + /// A number whose logarithm is to be found + /// Natural (base e) logarithm + protected sealed override float LogT(float val1) + { + return (float)Math.Log(val1); + } + + #endregion + } +} diff --git a/src/Numerics/LinearAlgebra/Single/Factorization/UserLU.cs b/src/Numerics/LinearAlgebra/Single/Factorization/UserLU.cs new file mode 100644 index 00000000..4be798b9 --- /dev/null +++ b/src/Numerics/LinearAlgebra/Single/Factorization/UserLU.cs @@ -0,0 +1,316 @@ +// +// 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. +// + +namespace MathNet.Numerics.LinearAlgebra.Single.Factorization +{ + using System; + using Generic; + using Generic.Factorization; + using Properties; + + /// + /// A class which encapsulates the functionality of an LU factorization. + /// 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. + /// + /// + /// The computation of the LU factorization is done at construction time. + /// + public class UserLU : LU + { + /// + /// Initializes a new instance of the class. This object will compute the + /// LU factorization when the constructor is called and cache it's factorization. + /// + /// The matrix to factor. + /// If is null. + /// If is not a square matrix. + public UserLU(Matrix 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))); + } + } + } + } + + /// + /// Solves a system of linear equations, AX = B, with A LU factorized. + /// + /// The right hand side , B. + /// The left hand side , X. + 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"); + } + + // 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); + } + } + } + } + + /// + /// Solves a system of linear equations, Ax = b, with A LU factorized. + /// + /// The right hand side vector, b. + /// The left hand side , x. + public override void Solve(Vector input, Vector 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); + } + } + } + + /// + /// Returns the inverse of this matrix. The inverse is calculated using LU decomposition. + /// + /// The inverse of this matrix. + public override Matrix 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 + + /// + /// Multiply two values T*T + /// + /// Left operand value + /// Right operand value + /// Result of multiplication + protected sealed override float MultiplyT(float val1, float val2) + { + return val1 * val2; + } + #endregion + } +} diff --git a/src/Numerics/LinearAlgebra/Single/Factorization/UserQR.cs b/src/Numerics/LinearAlgebra/Single/Factorization/UserQR.cs new file mode 100644 index 00000000..5c573594 --- /dev/null +++ b/src/Numerics/LinearAlgebra/Single/Factorization/UserQR.cs @@ -0,0 +1,357 @@ +// +// 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. +// + +namespace MathNet.Numerics.LinearAlgebra.Single.Factorization +{ + using System; + using System.Linq; + using Generic; + using Generic.Factorization; + using Properties; + + /// + /// A class which encapsulates the functionality of the QR decomposition. + /// 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). + /// + /// + /// The computation of the QR decomposition is done at construction time by Householder transformation. + /// + public class UserQR : QR + { + /// + /// Initializes a new instance of the class. This object will compute the + /// QR factorization when the constructor is called and cache it's factorization. + /// + /// The matrix to factor. + /// If is null. + public UserQR(Matrix 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); + } + } + + /// + /// Generate column from initial matrix to work array + /// + /// Initial matrix + /// The firts row + /// The last row + /// Column index + /// Generated vector + private static float[] GenerateColumn(Matrix 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; + } + + /// + /// Perform calculation of Q or R + /// + /// Work array + /// Q or R matrices + /// The first row + /// The last row + /// The first column + /// The last column + private static void ComputeQR(float[] u, Matrix 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])); + } + } + } + + /// + /// Solves a system of linear equations, AX = B, with A QR factorized. + /// + /// The right hand side , B. + /// The left hand side , X. + 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 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)); + } + } + } + + /// + /// Solves a system of linear equations, Ax = b, with A QR factorized. + /// + /// The right hand side vector, b. + /// The left hand side , x. + 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 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 + + /// + /// Multiply two values T*T + /// + /// Left operand value + /// Right operand value + /// Result of multiplication + protected sealed override float MultiplyT(float val1, float val2) + { + return val1 * val2; + } + + /// + /// Returns the absolute value of a specified number. + /// + /// A number whose absolute is to be found + /// Absolute value + protected sealed override double AbsoluteT(float val1) + { + return Math.Abs(val1); + } + #endregion + } +} diff --git a/src/Numerics/LinearAlgebra/Single/Factorization/UserSvd.cs b/src/Numerics/LinearAlgebra/Single/Factorization/UserSvd.cs new file mode 100644 index 00000000..8cba9b96 --- /dev/null +++ b/src/Numerics/LinearAlgebra/Single/Factorization/UserSvd.cs @@ -0,0 +1,950 @@ +// +// 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. +// +namespace MathNet.Numerics.LinearAlgebra.Single.Factorization +{ + using System; + using Generic; + using Generic.Factorization; + using Properties; + + /// + /// A class which encapsulates the functionality of the singular value decomposition (SVD) for . + /// 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. + /// + /// + /// The computation of the singular value decomposition is done at construction time. + /// + public class UserSvd : Svd + { + /// + /// Initializes a new instance of the class. This object will compute the + /// the singular value decomposition when the constructor is called and cache it's decomposition. + /// + /// The matrix to factor. + /// Compute the singular U and VT vectors or not. + /// If is null. + /// If SVD algorithm failed to converge with matrix . + public UserSvd(Matrix 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; + } + } + + /// + /// Calculates absolute value of multiplied on signum function of + /// + /// Double value z1 + /// Double value z2 + /// Result multiplication of signum function and absolute value + private static float Dsign(float z1, float z2) + { + return Math.Abs(z1) * (z2 / Math.Abs(z2)); + } + + /// + /// Swap column and + /// + /// Source matrix + /// The number of rows in + /// Column A index to swap + /// Column B index to swap + private static void Dswap(Matrix 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); + } + } + + /// + /// Scale column by starting from row + /// + /// Source matrix + /// The number of rows in + /// Column to scale + /// Row to scale from + /// Scale value + private static void DscalColumn(Matrix 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); + } + } + + /// + /// Scale vector by starting from index + /// + /// Source vector + /// Row to scale from + /// Scale value + private static void DscalVector(float[] a, int start, float z) + { + for (var i = start; i < a.Length; i++) + { + a[i] = a[i] * z; + } + } + + /// + /// 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. + /// + /// Provides the x-coordinate of the point p. On exit contains the parameter r associated with the Givens rotation + /// Provides the y-coordinate of the point p. On exit contains the parameter z associated with the Givens rotation + /// Contains the parameter c associated with the Givens rotation + /// Contains the parameter s associated with the Givens rotation + /// This is equivalent to the DROTG LAPACK routine. + 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; + } + + /// dded + /// Calculate Norm 2 of the column in matrix starting from row + /// + /// Source matrix + /// The number of rows in + /// Column index + /// Start row index + /// Norm2 (Euclidean norm) of trhe column + private static float Dnrm2Column(Matrix 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); + } + + /// + /// Calculate Norm 2 of the vector starting from index + /// + /// Source vector + /// Start index + /// Norm2 (Euclidean norm) of the vector + 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); + } + + /// + /// Calculate dot product of and + /// + /// Source matrix + /// The number of rows in + /// Index of column A + /// Index of column B + /// Starting row index + /// Dot product value + private static float Ddot(Matrix 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; + } + + /// + /// Performs rotation of points in the plane. Given two vectors x and y , + /// 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) + /// + /// Source matrix + /// The number of rows in + /// Index of column A + /// Index of column B + /// Scalar "c" value + /// Scalar "s" value + private static void Drot(Matrix 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); + } + } + + /// + /// Solves a system of linear equations, AX = B, with A SVD factorized. + /// + /// The right hand side , B. + /// The left hand side , X. + 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"); + } + + 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; + } + } + } + + /// + /// Solves a system of linear equations, Ax = b, with A SVD factorized. + /// + /// The right hand side vector, b. + /// The left hand side , x. + public override void Solve(Vector input, Vector 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 + + /// + /// Multiply two values T*T + /// + /// Left operand value + /// Right operand value + /// Result of multiplication + protected sealed override float MultiplyT(float val1, float val2) + { + return val1 * val2; + } + + /// + /// Returns the absolute value of a specified number. + /// + /// A number whose absolute is to be found + /// Absolute value + protected sealed override double AbsoluteT(float val1) + { + return Math.Abs(val1); + } + + #endregion + } +} diff --git a/src/Numerics/LinearAlgebra/Single/IO/DelimitedReader.cs b/src/Numerics/LinearAlgebra/Single/IO/DelimitedReader.cs new file mode 100644 index 00000000..8ffed659 --- /dev/null +++ b/src/Numerics/LinearAlgebra/Single/IO/DelimitedReader.cs @@ -0,0 +1,197 @@ +// +// 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. +// + +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; + + /// + /// Creates a from a delimited text file. If the user does not + /// specify a delimiter, then any whitespace is used. + /// + /// The type of the matrix to return. + public class DelimitedReader : MatrixReader + where TMatrix : Matrix + { + /// + /// Constructor to create matrix instance. + /// + private static readonly ConstructorInfo Constructor = typeof(TMatrix).GetConstructor(new[] { typeof(int), typeof(int) }); + + /// + /// Whitespace regular expression. + /// + private static readonly Regex WhiteSpace = new Regex(@"[\s]+"); + + /// + /// The delimiter to use. + /// + private readonly Regex _delimiter; + + /// + /// The to use. + /// + private CultureInfo _cultureInfo = CultureInfo.CurrentCulture; + + /// + /// Initializes a new instance of the class using + /// any whitespace as the delimiter. + /// + public DelimitedReader() + { + _delimiter = WhiteSpace; + } + + /// + /// Initializes a new instance of the class. + /// + /// The delimiter to use. + public DelimitedReader(char delimiter) + { + _delimiter = new Regex("[" + new string(delimiter, 1) + "]+"); + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// The delimiter to use. + /// + /// + /// If is . + /// + public DelimitedReader(string delimiter) + { + if (delimiter == null) + { + throw new ArgumentNullException("delimiter"); + } + + _delimiter = new Regex("[" + delimiter + "]+"); + } + + /// + /// Gets or sets the to use when parsing the numbers. + /// + /// The culture info. + /// Defaults to CultureInfo.CurrentCulture. + public CultureInfo CultureInfo + { + get + { + return _cultureInfo; + } + + set + { + if (value != null) + { + _cultureInfo = value; + } + } + } + + /// + /// Gets or sets a value indicating whether the files has a header row. + /// + /// + /// true if this instance has a header row; otherwise, false. + /// + /// Defaults to . + public bool HasHeaderRow + { + get; + set; + } + + /// + /// Performs the actual reading. + /// + /// The to read the matrix from. + /// + /// A matrix containing the data from the . is returned if the is empty. + /// + protected override TMatrix DoReadMatrix(Stream stream) + { + var data = new List(); + + // 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; + } + } +} diff --git a/src/Numerics/LinearAlgebra/Single/IO/DelimitedWriter.cs b/src/Numerics/LinearAlgebra/Single/IO/DelimitedWriter.cs new file mode 100644 index 00000000..416764c9 --- /dev/null +++ b/src/Numerics/LinearAlgebra/Single/IO/DelimitedWriter.cs @@ -0,0 +1,168 @@ +// +// 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. +// + +namespace MathNet.Numerics.LinearAlgebra.Single.IO +{ + using System; + using System.Collections.Generic; + using System.Globalization; + using System.IO; + using Generic; + + /// + /// Writes an to delimited text file. If the user does not + /// specify a delimiter, a tab separator is used. + /// + public class DelimitedWriter : MatrixWriter + { + /// + /// The delimiter to use. + /// + private readonly string _delimiter; + + /// + /// The to use. + /// + private CultureInfo _cultureInfo = CultureInfo.CurrentCulture; + + /// + /// Initializes a new instance of the class using + /// a comma as the delimiter. + /// + public DelimitedWriter() + { + _delimiter = ","; + } + + /// + /// Initializes a new instance of the class + /// using the given delimiter. + /// + /// + /// the delimiter to use. + /// + public DelimitedWriter(char delimiter) + { + _delimiter = new string(delimiter, 1); + } + + /// + /// Initializes a new instance of the class + /// using the given delimiter. + /// + /// + /// the delimiter to use. + /// + public DelimitedWriter(string delimiter) + { + _delimiter = delimiter; + } + + /// + /// Gets or sets the column header values. + /// + /// The column header values. + /// Will write the column headers if the list is not empty or null. + public IList ColumnHeaders + { + get; + set; + } + + /// + /// Gets or sets the to use when parsing the numbers. + /// + /// The culture info. + /// Defaults to CultureInfo.CurrentCulture. + public CultureInfo CultureInfo + { + get + { + return _cultureInfo; + } + + set + { + if (value != null) + { + _cultureInfo = value; + } + } + } + + /// + /// Writes the given to the given . + /// + /// The matrix to write. + /// The to write the matrix to. + /// The format to use on each element. + /// If either or is null. + protected override void DoWriteMatrix(Matrix 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); + } + } + } + } +} diff --git a/src/Numerics/LinearAlgebra/Single/IO/Matlab/MatlabFile.cs b/src/Numerics/LinearAlgebra/Single/IO/Matlab/MatlabFile.cs new file mode 100644 index 00000000..01b216fe --- /dev/null +++ b/src/Numerics/LinearAlgebra/Single/IO/Matlab/MatlabFile.cs @@ -0,0 +1,219 @@ +// +// 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. +// + +namespace MathNet.Numerics.LinearAlgebra.Single.IO.Matlab +{ + using System.Collections.Generic; + using Generic; + + /// + /// Represents a Matlab file + /// + internal class MatlabFile + { + /// + /// Matrices in a matlab file stored as 1-D arrays + /// + private readonly IDictionary> _matrices = new SortedList>(); + + /// + /// Gets or sets the header text. + /// + /// The header text. + public string HeaderText { get; set; } + + /// + /// Gets or sets the first name of the matrix. + /// + /// The first name of the matrix. + public string FirstMatrixName { get; set; } + + /// + /// Gets the first matrix. + /// + /// The first matrix. + public Matrix FirstMatrix + { + get + { + if (string.IsNullOrEmpty(FirstMatrixName) || !_matrices.ContainsKey(FirstMatrixName)) + { + return null; + } + + return _matrices[FirstMatrixName]; + } + } + + /// + /// Gets the matrices. + /// + /// The matrices. + public IDictionary> Matrices + { + get { return _matrices; } + } + } + + /* + /// + /// An + /// + /// + /// + internal class ListDictionary : IDictionary + { + private readonly IList _keys = new List(); + private readonly IList _values = new List(); + + public void Add(T key, K value) + { + _keys.Add(key); + _values.Add(value); + } + + public bool ContainsKey(T key) + { + return _keys.Contains(key); + } + + public ICollection 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 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 item) + { + Add(item.Key, item.Value); + } + + public void Clear() + { + _keys.Clear(); + _values.Clear(); + } + + public bool Contains(KeyValuePair item) + { + return _keys.Contains(item.Key) && _values[_keys.IndexOf(item.Key)].Equals(item.Value); + } + + public void CopyTo(KeyValuePair[] array, int arrayIndex) + { + for (int i = 0; i < _keys.Count; i++) + { + array[arrayIndex + i] = new KeyValuePair(_keys[i], _values[i]); + } + } + + public int Count + { + get { return _keys.Count; } + } + + public bool IsReadOnly + { + get { return false; } + } + + public bool Remove(KeyValuePair item) + { + if (Contains(item)) + { + Remove(item.Key); + return true; + } + return false; + } + + public IEnumerator> GetEnumerator() + { + for (int i = 0; i < _keys.Count; i++) + { + yield return new KeyValuePair(_keys[i], _values[i]); + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + }*/ +} diff --git a/src/Numerics/LinearAlgebra/Single/IO/Matlab/MatlabParser.cs b/src/Numerics/LinearAlgebra/Single/IO/Matlab/MatlabParser.cs new file mode 100644 index 00000000..5c626022 --- /dev/null +++ b/src/Numerics/LinearAlgebra/Single/IO/Matlab/MatlabParser.cs @@ -0,0 +1,536 @@ +// +// 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. +// + +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; + + /// + /// Parse a Matlab file + /// + internal class MatlabParser + { + /// + /// Large Block Size + /// + private const int LargeBlockSize = 8; + + /// + /// Little Endian Indicator + /// + private const byte LittleEndianIndicator = 0x49; + + /// + /// Small Block Size + /// + private const int SmallBlockSize = 4; + + /// + /// Holds the names of the matrices in the file. + /// + private readonly IList _names = new List(); + + /// + /// The stream to read the matlab file from. + /// + private readonly Stream _stream; + + /// + /// Initializes a new instance of the class. + /// + /// Name of the file. + public MatlabParser(string fileName) + : this(fileName, new string[0]) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The stream to read from. + public MatlabParser(Stream stream) + : this(stream, new string[0]) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The stream to read from. + /// The name of the objects to retrieve. + public MatlabParser(Stream stream, IEnumerable objectNames) + { + if (stream == null) + { + throw new ArgumentNullException("stream"); + } + + _stream = stream; + SetNames(objectNames); + } + + /// + /// Initializes a new instance of the class. + /// + /// Name of the file. + /// The name of the objects to retrieve. + public MatlabParser(string fileName, IEnumerable objectNames) + { + if (string.IsNullOrEmpty(fileName)) + { + throw new ArgumentException(Resources.StringNullOrEmpty, "filename"); + } + + _stream = File.OpenRead(fileName); + SetNames(objectNames); + } + + /// + /// Copies the names of the objects to retrieve to a local field. + /// + /// The name of the objects to retrieve. + private void SetNames(IEnumerable objectNames) + { + foreach (var name in objectNames) + { + _names.Add(name); + } + } + + /// + /// Parses the file. + /// + /// The parsed Matlab file as a object. + 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; + } + + /// + /// Aligns the data. + /// + /// The stream. + /// The size of the array. + /// if set to true if reading from a small block. + 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); + } + + /// + /// Decompresses the block. + /// + /// The compressed data. + /// The type data type contained in the block. + /// The decompressed block. + 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; + } + + /// + /// Adds a matrix from the actual file into our presentation of a matlab file. + /// + /// The data of the matrix. + /// The instance. + 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 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; + } + } + } + } + + /// + /// Populates a sparse matrix. + /// + /// The reader. + /// The number of rows. + /// The number of columns. + /// The size of the block. + /// A populated sparse matrix. + private static Matrix 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 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; + } + + /// + /// Populates a dense matrix. + /// + /// The type of data. + /// The reader. + /// The number of rows. + /// The number of columns. + /// Returns a populated dense matrix. + private static Matrix PopulateDenseMatrix(DataType type, BinaryReader reader, int rows, int columns) + { + Matrix 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; + } + } +} diff --git a/src/Numerics/LinearAlgebra/Single/IO/MatlabReader.cs b/src/Numerics/LinearAlgebra/Single/IO/MatlabReader.cs new file mode 100644 index 00000000..2c748eaa --- /dev/null +++ b/src/Numerics/LinearAlgebra/Single/IO/MatlabReader.cs @@ -0,0 +1,190 @@ +// +// 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. +// + +namespace MathNet.Numerics.LinearAlgebra.Single.IO +{ + using System; + using System.Collections.Generic; + using System.IO; + using Generic; + using Matlab; + using Properties; + + /// + /// Creates matrices from Matlab files. + /// + public class MatlabMatrixReader + { + /// + /// The name of the file to read from. + /// + private readonly string _filename; + + /// + /// The stream to read from if we are not reading from a file directly. + /// + private readonly Stream _stream; + + /// + /// Initializes a new instance of the class. + /// + /// Name of the file to read matrices from. + 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; + } + + /// + /// Initializes a new instance of the class. + /// + /// The stream to reader matrices from. + public MatlabMatrixReader(Stream stream) + { + if (stream == null) + { + throw new ArgumentNullException("stream"); + } + + _stream = stream; + } + + /// + /// Reads the first matrix from the file or stream. + /// + /// + /// If the matrix is stored as a sparse matrix, then a is returned. Otherwise, a + /// is returned. + /// + public Matrix ReadMatrix() + { + return ReadMatrix(null); + } + + /// + /// Reads the named matrix from the file or stream. + /// + /// The name of the matrix to read. + /// + /// If the matrix is stored as a sparse matrix, then a is returned. Otherwise, a + /// is returned. is returned if a matrix with the requests name doesn't exist. + /// + public Matrix 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 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; + } + + /// + /// Reads all matrices from the file or stream. + /// + /// All matrices from the file or stream. + public Matrix[] ReadMatrices() + { + return ReadMatrices(new string[] { }); + } + + /// + /// Reads the named matrices from the file or stream. + /// + /// The names of the matrices to retrieve. + /// + /// The named matrices from the file or stream. + /// + public Matrix[] ReadMatrices(IEnumerable 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[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; + } + } +} diff --git a/src/Numerics/LinearAlgebra/Single/IO/MatrixReader.cs b/src/Numerics/LinearAlgebra/Single/IO/MatrixReader.cs new file mode 100644 index 00000000..8f6364d0 --- /dev/null +++ b/src/Numerics/LinearAlgebra/Single/IO/MatrixReader.cs @@ -0,0 +1,84 @@ +// +// 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. +// + +namespace MathNet.Numerics.LinearAlgebra.Single.IO +{ + using System; + using System.IO; + using Generic; + using Properties; + + /// + /// Base class to read a single from a file or stream. + /// + /// The type of Matrix to return. + public abstract class MatrixReader where TMatrix : Matrix + { + /// + /// Reads a from a file. + /// + /// The file to read the matrix from. + /// A containing the data from the file. is returned if the file is empty. + /// If is . + /// If the file doesn't exist. + /// If a value is not a number or not in a valid format. + /// If a value represents a number less than or greater than . + public Matrix ReadMatrix(string file) + { + if (file == null) + { + throw new ArgumentNullException("file", Resources.StringNullOrEmpty); + } + + return ReadMatrix(File.OpenRead(file)); + } + + /// + /// Reads a from a . + /// + /// The to read the matrix from. + /// A matrix containing the data from the . is returned if the is empty. + /// If is . + /// If a value is not a number or not in a valid format. + /// If a value represents a number less than or greater than . + public TMatrix ReadMatrix(Stream stream) + { + if (stream == null) + { + throw new ArgumentNullException("stream"); + } + + return DoReadMatrix(stream); + } + + /// + /// Subclasses override this method to do the actual reading. + /// + /// The to read the matrix from. + /// A matrix containing the data from the . is returned if the is empty. + protected abstract TMatrix DoReadMatrix(Stream stream); + } +} diff --git a/src/Numerics/LinearAlgebra/Single/IO/MatrixWriter.cs b/src/Numerics/LinearAlgebra/Single/IO/MatrixWriter.cs new file mode 100644 index 00000000..d1d9a2d3 --- /dev/null +++ b/src/Numerics/LinearAlgebra/Single/IO/MatrixWriter.cs @@ -0,0 +1,197 @@ +// +// 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 System.IO; + +namespace MathNet.Numerics.LinearAlgebra.Single.IO +{ + using Generic; + + /// + /// Base class to write a single to a file or stream. + /// + public abstract class MatrixWriter + { + /// + /// Writes the given to the given file. If the file already exists, + /// the file will be overwritten. + /// + /// The matrix to write. + /// The file to write the matrix to. + /// If either or is null. + public void WriteMatrix(Matrix 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); + } + } + + /// + /// Writes the given to the given file. If the file already exists, + /// the file will be overwritten. + /// + /// the matrix to write. + /// The file to write the matrix to. + /// The format to use on each element. + /// If either or is null. + public void WriteMatrix(Matrix 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); + } + } + + /// + /// Writes the given to the given stream. + /// + /// The matrix to write. + /// The to write the matrix to. + /// If either or is null. + public void WriteMatrix(Matrix 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); + } + } + + /// + /// Writes the given to the given stream. + /// + /// The to write. + /// The to write the matrix to. + /// The format to use on each element. + /// If either or is null. + public void WriteMatrix(Matrix 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); + } + } + + /// + /// Writes the given to the given . + /// + /// The matrix to write. + /// The to write the matrix to. + /// If either or is null. + public void WriteMatrix(Matrix matrix, TextWriter writer) + { + if (matrix == null) + { + throw new ArgumentNullException("matrix"); + } + + if (writer == null) + { + throw new ArgumentNullException("writer"); + } + + DoWriteMatrix(matrix, writer, null); + } + + /// + /// Writes the given to the given . + /// + /// The matrix to write. + /// The to write the matrix to. + /// The format to use on each element. + /// If either or is null. + public void WriteMatrix(Matrix matrix, TextWriter writer, string format) + { + if (matrix == null) + { + throw new ArgumentNullException("matrix"); + } + + if (writer == null) + { + throw new ArgumentNullException("writer"); + } + + DoWriteMatrix(matrix, writer, format); + } + + /// + /// Subclasses must implement this method to do the actually writing. + /// + /// The matrix to serialize. + /// The to write the matrix to. + /// The format for the new matrix. + protected abstract void DoWriteMatrix(Matrix matrix, TextWriter writer, string format); + } +} diff --git a/src/Numerics/LinearAlgebra/Single/Solvers/Iterative/BiCgStab.cs b/src/Numerics/LinearAlgebra/Single/Solvers/Iterative/BiCgStab.cs new file mode 100644 index 00000000..b37a286a --- /dev/null +++ b/src/Numerics/LinearAlgebra/Single/Solvers/Iterative/BiCgStab.cs @@ -0,0 +1,520 @@ +// +// 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. +// + +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; + + /// + /// A Bi-Conjugate Gradient stabilized iterative matrix solver. + /// + /// + /// + /// 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.
+ /// Note that much of the success of the solver depends on the selection of the + /// proper preconditioner. + ///
+ /// + /// The Bi-CGSTAB algorithm was taken from:
+ /// Templates for the solution of linear systems: Building blocks + /// for iterative methods + ///
+ /// 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 + ///
+ /// Url: http://www.netlib.org/templates/Templates.html + ///
+ /// Algorithm is described in Chapter 2, section 2.3.8, page 27 + ///
+ /// + /// The example code below provides an indication of the possible use of the + /// solver. + /// + ///
+ public sealed class BiCgStab : IIterativeSolver + { + /// + /// The status used if there is no status, i.e. the solver hasn't run yet and there is no + /// iterator. + /// + private static readonly ICalculationStatus DefaultStatus = new CalculationIndetermined(); + + /// + /// The preconditioner that will be used. Can be set to , in which case the default + /// pre-conditioner will be used. + /// + private IPreConditioner _preconditioner; + + /// + /// The iterative process controller. + /// + private IIterator _iterator; + + /// + /// Indicates if the user has stopped the solver. + /// + private bool _hasBeenStopped; + + /// + /// Initializes a new instance of the class. + /// + /// + /// When using this constructor the solver will use the with + /// the standard settings and a default preconditioner. + /// + public BiCgStab() : this(null, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// + /// When using this constructor the solver will use a default preconditioner. + /// + /// + /// The main advantages of using a user defined are: + /// + /// It is possible to set the desired convergence limits. + /// + /// It is possible to check the reason for which the solver finished + /// the iterative procedure by calling the property. + /// + /// + /// + /// + /// The that will be used to monitor the iterative process. + public BiCgStab(IIterator iterator) : this(null, iterator) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// When using this constructor the solver will use the with + /// the standard settings. + /// + /// The that will be used to precondition the matrix equation. + public BiCgStab(IPreConditioner preconditioner) : this(preconditioner, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// + /// The main advantages of using a user defined are: + /// + /// It is possible to set the desired convergence limits. + /// + /// It is possible to check the reason for which the solver finished + /// the iterative procedure by calling the property. + /// + /// + /// + /// + /// The that will be used to precondition the matrix equation. + /// The that will be used to monitor the iterative process. + public BiCgStab(IPreConditioner preconditioner, IIterator iterator) + { + _iterator = iterator; + _preconditioner = preconditioner; + } + + /// + /// Sets the that will be used to precondition the iterative process. + /// + /// The preconditioner. + public void SetPreconditioner(IPreConditioner preconditioner) + { + _preconditioner = preconditioner; + } + + /// + /// Sets the that will be used to track the iterative process. + /// + /// The iterator. + public void SetIterator(IIterator iterator) + { + _iterator = iterator; + } + + /// + /// Gets the status of the iteration once the calculation is finished. + /// + public ICalculationStatus IterationResult + { + get + { + return (_iterator != null) ? _iterator.Status : DefaultStatus; + } + } + + /// + /// Stops the solve process. + /// + /// + /// Note that it may take an indetermined amount of time for the solver to actually stop the process. + /// + public void StopSolve() + { + _hasBeenStopped = true; + } + + /// + /// Solves the matrix equation Ax = b, where A is the coefficient matrix, b is the + /// solution vector and x is the unknown vector. + /// + /// The coefficient , A. + /// The solution , b. + /// The result , x. + public Vector Solve(Matrix matrix, Vector vector) + { + if (vector == null) + { + throw new ArgumentNullException(); + } + + Vector result = new DenseVector(matrix.RowCount); + Solve(matrix, vector, result); + return result; + } + + /// + /// Solves the matrix equation Ax = b, where A is the coefficient matrix, b is the + /// solution vector and x is the unknown vector. + /// + /// The coefficient , A. + /// The solution , b. + /// The result , x. + public void Solve(Matrix matrix, Vector input, Vector 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 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 vecP = new DenseVector(residuals.Count); + Vector vecPdash = new DenseVector(residuals.Count); + Vector nu = new DenseVector(residuals.Count); + Vector vecS = new DenseVector(residuals.Count); + Vector vecSdash = new DenseVector(residuals.Count); + Vector 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++; + } + } + + /// + /// Calculates the true residual of the matrix equation Ax = b according to: residual = b - Ax + /// + /// Instance of the A. + /// Residual values in . + /// Instance of the x. + /// Instance of the b. + private static void CalculateTrueResidual(Matrix matrix, Vector residual, Vector x, Vector 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); + } + + /// + /// Determine if calculation should continue + /// + /// Number of iterations passed + /// Result . + /// Source . + /// Residual . + /// true if continue, otherwise false + private bool ShouldContinue(int iterationNumber, Vector result, Vector source, Vector 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); + } + + /// + /// Solves the matrix equation AX = B, where A is the coefficient matrix, B is the + /// solution matrix and X is the unknown matrix. + /// + /// The coefficient , A. + /// The solution , B. + /// The result , X. + public Matrix Solve(Matrix matrix, Matrix 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; + } + + /// + /// Solves the matrix equation AX = B, where A is the coefficient matrix, B is the + /// solution matrix and X is the unknown matrix. + /// + /// The coefficient , A. + /// The solution , B. + /// The result , X + public void Solve(Matrix matrix, Matrix input, Matrix 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); + } + } + } + } +} diff --git a/src/Numerics/LinearAlgebra/Single/Solvers/Iterative/CompositeSolver.cs b/src/Numerics/LinearAlgebra/Single/Solvers/Iterative/CompositeSolver.cs new file mode 100644 index 00000000..b576bf20 --- /dev/null +++ b/src/Numerics/LinearAlgebra/Single/Solvers/Iterative/CompositeSolver.cs @@ -0,0 +1,629 @@ +// +// 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. +// + +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; + + /// + /// A composite matrix solver. The actual solver is made by a sequence of + /// matrix solvers. + /// + /// + /// + /// Solver based on:
+ /// Faster PDE-based simulations using robust composite linear solvers
+ /// S. Bhowmicka, P. Raghavan a,*, L. McInnes b, B. Norris
+ /// Future Generation Computer Systems, Vol 20, 2004, pp 373–387
+ ///
+ /// + /// Note that if an iterator is passed to this solver it will be used for all the sub-solvers. + /// + ///
+ public sealed class CompositeSolver : IIterativeSolver + { + #region Internal class - DoubleComparer + /// + /// An IComparer used to compare double precision floating points. + /// + /// NOTE: The instance of this class is used only in . 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 constructor + public sealed class DoubleComparer : IComparer + { + /// + /// Compares two double values based on the selected comparison method. + /// + /// The first double to compare. + /// The second double to compare. + /// + /// 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. + /// + public int Compare(double x, double y) + { + return x.CompareTo(y, 1); + } + } + #endregion + + /// + /// The default status used if the solver is not running. + /// + private static readonly ICalculationStatus NonRunningStatus = new CalculationIndetermined(); + + /// + /// The default status used if the solver is running. + /// + private static readonly ICalculationStatus RunningStatus = new CalculationRunning(); + +#if SILVERLIGHT + private static readonly Dictionary>> SolverSetups = new Dictionary>>(); +#else + /// + /// The collection of iterative solver setups. Stored based on the + /// ratio between the relative speed and relative accuracy. + /// + private static readonly SortedList>> SolverSetups = new SortedList>>(new DoubleComparer()); +#endif + + #region Solver information loading methods + + /// + /// Loads all the available objects from the MathNet.Numerics assembly. + /// + public static void LoadSolverInformation() + { + LoadSolverInformation(new Type[0]); + } + + /// + /// Loads the available objects from the MathNet.Numerics assembly. + /// + /// The types that should not be loaded. + public static void LoadSolverInformation(Type[] typesToExclude) + { + LoadSolverInformationFromAssembly(Assembly.GetExecutingAssembly(), typesToExclude); + } + + /// + /// Loads the available objects from the assembly specified by the file location. + /// + /// The fully qualified path to the assembly. + public static void LoadSolverInformationFromAssembly(string assemblyLocation) + { + LoadSolverInformationFromAssembly(assemblyLocation, new Type[0]); + } + + /// + /// Loads the available objects from the assembly specified by the file location. + /// + /// The fully qualified path to the assembly. + /// The types that should not be loaded. + 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); + + // 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); + } + + /// + /// Loads the available objects from the assembly specified by the assembly name. + /// + /// The of the assembly that should be searched for setup objects. + public static void LoadSolverInformationFromAssembly(AssemblyName assemblyName) + { + LoadSolverInformationFromAssembly(assemblyName, new Type[0]); + } + + /// + /// Loads the available objects from the assembly specified by the assembly name. + /// + /// The of the assembly that should be searched for setup objects. + /// The types that should not be loaded. + 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); + } + + /// + /// Loads the available objects from the assembly specified by the type. + /// + /// The type in the assembly which should be searched for setup objects. + public static void LoadSolverInformationFromAssembly(Type typeInAssembly) + { + LoadSolverInformationFromAssembly(typeInAssembly, new Type[0]); + } + + /// + /// Loads the available objects from the assembly specified by the type. + /// + /// The type in the assembly which should be searched for setup objects. + /// The types that should not be loaded. + public static void LoadSolverInformationFromAssembly(Type typeInAssembly, params Type[] typesToExclude) + { + if (typeInAssembly == null) + { + throw new ArgumentNullException("typeInAssembly"); + } + + LoadSolverInformationFromAssembly(typeInAssembly.Assembly, typesToExclude); + } + + /// + /// Loads the available objects from the specified assembly. + /// + /// The assembly which will be searched for setup objects. + public static void LoadSolverInformationFromAssembly(Assembly assembly) + { + LoadSolverInformationFromAssembly(assembly, new Type[0]); + } + + /// + /// Loads the available objects from the specified assembly. + /// + /// The assembly which will be searched for setup objects. + /// The types that should not be loaded. + 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(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(); + 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).IsAssignableFrom(match))) + { + continue; + } + + // See if we actually want this type of iterative solver + IIterativeSolverSetup 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)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>()); + } + + var list = SolverSetups[ratio]; + list.Add(setup); + } + } + + #endregion + + /// + /// The collection of solvers that will be used to + /// + private readonly List> _solvers = new List>(); + + /// + /// The status of the calculation. + /// + private ICalculationStatus _status = NonRunningStatus; + + /// + /// The iterator that is used to control the iteration process. + /// + private IIterator _iterator; + + /// + /// A flag indicating if the solver has been stopped or not. + /// + private bool _hasBeenStopped; + + /// + /// The solver that is currently running. Reference is used to be able to stop the + /// solver if the user cancels the solve process. + /// + private IIterativeSolver _currentSolver; + + /// + /// Initializes a new instance of the class with the default iterator. + /// + public CompositeSolver() : this(null) + { + } + + /// + /// Initializes a new instance of the class with the specified iterator. + /// + /// The iterator that will be used to control the iteration process. + public CompositeSolver(IIterator iterator) + { + _iterator = iterator; + } + + /// + /// Sets the that will be used to track the iterative process. + /// + /// The iterator. + public void SetIterator(IIterator iterator) + { + _iterator = iterator; + } + + /// + /// Gets the status of the iteration once the calculation is finished. + /// + public ICalculationStatus IterationResult + { + get + { + return _status; + } + } + + /// + /// Stops the solve process. + /// + /// + /// Note that it may take an indetermined amount of time for the solver to actually stop the process. + /// + public void StopSolve() + { + _hasBeenStopped = true; + if (_currentSolver != null) + { + _currentSolver.StopSolve(); + } + } + + /// + /// Solves the matrix equation Ax = b, where A is the coefficient matrix, b is the + /// solution vector and x is the unknown vector. + /// + /// The coefficient matrix, A. + /// The solution vector, b. + /// The result vector, x. + public Vector Solve(Matrix matrix, Vector vector) + { + if (vector == null) + { + throw new ArgumentNullException(); + } + + Vector result = new DenseVector(matrix.RowCount); + Solve(matrix, vector, result); + return result; + } + + /// + /// Solves the matrix equation Ax = b, where A is the coefficient matrix, b is the + /// solution vector and x is the unknown vector. + /// + /// The coefficient matrix, A. + /// The solution vector, b + /// The result vector, x + public void Solve(Matrix matrix, Vector input, Vector 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; + } + + /// + /// Load solvers + /// + 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()); + } + } + + /// + /// Solves the matrix equation AX = B, where A is the coefficient matrix, B is the + /// solution matrix and X is the unknown matrix. + /// + /// The coefficient matrix, A. + /// The solution matrix, B. + /// The result matrix, X. + public Matrix Solve(Matrix matrix, Matrix 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; + } + + /// + /// Solves the matrix equation AX = B, where A is the coefficient matrix, B is the + /// solution matrix and X is the unknown matrix. + /// + /// The coefficient matrix, A. + /// The solution matrix, B. + /// The result matrix, X + public void Solve(Matrix matrix, Matrix input, Matrix 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); + } + } + } + } +} diff --git a/src/Numerics/LinearAlgebra/Single/Solvers/Iterative/GpBiCg.cs b/src/Numerics/LinearAlgebra/Single/Solvers/Iterative/GpBiCg.cs new file mode 100644 index 00000000..10de0666 --- /dev/null +++ b/src/Numerics/LinearAlgebra/Single/Solvers/Iterative/GpBiCg.cs @@ -0,0 +1,624 @@ +// +// 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. +// + +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; + + /// + /// A Generalized Product Bi-Conjugate Gradient iterative matrix solver. + /// + /// + /// + /// 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.
+ /// Note that much of the success of the solver depends on the selection of the + /// proper preconditioner. + ///
+ /// + /// The GPBiCG algorithm was taken from:
+ /// GPBiCG(m,l): A hybrid of BiCGSTAB and GPBiCG methods with + /// efficiency and robustness + ///
+ /// S. Fujino + ///
+ /// Applied Numerical Mathematics, Volume 41, 2002, pp 107 - 117 + ///
+ ///
+ /// + /// The example code below provides an indication of the possible use of the + /// solver. + /// + ///
+ public sealed class GpBiCg : IIterativeSolver + { + /// + /// The status used if there is no status, i.e. the solver hasn't run yet and there is no + /// iterator. + /// + private static readonly ICalculationStatus DefaultStatus = new CalculationIndetermined(); + + /// + /// The preconditioner that will be used. Can be set to null, in which case the default + /// pre-conditioner will be used. + /// + private IPreConditioner _preconditioner; + + /// + /// The iterative process controller. + /// + private IIterator _iterator; + + /// + /// Indicates the number of BiCGStab steps should be taken + /// before switching. + /// + private int _numberOfBiCgStabSteps = 1; + + /// + /// Indicates the number of GPBiCG steps should be taken + /// before switching. + /// + private int _numberOfGpbiCgSteps = 4; + + /// + /// Indicates if the user has stopped the solver. + /// + private bool _hasBeenStopped; + + /// + /// Initializes a new instance of the class. + /// + /// + /// When using this constructor the solver will use the with + /// the standard settings and a default preconditioner. + /// + public GpBiCg() : this(null, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// + /// When using this constructor the solver will use a default preconditioner. + /// + /// + /// The main advantages of using a user defined are: + /// + /// It is possible to set the desired convergence limits. + /// + /// It is possible to check the reason for which the solver finished + /// the iterative procedure by calling the property. + /// + /// + /// + /// + /// The that will be used to monitor the iterative process. + public GpBiCg(IIterator iterator) : this(null, iterator) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// When using this constructor the solver will use the with + /// the standard settings. + /// + /// The that will be used to precondition the matrix equation. + public GpBiCg(IPreConditioner preconditioner) : this(preconditioner, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// + /// The main advantages of using a user defined are: + /// + /// It is possible to set the desired convergence limits. + /// + /// It is possible to check the reason for which the solver finished + /// the iterative procedure by calling the property. + /// + /// + /// + /// + /// The that will be used to precondition the matrix equation. + /// The that will be used to monitor the iterative process. + public GpBiCg(IPreConditioner preconditioner, IIterator iterator) + { + _iterator = iterator; + _preconditioner = preconditioner; + } + + /// + /// Gets or sets the number of steps taken with the BiCgStab algorithm + /// before switching over to the GPBiCG algorithm. + /// + public int NumberOfBiCgStabSteps + { + get + { + return _numberOfBiCgStabSteps; + } + + set + { + if (value < 0) + { + throw new ArgumentOutOfRangeException("value"); + } + + _numberOfBiCgStabSteps = value; + } + } + + /// + /// Gets or sets the number of steps taken with the GPBiCG algorithm + /// before switching over to the BiCgStab algorithm. + /// + public int NumberOfGpBiCgSteps + { + get + { + return _numberOfGpbiCgSteps; + } + + set + { + if (value < 0) + { + throw new ArgumentOutOfRangeException("value"); + } + + _numberOfGpbiCgSteps = value; + } + } + + /// + /// Sets the that will be used to precondition the iterative process. + /// + /// The preconditioner. + public void SetPreconditioner(IPreConditioner preconditioner) + { + _preconditioner = preconditioner; + } + + /// + /// Sets the that will be used to track the iterative process. + /// + /// The iterator. + public void SetIterator(IIterator iterator) + { + _iterator = iterator; + } + + /// + /// Gets the status of the iteration once the calculation is finished. + /// + public ICalculationStatus IterationResult + { + get + { + return (_iterator != null) ? _iterator.Status : DefaultStatus; + } + } + + /// + /// Stops the solve process. + /// + /// + /// Note that it may take an indetermined amount of time for the solver to actually + /// stop the process. + /// + public void StopSolve() + { + _hasBeenStopped = true; + } + + /// + /// Solves the matrix equation Ax = b, where A is the coefficient matrix, b is the + /// solution vector and x is the unknown vector. + /// + /// The coefficient matrix, A. + /// The solution vector, b. + /// The result vector, x. + public Vector Solve(Matrix matrix, Vector vector) + { + if (vector == null) + { + throw new ArgumentNullException(); + } + + Vector result = new DenseVector(matrix.RowCount); + Solve(matrix, vector, result); + return result; + } + + /// + /// Solves the matrix equation Ax = b, where A is the coefficient matrix, b is the + /// solution vector and x is the unknown vector. + /// + /// The coefficient matrix, A. + /// The solution vector, b + /// The result vector, x + public void Solve(Matrix matrix, Vector input, Vector 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 xtemp = new DenseVector(input.Count); + + // r_0 = b - Ax_0 + // This is basically a SAXPY so it could be made a lot faster + Vector 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 rdash = new DenseVector(residuals); + + // t_-1 = 0 + Vector t = new DenseVector(residuals.Count); + Vector t0 = new DenseVector(residuals.Count); + + // w_-1 = 0 + Vector w = new DenseVector(residuals.Count); + + // Define the remaining temporary vectors + Vector c = new DenseVector(residuals.Count); + Vector p = new DenseVector(residuals.Count); + Vector s = new DenseVector(residuals.Count); + Vector u = new DenseVector(residuals.Count); + Vector y = new DenseVector(residuals.Count); + Vector z = new DenseVector(residuals.Count); + + Vector 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++; + } + } + + /// + /// Calculates the true residual of the matrix equation Ax = b according to: residual = b - Ax + /// + /// Instance of the A. + /// Residual values in . + /// Instance of the x. + /// Instance of the b. + private static void CalculateTrueResidual(Matrix matrix, Vector residual, Vector x, Vector b) + { + // -Ax = residual + matrix.Multiply(x, residual); + residual.Multiply(-1, residual); + + // residual + b + residual.Add(b, residual); + } + + /// + /// Determine if calculation should continue + /// + /// Number of iterations passed + /// Result . + /// Source . + /// Residual . + /// true if continue, otherwise false + private bool ShouldContinue(int iterationNumber, Vector result, Vector source, Vector 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); + } + + /// + /// Decide if to do steps with BiCgStab + /// + /// Number of iteration + /// true if yes, otherwise false + 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); + } + + /// + /// Solves the matrix equation AX = B, where A is the coefficient matrix, B is the + /// solution matrix and X is the unknown matrix. + /// + /// The coefficient matrix, A. + /// The solution matrix, B. + /// The result matrix, X. + public Matrix Solve(Matrix matrix, Matrix 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; + } + + /// + /// Solves the matrix equation AX = B, where A is the coefficient matrix, B is the + /// solution matrix and X is the unknown matrix. + /// + /// The coefficient matrix, A. + /// The solution matrix, B. + /// The result matrix, X + public void Solve(Matrix matrix, Matrix input, Matrix 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); + } + } + } + } +} diff --git a/src/Numerics/LinearAlgebra/Single/Solvers/Iterative/MlkBiCgStab.cs b/src/Numerics/LinearAlgebra/Single/Solvers/Iterative/MlkBiCgStab.cs new file mode 100644 index 00000000..0e3c452a --- /dev/null +++ b/src/Numerics/LinearAlgebra/Single/Solvers/Iterative/MlkBiCgStab.cs @@ -0,0 +1,779 @@ +// +// 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. +// + +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; + + /// + /// A Multiple-Lanczos Bi-Conjugate Gradient stabilized iterative matrix solver. + /// + /// + /// + /// The Multiple-Lanczos Bi-Conjugate Gradient stabilized (ML(k)-BiCGStab) solver is an 'improvement' + /// of the standard BiCgStab solver. + /// + /// + /// The algorithm was taken from:
+ /// ML(k)BiCGSTAB: A BiCGSTAB variant based on multiple Lanczos starting vectors + ///
+ /// Man-chung Yeung and Tony F. Chan + ///
+ /// SIAM Journal of Scientific Computing + ///
+ /// Volume 21, Number 4, pp. 1263 - 1290 + ///
+ /// + /// The example code below provides an indication of the possible use of the + /// solver. + /// + ///
+ public sealed class MlkBiCgStab : IIterativeSolver + { + /// + /// The default number of starting vectors. + /// + private const int DefaultNumberOfStartingVectors = 50; + + /// + /// The status used if there is no status, i.e. the solver hasn't run yet and there is no + /// iterator. + /// + private static readonly ICalculationStatus DefaultStatus = new CalculationIndetermined(); + + /// + /// The preconditioner that will be used. Can be set to , in which case the default + /// pre-conditioner will be used. + /// + private IPreConditioner _preconditioner; + + /// + /// The iterative process controller. + /// + private IIterator _iterator; + + /// + /// The collection of starting vectors which are used as the basis for the Krylov sub-space. + /// + private IList> _startingVectors; + + /// + /// The number of starting vectors used by the algorithm + /// + private int _numberOfStartingVectors = DefaultNumberOfStartingVectors; + + /// + /// Indicates if the user has stopped the solver. + /// + private bool _hasBeenStopped; + + /// + /// Initializes a new instance of the class. + /// + /// + /// When using this constructor the solver will use the with + /// the standard settings and a default preconditioner. + /// + public MlkBiCgStab() : this(null, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// + /// When using this constructor the solver will use a default preconditioner. + /// + /// + /// The main advantages of using a user defined are: + /// + /// It is possible to set the desired convergence limits. + /// + /// It is possible to check the reason for which the solver finished + /// the iterative procedure by calling the property. + /// + /// + /// + /// + /// The that will be used to monitor the iterative process. + public MlkBiCgStab(IIterator iterator) : this(null, iterator) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// When using this constructor the solver will use the with + /// the standard settings. + /// + /// The that will be used to precondition the matrix equation. + public MlkBiCgStab(IPreConditioner preconditioner) : this(preconditioner, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// + /// The main advantages of using a user defined are: + /// + /// It is possible to set the desired convergence limits. + /// + /// It is possible to check the reason for which the solver finished + /// the iterative procedure by calling the property. + /// + /// + /// + /// + /// The that will be used to precondition the matrix equation. + /// The that will be used to monitor the iterative process. + public MlkBiCgStab(IPreConditioner preconditioner, IIterator iterator) + { + _iterator = iterator; + _preconditioner = preconditioner; + } + + /// + /// Gets or sets the number of starting vectors. + /// + /// + /// Must be larger than 1 and smaller than the number of variables in the matrix that + /// for which this solver will be used. + /// + public int NumberOfStartingVectors + { + [DebuggerStepThrough] + get + { + return _numberOfStartingVectors; + } + + [DebuggerStepThrough] + set + { + if (value <= 1) + { + throw new ArgumentOutOfRangeException("value"); + } + + _numberOfStartingVectors = value; + } + } + + /// + /// Resets the number of starting vectors to the default value. + /// + public void ResetNumberOfStartingVectors() + { + _numberOfStartingVectors = DefaultNumberOfStartingVectors; + } + + /// + /// Sets the that will be used to precondition the iterative process. + /// + /// The preconditioner. + public void SetPreconditioner(IPreConditioner preconditioner) + { + _preconditioner = preconditioner; + } + + /// + /// Sets the that will be used to track the iterative process. + /// + /// The iterator. + public void SetIterator(IIterator iterator) + { + _iterator = iterator; + } + + /// + /// Gets or sets a series of orthonormal vectors which will be used as basis for the + /// Krylov sub-space. + /// + public IList> StartingVectors + { + [DebuggerStepThrough] + get + { + return _startingVectors; + } + + [DebuggerStepThrough] + set + { + if ((value == null) || (value.Count == 0)) + { + _startingVectors = null; + } + else + { + _startingVectors = value; + } + } + } + + /// + /// Gets the status of the iteration once the calculation is finished. + /// + public ICalculationStatus IterationResult + { + [DebuggerStepThrough] + get + { + return (_iterator != null) ? _iterator.Status : DefaultStatus; + } + } + + /// + /// Stops the solve process. + /// + /// + /// Note that it may take an indetermined amount of time for the solver to actually stop the process. + /// + public void StopSolve() + { + _hasBeenStopped = true; + } + + /// + /// Solves the matrix equation Ax = b, where A is the coefficient matrix, b is the + /// solution vector and x is the unknown vector. + /// + /// The coefficient matrix, A. + /// The solution vector, b. + /// The result vector, x. + public Vector Solve(Matrix matrix, Vector vector) + { + if (vector == null) + { + throw new ArgumentNullException(); + } + + Vector result = new DenseVector(matrix.RowCount); + Solve(matrix, vector, result); + return result; + } + + /// + /// Solves the matrix equation Ax = b, where A is the coefficient matrix, b is the + /// solution vector and x is the unknown vector. + /// + /// The coefficient matrix, A. + /// The solution vector, b + /// The result vector, x + public void Solve(Matrix matrix, Vector input, Vector 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 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 residuals = new DenseVector(matrix.RowCount); + CalculateTrueResidual(matrix, residuals, xtemp, input); + + // Define the temporary scalars + var c = new float[k]; + + // Define the temporary vectors + Vector gtemp = new DenseVector(residuals.Count); + + Vector u = new DenseVector(residuals.Count); + Vector utemp = new DenseVector(residuals.Count); + Vector temp = new DenseVector(residuals.Count); + Vector temp1 = new DenseVector(residuals.Count); + + Vector zd = new DenseVector(residuals.Count); + Vector zg = new DenseVector(residuals.Count); + Vector 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); + } + + /// + /// Gets the number of starting vectors to create + /// + /// Maximum number + /// Number of variables + /// Number of starting vectors to create + 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)); + } + + /// + /// Returns an array of starting vectors. + /// + /// The maximum number of starting vectors that should be created. + /// The number of variables. + /// + /// An array with starting vectors. The array will never be larger than the + /// but it may be smaller if + /// the is smaller than + /// the . + /// + private static IList> 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 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>(); + 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; + } + + /// + /// Create random vecrors array + /// + /// Number of vectors + /// Size of each vector + /// Array of random vectors + private static Vector[] CreateVectorArray(int arraySize, int vectorSize) + { + var result = new Vector[arraySize]; + for (var i = 0; i < result.Length; i++) + { + result[i] = new DenseVector(vectorSize); + } + + return result; + } + + /// + /// Calculates the true residual of the matrix equation Ax = b according to: residual = b - Ax + /// + /// Source A. + /// Residual data. + /// x data. + /// b data. + private static void CalculateTrueResidual(Matrix matrix, Vector residual, Vector x, Vector b) + { + // -Ax = residual + matrix.Multiply(x, residual); + residual.Multiply(-1, residual); + + // residual + b + residual.Add(b, residual); + } + + /// + /// Determine if calculation should continue + /// + /// Number of iterations passed + /// Result . + /// Source . + /// Residual . + /// true if continue, otherwise false + private bool ShouldContinue(int iterationNumber, Vector result, Vector source, Vector 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); + } + + /// + /// Solves the matrix equation AX = B, where A is the coefficient matrix, B is the + /// solution matrix and X is the unknown matrix. + /// + /// The coefficient matrix, A. + /// The solution matrix, B. + /// The result matrix, X. + public Matrix Solve(Matrix matrix, Matrix 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; + } + + /// + /// Solves the matrix equation AX = B, where A is the coefficient matrix, B is the + /// solution matrix and X is the unknown matrix. + /// + /// The coefficient matrix, A. + /// The solution matrix, B. + /// The result matrix, X + public void Solve(Matrix matrix, Matrix input, Matrix 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); + } + } + } + } +} diff --git a/src/Numerics/LinearAlgebra/Single/Solvers/Iterative/TFQMR.cs b/src/Numerics/LinearAlgebra/Single/Solvers/Iterative/TFQMR.cs new file mode 100644 index 00000000..0f388ea0 --- /dev/null +++ b/src/Numerics/LinearAlgebra/Single/Solvers/Iterative/TFQMR.cs @@ -0,0 +1,528 @@ +// +// 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. +// + +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; + + /// + /// A Transpose Free Quasi-Minimal Residual (TFQMR) iterative matrix solver. + /// + /// + /// + /// The TFQMR algorithm was taken from:
+ /// Iterative methods for sparse linear systems. + ///
+ /// Yousef Saad + ///
+ /// Algorithm is described in Chapter 7, section 7.4.3, page 219 + ///
+ /// + /// The example code below provides an indication of the possible use of the + /// solver. + /// + ///
+ public sealed class TFQMR : IIterativeSolver + { + /// + /// The status used if there is no status, i.e. the solver hasn't run yet and there is no + /// iterator. + /// + private static readonly ICalculationStatus DefaultStatus = new CalculationIndetermined(); + + /// + /// The preconditioner that will be used. Can be set to , in which case the default + /// pre-conditioner will be used. + /// + private IPreConditioner _preconditioner; + + /// + /// The iterative process controller. + /// + private IIterator _iterator; + + /// + /// Indicates if the user has stopped the solver. + /// + private bool _hasBeenStopped; + + /// + /// Initializes a new instance of the class. + /// + /// + /// When using this constructor the solver will use the with + /// the standard settings and a default preconditioner. + /// + public TFQMR() : this(null, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// + /// When using this constructor the solver will use a default preconditioner. + /// + /// + /// The main advantages of using a user defined are: + /// + /// It is possible to set the desired convergence limits. + /// + /// It is possible to check the reason for which the solver finished + /// the iterative procedure by calling the property. + /// + /// + /// + /// + /// The that will be used to monitor the iterative process. + public TFQMR(IIterator iterator) : this(null, iterator) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// When using this constructor the solver will use the with + /// the standard settings. + /// + /// The that will be used to precondition the matrix equation. + public TFQMR(IPreConditioner preconditioner) : this(preconditioner, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// + /// The main advantages of using a user defined are: + /// + /// It is possible to set the desired convergence limits. + /// + /// It is possible to check the reason for which the solver finished + /// the iterative procedure by calling the property. + /// + /// + /// + /// + /// The that will be used to precondition the matrix equation. + /// The that will be used to monitor the iterative process. + public TFQMR(IPreConditioner preconditioner, IIterator iterator) + { + _iterator = iterator; + _preconditioner = preconditioner; + } + + /// + /// Sets the that will be used to precondition the iterative process. + /// + /// The preconditioner. + public void SetPreconditioner(IPreConditioner preconditioner) + { + _preconditioner = preconditioner; + } + + /// + /// Sets the that will be used to track the iterative process. + /// + /// The iterator. + public void SetIterator(IIterator iterator) + { + _iterator = iterator; + } + + /// + /// Gets the status of the iteration once the calculation is finished. + /// + public ICalculationStatus IterationResult + { + get + { + return (_iterator != null) ? _iterator.Status : DefaultStatus; + } + } + + /// + /// Stops the solve process. + /// + /// + /// Note that it may take an indetermined amount of time for the solver to actually stop the process. + /// + public void StopSolve() + { + _hasBeenStopped = true; + } + + /// + /// Solves the matrix equation Ax = b, where A is the coefficient matrix, b is the + /// solution vector and x is the unknown vector. + /// + /// The coefficient matrix, A. + /// The solution vector, b. + /// The result vector, x. + public Vector Solve(Matrix matrix, Vector vector) + { + if (vector == null) + { + throw new ArgumentNullException(); + } + + Vector result = new DenseVector(matrix.RowCount); + Solve(matrix, vector, result); + return result; + } + + /// + /// Solves the matrix equation Ax = b, where A is the coefficient matrix, b is the + /// solution vector and x is the unknown vector. + /// + /// The coefficient matrix, A. + /// The solution vector, b + /// The result vector, x + public void Solve(Matrix matrix, Vector input, Vector 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++; + } + } + + /// + /// Calculates the true residual of the matrix equation Ax = b according to: residual = b - Ax + /// + /// Instance of the A. + /// Residual values in . + /// Instance of the x. + /// Instance of the b. + private static void CalculateTrueResidual(Matrix matrix, Vector residual, Vector x, Vector b) + { + // -Ax = residual + matrix.Multiply(x, residual); + residual.Multiply(-1, residual); + + // residual + b + residual.Add(b, residual); + } + + /// + /// Determine if calculation should continue + /// + /// Number of iterations passed + /// Result . + /// Source . + /// Residual . + /// true if continue, otherwise false + private bool ShouldContinue(int iterationNumber, Vector result, Vector source, Vector 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); + } + + /// + /// Is even? + /// + /// Number to check + /// true if even, otherwise false + private static bool IsEven(int number) + { + return number % 2 == 0; + } + + /// + /// Solves the matrix equation AX = B, where A is the coefficient matrix, B is the + /// solution matrix and X is the unknown matrix. + /// + /// The coefficient matrix, A. + /// The solution matrix, B. + /// The result matrix, X. + public Matrix Solve(Matrix matrix, Matrix 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; + } + + /// + /// Solves the matrix equation AX = B, where A is the coefficient matrix, B is the + /// solution matrix and X is the unknown matrix. + /// + /// The coefficient matrix, A. + /// The solution matrix, B. + /// The result matrix, X + public void Solve(Matrix matrix, Matrix input, Matrix 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); + } + } + } + } +} diff --git a/src/Numerics/LinearAlgebra/Single/Solvers/Iterator.cs b/src/Numerics/LinearAlgebra/Single/Solvers/Iterator.cs new file mode 100644 index 00000000..a9667cc6 --- /dev/null +++ b/src/Numerics/LinearAlgebra/Single/Solvers/Iterator.cs @@ -0,0 +1,328 @@ +// +// 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. +// + +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; + + /// + /// An iterator that is used to check if an iterative calculation should continue or stop. + /// + public sealed class Iterator : IIterator + { + /// + /// The default status for the iterator. + /// + private static readonly ICalculationStatus DefaultStatus = new CalculationIndetermined(); + + /// + /// Creates a default iterator with all the objects. + /// + /// A new object. + public static IIterator CreateDefault() + { + var iterator = new Iterator(); + iterator.Add(new FailureStopCriterium()); + iterator.Add(new DivergenceStopCriterium()); + iterator.Add(new IterationCountStopCriterium()); + iterator.Add(new ResidualStopCriterium()); + + return iterator; + } + + /// + /// The collection that holds all the stop criteria and the flag indicating if they should be added + /// to the child iterators. + /// + private readonly Dictionary> _stopCriterias = new Dictionary>(); + + /// + /// The status of the iterator. + /// + private ICalculationStatus _status = DefaultStatus; + + /// + /// Indicates if the iteration was cancelled. + /// + private bool _wasIterationCancelled; + + /// + /// Initializes a new instance of the class. + /// + public Iterator() : this(null) + { + } + + /// + /// Initializes a new instance of the class with the specified stop criteria. + /// + /// + /// 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. + /// + /// Thrown if contains multiple stop criteria of the same type. + public Iterator(IEnumerable> stopCriteria) + { + // Add the stop criteria + if (stopCriteria == null) + { + return; + } + + foreach (var stopCriterium in stopCriteria.Where(stopCriterium => stopCriterium != null)) + { + Add(stopCriterium); + } + } + + /// + /// Adds an to the internal collection of stop-criteria. Only a + /// single stop criterium of each type can be stored. + /// + /// The stop criterium to add. + /// Thrown if is . + /// + /// Thrown if is of the same type as an already + /// stored criterium. + /// + public void Add(IIterationStopCriterium 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); + } + + /// + /// Removes the from the internal collection. + /// + /// The stop criterium that must be removed. + public void Remove(IIterationStopCriterium stopCriterium) + { + if (stopCriterium == null) + { + throw new ArgumentNullException("stopCriterium"); + } + + if (!_stopCriterias.ContainsKey(stopCriterium.GetType())) + { + return; + } + + // Remove from the collection + _stopCriterias.Remove(stopCriterium.GetType()); + } + + /// + /// Indicates if the specific stop criterium is stored by the . + /// + /// The stop criterium. + /// true if the contains the stop criterium; otherwise false. + public bool Contains(IIterationStopCriterium stopCriterium) + { + return stopCriterium != null && _stopCriterias.ContainsKey(stopCriterium.GetType()); + } + + /// + /// Gets the number of stored stop criteria. + /// + /// Used for testing only. + internal int NumberOfCriteria + { + get + { + return _stopCriterias.Count; + } + } + + /// + /// Gets an IEnumerator that enumerates over all the stored stop criteria. + /// + /// Used for testing only. + internal IEnumerator> StoredStopCriteria + { + get + { + return _stopCriterias.Select(criterium => criterium.Value).GetEnumerator(); + } + } + + /// + /// Indicates to the iterator that the iterative process has been cancelled. + /// + /// + /// Does not reset the stop-criteria. + /// + public void IterationCancelled() + { + _wasIterationCancelled = true; + _status = new CalculationCancelled(); + } + + /// + /// Determines the status of the iterative calculation based on the stop criteria stored + /// by the current . Result is set into Status field. + /// + /// The number of iterations that have passed so far. + /// The vector containing the current solution values. + /// The right hand side vector. + /// The vector containing the current residual vectors. + /// + /// 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. + /// + public void DetermineStatus(int iterationNumber, Vector solutionVector, Vector sourceVector, Vector 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(); + } + } + + /// + /// Gets the current calculation status. + /// + public ICalculationStatus Status + { + get + { + return _status; + } + } + + /// + /// Resets the to the pre-calculation state. + /// + 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(); + } + } + + /// + /// Creates a deep clone of the current iterator. + /// + /// The deep clone of the current iterator. + public IIterator Clone() + { + var stopCriteria = _stopCriterias.Select(pair => pair.Value).Select(stopCriterium => (IIterationStopCriterium)stopCriterium.Clone()).ToList(); + return new Iterator(stopCriteria); + } + + #if !SILVERLIGHT + /// + /// Creates a deep clone of the current iterator. + /// + /// The deep clone of the current iterator. + object ICloneable.Clone() + { + return Clone(); + } + #endif + } +} diff --git a/src/Numerics/LinearAlgebra/Single/Solvers/Preconditioners/Diagonal.cs b/src/Numerics/LinearAlgebra/Single/Solvers/Preconditioners/Diagonal.cs new file mode 100644 index 00000000..65a3d750 --- /dev/null +++ b/src/Numerics/LinearAlgebra/Single/Solvers/Preconditioners/Diagonal.cs @@ -0,0 +1,150 @@ +// +// 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. +// + +namespace MathNet.Numerics.LinearAlgebra.Single.Solvers.Preconditioners +{ + using System; + using Generic; + using Generic.Solvers.Preconditioners; + using Properties; + + /// + /// A diagonal preconditioner. The preconditioner uses the inverse + /// of the matrix diagonal as preconditioning values. + /// + public sealed class Diagonal : IPreConditioner + { + /// + /// The inverse of the matrix diagonal. + /// + private float[] _inverseDiagonals; + + /// + /// Returns the decomposed matrix diagonal. + /// + /// The matrix diagonal. + 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; + } + + /// + /// Initializes the preconditioner and loads the internal data structures. + /// + /// + /// The upon which this preconditioner is based. + /// If is . + /// If is not a square matrix. + public void Initialize(Matrix 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]; + } + } + + /// + /// Approximates the solution to the matrix equation Ax = b. + /// + /// The right hand side vector. + /// The left hand side vector. + public Vector Approximate(Vector 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 result = new DenseVector(rhs.Count); + Approximate(rhs, result); + return result; + } + + /// + /// Approximates the solution to the matrix equation Ax = b. + /// + /// The right hand side vector. + /// The left hand side vector. Also known as the result vector. + public void Approximate(Vector rhs, Vector 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]; + } + } + } +} diff --git a/src/Numerics/LinearAlgebra/Single/Solvers/Preconditioners/Ilutp.cs b/src/Numerics/LinearAlgebra/Single/Solvers/Preconditioners/Ilutp.cs new file mode 100644 index 00000000..d86f48d5 --- /dev/null +++ b/src/Numerics/LinearAlgebra/Single/Solvers/Preconditioners/Ilutp.cs @@ -0,0 +1,729 @@ +// +// 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. +// + +namespace MathNet.Numerics.LinearAlgebra.Single.Solvers.Preconditioners +{ + using System; + using System.Collections.Generic; + using Generic; + using Generic.Solvers.Preconditioners; + using Properties; + + /// + /// 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. + /// + /// + /// The ILUTP-Mem algorithm was taken from:
+ /// ILUTP_Mem: a Space-Efficient Incomplete LU Preconditioner + ///
+ /// Tzu-Yi Chen, Department of Mathematics and Computer Science,
+ /// Pomona College, Claremont CA 91711, USA
+ /// Published in:
+ /// Lecture Notes in Computer Science
+ /// Volume 3046 / 2004
+ /// pp. 20 - 28
+ /// Algorithm is described in Section 2, page 22 + ///
+ public sealed class Ilutp : IPreConditioner + { + /// + /// The default fill level. + /// + public const double DefaultFillLevel = 200.0; + + /// + /// The default drop tolerance. + /// + public const double DefaultDropTolerance = 0.0001; + + /// + /// The decomposed upper triangular matrix. + /// + private SparseMatrix _upper; + + /// + /// The decomposed lower triangular matrix. + /// + private SparseMatrix _lower; + + /// + /// The array containing the pivot values. + /// + private int[] _pivots; + + /// + /// The fill level. + /// + private double _fillLevel = DefaultFillLevel; + + /// + /// The drop tolerance. + /// + private double _dropTolerance = DefaultDropTolerance; + + /// + /// The pivot tolerance. + /// + private double _pivotTolerance; + + /// + /// Initializes a new instance of the class with the default settings. + /// + public Ilutp() + { + } + + /// + /// Initializes a new instance of the class with the specified settings. + /// + /// + /// 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. + /// + /// + /// 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. + /// + /// + /// The pivot tolerance which indicates at what level pivoting will take place. A + /// value of 0.0 means that no pivoting will take place. + /// + 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; + } + + /// + /// 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. + /// + /// + /// + /// 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. + /// + /// + /// Note that any changes to the FillLevel after creating the preconditioner + /// will invalidate the created preconditioner and will require a re-initialization of + /// the preconditioner. + /// + /// + /// Thrown if a negative value is provided. + public double FillLevel + { + get + { + return _fillLevel; + } + + set + { + if (value < 0) + { + throw new ArgumentOutOfRangeException("Value"); + } + + _fillLevel = value; + } + } + + /// + /// 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. + /// + /// + /// + /// 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. + /// + /// + /// Note that any changes to the DropTolerance after creating the preconditioner + /// will invalidate the created preconditioner and will require a re-initialization of + /// the preconditioner. + /// + /// + /// Thrown if a negative value is provided. + public double DropTolerance + { + get + { + return _dropTolerance; + } + + set + { + if (value < 0) + { + throw new ArgumentOutOfRangeException("Value"); + } + + _dropTolerance = value; + } + } + + /// + /// 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. + /// + /// + /// + /// 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 row(i,j) > row(i,i) / PivotTolerance for + /// any j that is not equal to i. + /// + /// + /// Note that any changes to the PivotTolerance after creating the preconditioner + /// will invalidate the created preconditioner and will require a re-initialization of + /// the preconditioner. + /// + /// + /// Thrown if a negative value is provided. + public double PivotTolerance + { + get + { + return _pivotTolerance; + } + + set + { + if (value < 0) + { + throw new ArgumentOutOfRangeException("Value"); + } + + _pivotTolerance = value; + } + } + + /// + /// Returns the upper triagonal matrix that was created during the LU decomposition. + /// + /// + /// This method is used for debugging purposes only and should normally not be used. + /// + /// A new matrix containing the upper triagonal elements. + internal Matrix UpperTriangle() + { + return _upper.Clone(); + } + + /// + /// Returns the lower triagonal matrix that was created during the LU decomposition. + /// + /// + /// This method is used for debugging purposes only and should normally not be used. + /// + /// A new matrix containing the lower triagonal elements. + internal Matrix LowerTriangle() + { + return _lower.Clone(); + } + + /// + /// 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. + /// + /// + /// This method is used for debugging purposes only and should normally not be used. + /// + /// The pivot array. + internal int[] Pivots() + { + var result = new int[_pivots.Length]; + for (var i = 0; i < _pivots.Length; i++) + { + result[i] = _pivots[i]; + } + + return result; + } + + /// + /// Initializes the preconditioner and loads the internal data structures. + /// + /// + /// The 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. + /// + /// If is . + /// If is not a square matrix. + public void Initialize(Matrix 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 workVector = new DenseVector(sparseMatrix.RowCount); + Vector 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; + } + } + + /// + /// Pivot elements in the according to internal pivot array + /// + /// Row to pivot in + private void PivotRow(Vector row) + { + var knownPivots = new Dictionary(); + + // 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; + } + } + } + + /// + /// Was pivoting already performed + /// + /// Pivots already done + /// Current item to pivot + /// true if performed, otherwise false + private bool PivotMapFound(Dictionary 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; + } + + /// + /// Swap columns in the + /// + /// Source . + /// First column index to swap + /// Second column index to swap + private static void SwapColumns(Matrix 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; + } + } + + /// + /// Sort vector descending, not changing vector but placing sorted indicies to + /// + /// Start sort form + /// Sort till upper bound + /// Array with sorted vector indicies + /// Source + private static void FindLargestItems(int lowerBound, int upperBound, int[] sortedIndices, Vector 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); + } + + /// + /// Approximates the solution to the matrix equation Ax = b. + /// + /// The right hand side vector. + /// The left hand side vector. + public Vector Approximate(Vector 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 result = new DenseVector(rhs.Count); + Approximate(rhs, result); + return result; + } + + /// + /// Approximates the solution to the matrix equation Ax = b. + /// + /// The right hand side vector. + /// The left hand side vector. Also known as the result vector. + public void Approximate(Vector rhs, Vector 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 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); + } + + /// + /// Pivot elements in accoring to internal pivot array + /// + /// Source . + /// Result after pivoting. + private void Pivot(Vector vector, Vector result) + { + for (var i = 0; i < _pivots.Length; i++) + { + result[i] = vector[_pivots[i]]; + } + } + } +} diff --git a/src/Numerics/LinearAlgebra/Single/Solvers/Preconditioners/IlutpElementSorter.cs b/src/Numerics/LinearAlgebra/Single/Solvers/Preconditioners/IlutpElementSorter.cs new file mode 100644 index 00000000..fd6c40bf --- /dev/null +++ b/src/Numerics/LinearAlgebra/Single/Solvers/Preconditioners/IlutpElementSorter.cs @@ -0,0 +1,227 @@ +// +// 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. +// + +namespace MathNet.Numerics.LinearAlgebra.Single.Solvers.Preconditioners +{ + using Generic; + + /// + /// An element sort algorithm for the class. + /// + /// + /// 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. + /// + internal class IlutpElementSorter + { + /// + /// Sorts the elements of the vector in decreasing + /// fashion. The vector itself is not affected. + /// + /// The starting index. + /// The stopping index. + /// An array that will contain the sorted indices once the algorithm finishes. + /// The that contains the values that need to be sorted. + public static void SortDoubleIndicesDecreasing(int lowerBound, int upperBound, int[] sortedIndices, Vector 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); + } + + /// + /// Sorts the elements of the vector in decreasing + /// fashion using heap sort algorithm. The vector itself is not affected. + /// + /// The starting index. + /// The stopping index. + /// An array that will contain the sorted indices once the algorithm finishes. + /// The that contains the values that need to be sorted. + private static void HeapSortDoublesIndices(int lowerBound, int upperBound, int[] sortedIndices, Vector 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; + } + } + + /// + /// Build heap for double indicies + /// + /// Root position + /// Lenght of + /// Indicies of + /// Target + private static void BuildDoubleIndexHeap(int start, int count, int[] sortedIndices, Vector values) + { + while (start >= 0) + { + SiftDoubleIndices(sortedIndices, values, start, count); + start -= 1; + } + } + + /// + /// Sift double indicies + /// + /// Indicies of + /// Target + /// Root position + /// Lenght of + private static void SiftDoubleIndices(int[] sortedIndices, Vector 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; + } + } + + /// + /// Sorts the given integers in a decreasing fashion. + /// + /// The values. + public static void SortIntegersDecreasing(int[] values) + { + HeapSortIntegers(values, values.Length); + } + + /// + /// Sort the given integers in a decreasing fashion using heapsort algorithm + /// + /// Array of values to sort + /// Length of + 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; + } + } + + /// + /// Build heap + /// + /// Target values array + /// Root position + /// Lenght of + private static void BuildHeap(int[] values, int start, int count) + { + while (start >= 0) + { + Sift(values, start, count); + start -= 1; + } + } + + /// + /// Sift values + /// + /// Target value array + /// Root position + /// Lenght of + 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; + } + } + } + + /// + /// Exchange values in array + /// + /// Target values array + /// First value to exchanghe + /// Second value to exchanghe + private static void Exchange(int[] values, int first, int second) + { + var t = values[first]; + values[first] = values[second]; + values[second] = t; + } + } +} \ No newline at end of file diff --git a/src/Numerics/LinearAlgebra/Single/Solvers/Preconditioners/IncompleteLU.cs b/src/Numerics/LinearAlgebra/Single/Solvers/Preconditioners/IncompleteLU.cs new file mode 100644 index 00000000..627c1f6c --- /dev/null +++ b/src/Numerics/LinearAlgebra/Single/Solvers/Preconditioners/IncompleteLU.cs @@ -0,0 +1,260 @@ +// +// 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. +// + +namespace MathNet.Numerics.LinearAlgebra.Single.Solvers.Preconditioners +{ + using System; + using Generic; + using Generic.Solvers.Preconditioners; + using Properties; + + /// + /// An incomplete, level 0, LU factorization preconditioner. + /// + /// + /// The ILU(0) algorithm was taken from:
+ /// Iterative methods for sparse linear systems
+ /// Yousef Saad
+ /// Algorithm is described in Chapter 10, section 10.3.2, page 275
+ ///
+ public sealed class IncompleteLU : IPreConditioner + { + /// + /// The matrix holding the lower (L) and upper (U) matrices. The + /// decomposition matrices are combined to reduce storage. + /// + private SparseMatrix _decompositionLU; + + /// + /// Returns the upper triagonal matrix that was created during the LU decomposition. + /// + /// A new matrix containing the upper triagonal elements. + internal Matrix 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; + } + + /// + /// Returns the lower triagonal matrix that was created during the LU decomposition. + /// + /// A new matrix containing the lower triagonal elements. + internal Matrix 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; + } + + /// + /// Initializes the preconditioner and loads the internal data structures. + /// + /// The matrix upon which the preconditioner is based. + /// If is . + /// If is not a square matrix. + public void Initialize(Matrix 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]); + } + } + } + } + } + } + + /// + /// Approximates the solution to the matrix equation Ax = b. + /// + /// The right hand side vector. + /// The left hand side vector. + public Vector Approximate(Vector 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 result = new DenseVector(rhs.Count); + Approximate(rhs, result); + return result; + } + + /// + /// Approximates the solution to the matrix equation Ax = b. + /// + /// The right hand side vector. + /// The left hand side vector. Also known as the result vector. + public void Approximate(Vector rhs, Vector 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 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); + } + } + } +} diff --git a/src/Numerics/LinearAlgebra/Single/Solvers/Preconditioners/UnitPreconditioner.cs b/src/Numerics/LinearAlgebra/Single/Solvers/Preconditioners/UnitPreconditioner.cs new file mode 100644 index 00000000..da0644c8 --- /dev/null +++ b/src/Numerics/LinearAlgebra/Single/Solvers/Preconditioners/UnitPreconditioner.cs @@ -0,0 +1,139 @@ +// +// 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. +// + +namespace MathNet.Numerics.LinearAlgebra.Single.Solvers.Preconditioners +{ + using System; + using Generic; + using Generic.Solvers; + using Generic.Solvers.Preconditioners; + using Properties; + + /// + /// A unit preconditioner. This preconditioner does not actually do anything + /// it is only used when running an without + /// a preconditioner. + /// + internal sealed class UnitPreconditioner : IPreConditioner + { + /// + /// The coefficient matrix on which this preconditioner operates. + /// Is used to check dimensions on the different vectors that are processed. + /// + private int _size; + + /// + /// Initializes the preconditioner and loads the internal data structures. + /// + /// + /// The matrix upon which the preconditioner is based. + /// + /// If is . + /// If is not a square matrix. + public void Initialize(Matrix matrix) + { + if (matrix == null) + { + throw new ArgumentNullException("matrix"); + } + + if (matrix.RowCount != matrix.ColumnCount) + { + throw new ArgumentException(Resources.ArgumentMatrixSquare, "matrix"); + } + + _size = matrix.RowCount; + } + + /// + /// Approximates the solution to the matrix equation Ax = b. + /// + /// The right hand side vector. + /// The left hand side vector. Also known as the result vector. + /// If is . + /// If is . + /// + /// + /// If and do not have the same size. + /// + /// + /// - or - + /// + /// + /// If the size of is different the number of rows of the coefficient matrix. + /// + /// + public void Approximate(Vector rhs, Vector 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); + } + + /// + /// Approximates the solution to the matrix equation Ax = b. + /// + /// The right hand side vector. + /// The left hand side vector. + /// If is . + /// + /// If the size of is different the number of rows of the coefficient matrix. + /// + public Vector Approximate(Vector rhs) + { + if (rhs == null) + { + throw new ArgumentNullException("rhs"); + } + + if (rhs.Count != _size) + { + throw new ArgumentException(Resources.ArgumentMatrixDimensions); + } + + Vector result = new DenseVector(rhs.Count); + Approximate(rhs, result); + return result; + } + } +} diff --git a/src/Numerics/LinearAlgebra/Single/Solvers/StopCriterium/DivergenceStopCriterium.cs b/src/Numerics/LinearAlgebra/Single/Solvers/StopCriterium/DivergenceStopCriterium.cs new file mode 100644 index 00000000..7cf0ba01 --- /dev/null +++ b/src/Numerics/LinearAlgebra/Single/Solvers/StopCriterium/DivergenceStopCriterium.cs @@ -0,0 +1,393 @@ +// +// 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. +// + +namespace MathNet.Numerics.LinearAlgebra.Single.Solvers.StopCriterium +{ + using System; + using System.Diagnostics; + using Generic; + using Generic.Solvers.Status; + using Generic.Solvers.StopCriterium; + + /// + /// Monitors an iterative calculation for signs of divergence. + /// + public sealed class DivergenceStopCriterium : IIterationStopCriterium + { + /// + /// Default value for the maximum relative increase that the + /// residual may experience before a divergence warning is issued. + /// + public const double DefaultMaximumRelativeIncrease = 0.08; + + /// + /// Default value for the minimum number of iterations over which + /// the residual must grow before a divergence warning is issued. + /// + public const int DefaultMinimumNumberOfIterations = 10; + + /// + /// Defines the default last iteration number. Set to -1 because iterations normally + /// start at 0. + /// + private const int DefaultLastIterationNumber = -1; + + /// + /// The default status. + /// + private static readonly ICalculationStatus DefaultStatus = new CalculationIndetermined(); + + /// + /// The maximum relative increase the residual may experience without triggering a divergence warning. + /// + private double _maximumRelativeIncrease; + + /// + /// The number of iterations over which a residual increase should be tracked before issuing a divergence warning. + /// + private int _minimumNumberOfIterations; + + /// + /// The status of the calculation + /// + private ICalculationStatus _status = DefaultStatus; + + /// + /// The array that holds the tracking information. + /// + private double[] _residualHistory; + + /// + /// The iteration number of the last iteration. + /// + private int _lastIteration = DefaultLastIterationNumber; + + /// + /// Initializes a new instance of the class with the default maximum + /// relative increase and the default minimum number of tracking iterations. + /// + public DivergenceStopCriterium() : this(DefaultMaximumRelativeIncrease, DefaultMinimumNumberOfIterations) + { + } + + /// + /// Initializes a new instance of the class with the specified maximum + /// relative increase and the default minimum number of tracking iterations. + /// + /// The maximum relative increase that the residual may experience before a divergence warning is issued. + public DivergenceStopCriterium(double maximumRelativeIncrease) : this(maximumRelativeIncrease, DefaultMinimumNumberOfIterations) + { + } + + /// + /// Initializes a new instance of the class with the default maximum + /// relative increase and the specified minimum number of tracking iterations. + /// + /// The minimum number of iterations over which the residual must grow before a divergence warning is issued. + public DivergenceStopCriterium(int minimumIterations) : this(DefaultMinimumNumberOfIterations, minimumIterations) + { + } + + /// + /// Initializes a new instance of the class with the specified maximum + /// relative increase and the specified minimum number of tracking iterations. + /// + /// The maximum relative increase that the residual may experience before a divergence warning is issued. + /// The minimum number of iterations over which the residual must grow before a divergence warning is issued. + 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; + } + + /// + /// Gets or sets the maximum relative increase that the residual may experience before a divergence warning is issued. + /// + /// Thrown if the Maximum is set to zero or below. + public double MaximumRelativeIncrease + { + [DebuggerStepThrough] + get + { + return _maximumRelativeIncrease; + } + + [DebuggerStepThrough] + set + { + if (value <= 0) + { + throw new ArgumentOutOfRangeException("value"); + } + + _maximumRelativeIncrease = value; + } + } + + /// + /// Returns the maximum relative increase to the default. + /// + public void ResetMaximumRelativeIncreaseToDefault() + { + _maximumRelativeIncrease = DefaultMaximumRelativeIncrease; + } + + /// + /// Gets or sets the minimum number of iterations over which the residual must grow before + /// issuing a divergence warning. + /// + /// Thrown if the value is set to less than one. + 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; + } + } + + /// + /// Returns the minimum number of iterations to the default. + /// + public void ResetNumberOfIterationsToDefault() + { + _minimumNumberOfIterations = DefaultMinimumNumberOfIterations; + } + + /// + /// Determines the status of the iterative calculation based on the stop criteria stored + /// by the current . Result is set into Status field. + /// + /// The number of iterations that have passed so far. + /// The vector containing the current solution values. + /// The right hand side vector. + /// The vector containing the current residual vectors. + /// + /// 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. + /// + public void DetermineStatus(int iterationNumber, Vector solutionVector, Vector sourceVector, Vector 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; + } + + /// + /// Detect if solution is diverging + /// + /// true if diverging, otherwise false + 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; + } + + /// + /// Gets required history lenght + /// + private int RequiredHistoryLength + { + [DebuggerStepThrough] + get + { + return _minimumNumberOfIterations + 1; + } + } + + /// + /// Set status to + /// + private void SetStatusToDiverged() + { + if (!(_status is CalculationDiverged)) + { + _status = new CalculationDiverged(); + } + } + + /// + /// Set status to + /// + private void SetStatusToRunning() + { + if (!(_status is CalculationRunning)) + { + _status = new CalculationRunning(); + } + } + + /// + /// Gets the current calculation status. + /// + public ICalculationStatus Status + { + [DebuggerStepThrough] + get + { + return _status; + } + } + + /// + /// Resets the to the pre-calculation state. + /// + public void ResetToPrecalculationState() + { + _status = DefaultStatus; + _lastIteration = DefaultLastIterationNumber; + _residualHistory = null; + } + + /// + /// Gets the which indicates what sort of stop criterium this + /// monitors. + /// + /// Returns . + public StopLevel StopLevel + { + [DebuggerStepThrough] + get + { + return StopLevel.Divergence; + } + } + + /// + /// Clones the current and its settings. + /// + /// A new instance of the class. + public IIterationStopCriterium Clone() + { + return new DivergenceStopCriterium(_maximumRelativeIncrease, _minimumNumberOfIterations); + } + +#if !SILVERLIGHT + /// + /// Clone this object + /// + /// Object clone + object ICloneable.Clone() + { + return Clone(); + } +#endif + } +} diff --git a/src/Numerics/LinearAlgebra/Single/Solvers/StopCriterium/FailureStopCriterium.cs b/src/Numerics/LinearAlgebra/Single/Solvers/StopCriterium/FailureStopCriterium.cs new file mode 100644 index 00000000..99f9d100 --- /dev/null +++ b/src/Numerics/LinearAlgebra/Single/Solvers/StopCriterium/FailureStopCriterium.cs @@ -0,0 +1,201 @@ +// +// 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. +// + +namespace MathNet.Numerics.LinearAlgebra.Single.Solvers.StopCriterium +{ + using System; + using System.Diagnostics; + using Generic; + using Generic.Solvers.Status; + using Generic.Solvers.StopCriterium; + using Properties; + + /// + /// Defines an that monitors residuals for NaN's. + /// + public sealed class FailureStopCriterium : IIterationStopCriterium + { + /// + /// Defines the default last iteration number. Set to -1 because iterations normally + /// start at 0. + /// + private const int DefaultLastIterationNumber = -1; + + /// + /// The default status. + /// + private static readonly ICalculationStatus DefaultStatus = new CalculationIndetermined(); + + /// + /// The status of the calculation + /// + private ICalculationStatus _status = DefaultStatus; + + /// + /// The iteration number of the last iteration. + /// + private int _lastIteration = DefaultLastIterationNumber; + + /// + /// Determines the status of the iterative calculation based on the stop criteria stored + /// by the current . Result is set into Status field. + /// + /// The number of iterations that have passed so far. + /// The vector containing the current solution values. + /// The right hand side vector. + /// The vector containing the current residual vectors. + /// + /// 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. + /// + public void DetermineStatus(int iterationNumber, Vector solutionVector, Vector sourceVector, Vector 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; + } + + /// + /// Set status to + /// + private void SetStatusToFailed() + { + if (!(_status is CalculationFailure)) + { + _status = new CalculationFailure(); + } + } + + /// + /// Set status to + /// + private void SetStatusToRunning() + { + if (!(_status is CalculationRunning)) + { + _status = new CalculationRunning(); + } + } + + /// + /// Gets the current calculation status. + /// + public ICalculationStatus Status + { + [DebuggerStepThrough] + get + { + return _status; + } + } + + /// + /// Resets the to the pre-calculation state. + /// + public void ResetToPrecalculationState() + { + _status = DefaultStatus; + _lastIteration = DefaultLastIterationNumber; + } + + /// + /// Gets the which indicates what sort of stop criterium this + /// monitors. + /// + /// Returns . + public StopLevel StopLevel + { + [DebuggerStepThrough] + get + { + return StopLevel.CalculationFailure; + } + } + + /// + /// Clones the current and its settings. + /// + /// A new instance of the class. + public IIterationStopCriterium Clone() + { + return new FailureStopCriterium(); + } + +#if !SILVERLIGHT + /// + /// Clones the current and its settings. + /// + /// A new instance of the class. + object ICloneable.Clone() + { + return Clone(); + } +#endif + } +} diff --git a/src/Numerics/LinearAlgebra/Single/Solvers/StopCriterium/IterationCountStopCriterium.cs b/src/Numerics/LinearAlgebra/Single/Solvers/StopCriterium/IterationCountStopCriterium.cs new file mode 100644 index 00000000..3409cae3 --- /dev/null +++ b/src/Numerics/LinearAlgebra/Single/Solvers/StopCriterium/IterationCountStopCriterium.cs @@ -0,0 +1,227 @@ +// +// 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. +// + +namespace MathNet.Numerics.LinearAlgebra.Single.Solvers.StopCriterium +{ + using System; + using System.Diagnostics; + using Generic; + using Generic.Solvers.Status; + using Generic.Solvers.StopCriterium; + + /// + /// Defines an that monitors the numbers of iteration + /// steps as stop criterium. + /// + public sealed class IterationCountStopCriterium : IIterationStopCriterium + { + /// + /// The default value for the maximum number of iterations the process is allowed + /// to perform. + /// + public const int DefaultMaximumNumberOfIterations = 1000; + + /// + /// The default status. + /// + private static readonly ICalculationStatus DefaultStatus = new CalculationIndetermined(); + + /// + /// The maximum number of iterations the calculation is allowed to perform. + /// + private int _maximumNumberOfIterations; + + /// + /// The status of the calculation + /// + private ICalculationStatus _status = DefaultStatus; + + /// + /// Initializes a new instance of the class with the default maximum + /// number of iterations. + /// + public IterationCountStopCriterium() : this(DefaultMaximumNumberOfIterations) + { + } + + /// + /// Initializes a new instance of the class with the specified maximum + /// number of iterations. + /// + /// The maximum number of iterations the calculation is allowed to perform. + public IterationCountStopCriterium(int maximumNumberOfIterations) + { + if (maximumNumberOfIterations < 1) + { + throw new ArgumentOutOfRangeException("maximumNumberOfIterations"); + } + + _maximumNumberOfIterations = maximumNumberOfIterations; + } + + /// + /// Gets or sets the maximum number of iterations the calculation is allowed to perform. + /// + /// Thrown if the Maximum is set to a negative value. + public int MaximumNumberOfIterations + { + [DebuggerStepThrough] + get + { + return _maximumNumberOfIterations; + } + + [DebuggerStepThrough] + set + { + if (value < 1) + { + throw new ArgumentOutOfRangeException("value"); + } + + _maximumNumberOfIterations = value; + } + } + + /// + /// Returns the maximum number of iterations to the default. + /// + public void ResetMaximumNumberOfIterationsToDefault() + { + _maximumNumberOfIterations = DefaultMaximumNumberOfIterations; + } + + /// + /// Determines the status of the iterative calculation based on the stop criteria stored + /// by the current . Result is set into Status field. + /// + /// The number of iterations that have passed so far. + /// The vector containing the current solution values. + /// The right hand side vector. + /// The vector containing the current residual vectors. + /// + /// 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. + /// + public void DetermineStatus(int iterationNumber, Vector solutionVector, Vector sourceVector, Vector residualVector) + { + if (iterationNumber < 0) + { + throw new ArgumentOutOfRangeException("iterationNumber"); + } + + if (iterationNumber >= _maximumNumberOfIterations) + { + SetStatusToFinished(); + } + else + { + SetStatusToRunning(); + } + } + + /// + /// Set status to + /// + private void SetStatusToFinished() + { + if (!(_status is CalculationStoppedWithoutConvergence)) + { + _status = new CalculationStoppedWithoutConvergence(); + } + } + + /// + /// Set status to + /// + private void SetStatusToRunning() + { + if (!(_status is CalculationRunning)) + { + _status = new CalculationRunning(); + } + } + + /// + /// Gets the current calculation status. + /// + public ICalculationStatus Status + { + [DebuggerStepThrough] + get + { + return _status; + } + } + + /// + /// Resets the to the pre-calculation state. + /// + public void ResetToPrecalculationState() + { + _status = DefaultStatus; + } + + /// + /// Gets the which indicates what sort of stop criterium this + /// monitors. + /// + /// Returns . + public StopLevel StopLevel + { + [DebuggerStepThrough] + get + { + return StopLevel.StoppedWithoutConvergence; + } + } + + /// + /// Clones the current and its settings. + /// + /// A new instance of the class. + public IIterationStopCriterium Clone() + { + return new IterationCountStopCriterium(_maximumNumberOfIterations); + } + +#if !SILVERLIGHT + /// + /// Clones the current and its settings. + /// + /// A new instance of the object. + object ICloneable.Clone() + { + return Clone(); + } +#endif + } +} diff --git a/src/Numerics/LinearAlgebra/Single/Solvers/StopCriterium/ResidualStopCriterium.cs b/src/Numerics/LinearAlgebra/Single/Solvers/StopCriterium/ResidualStopCriterium.cs new file mode 100644 index 00000000..4f828c6b --- /dev/null +++ b/src/Numerics/LinearAlgebra/Single/Solvers/StopCriterium/ResidualStopCriterium.cs @@ -0,0 +1,409 @@ +// +// 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. +// + +namespace MathNet.Numerics.LinearAlgebra.Single.Solvers.StopCriterium +{ + using System; + using System.Diagnostics; + using Generic; + using Generic.Solvers.Status; + using Generic.Solvers.StopCriterium; + using Properties; + + /// + /// Defines an that monitors residuals as stop criterium. + /// + public sealed class ResidualStopCriterium : IIterationStopCriterium + { + /// + /// The default value for the maximum value of the residual. + /// + public const double DefaultMaximumResidual = 1e-6; + + /// + /// The default value for the minimum number of iterations. + /// + public const int DefaultMinimumIterationsBelowMaximum = 0; + + /// + /// Defines the default last iteration number. Set to -1 because iterations normally start at 0. + /// + private const int DefaultLastIterationNumber = -1; + + /// + /// The default status. + /// + private static readonly ICalculationStatus DefaultStatus = new CalculationIndetermined(); + + /// + /// The maximum value for the residual below which the calculation is considered converged. + /// + private double _maximum; + + /// + /// The minimum number of iterations for which the residual has to be below the maximum before + /// the calculation is considered converged. + /// + private int _minimumIterationsBelowMaximum; + + /// + /// The status of the calculation + /// + private ICalculationStatus _status = DefaultStatus; + + /// + /// The number of iterations since the residuals got below the maximum. + /// + private int _iterationCount; + + /// + /// The iteration number of the last iteration. + /// + private int _lastIteration = DefaultLastIterationNumber; + + /// + /// Initializes a new instance of the class with the default maximum + /// residual and the default minimum number of iterations. + /// + public ResidualStopCriterium() : this(DefaultMaximumResidual, DefaultMinimumIterationsBelowMaximum) + { + } + + /// + /// Initializes a new instance of the class with the specified + /// maximum residual and the default minimum number of iterations. + /// + /// The maximum value for the residual below which the calculation is considered converged. + public ResidualStopCriterium(double maximum) : this(maximum, DefaultMinimumIterationsBelowMaximum) + { + } + + /// + /// Initializes a new instance of the class with the default maximum residual + /// and specified minimum number of iterations. + /// + /// + /// The minimum number of iterations for which the residual has to be below the maximum before + /// the calculation is considered converged. + /// + public ResidualStopCriterium(int minimumIterationsBelowMaximum) : this(DefaultMaximumResidual, minimumIterationsBelowMaximum) + { + } + + /// + /// Initializes a new instance of the class with the specified + /// maximum residual and minimum number of iterations. + /// + /// + /// The maximum value for the residual below which the calculation is considered converged. + /// + /// + /// The minimum number of iterations for which the residual has to be below the maximum before + /// the calculation is considered converged. + /// + public ResidualStopCriterium(double maximum, int minimumIterationsBelowMaximum) + { + if (maximum < 0) + { + throw new ArgumentOutOfRangeException("maximum"); + } + + if (minimumIterationsBelowMaximum < 0) + { + throw new ArgumentOutOfRangeException("minimumIterationsBelowMaximum"); + } + + _maximum = maximum; + _minimumIterationsBelowMaximum = minimumIterationsBelowMaximum; + } + + /// + /// Gets or sets the maximum value for the residual below which the calculation is considered + /// converged. + /// + /// Thrown if the Maximum is set to a negative value. + public double Maximum + { + [DebuggerStepThrough] + get + { + return _maximum; + } + + [DebuggerStepThrough] + set + { + if (value < 0) + { + throw new ArgumentOutOfRangeException("value"); + } + + _maximum = value; + } + } + + /// + /// Returns the maximum residual to the default. + /// + public void ResetMaximumResidualToDefault() + { + _maximum = DefaultMaximumResidual; + } + + /// + /// Gets or sets the minimum number of iterations for which the residual has to be + /// below the maximum before the calculation is considered converged. + /// + /// Thrown if the BelowMaximumFor is set to a value less than 1. + public int MinimumIterationsBelowMaximum + { + [DebuggerStepThrough] + get + { + return _minimumIterationsBelowMaximum; + } + + [DebuggerStepThrough] + set + { + if (value < 0) + { + throw new ArgumentOutOfRangeException("value"); + } + + _minimumIterationsBelowMaximum = value; + } + } + + /// + /// Returns the minimum number of iterations to the default. + /// + public void ResetMinimumIterationsBelowMaximumToDefault() + { + _minimumIterationsBelowMaximum = DefaultMinimumIterationsBelowMaximum; + } + + /// + /// Determines the status of the iterative calculation based on the stop criteria stored + /// by the current . Result is set into Status field. + /// + /// The number of iterations that have passed so far. + /// The vector containing the current solution values. + /// The right hand side vector. + /// The vector containing the current residual vectors. + /// + /// 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. + /// + public void DetermineStatus(int iterationNumber, Vector solutionVector, Vector sourceVector, Vector 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; + } + + /// + /// Calculate stop criterium + /// + /// Solution vector norm + /// Criterium value + 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); + } + + /// + /// Set status to + /// + private void SetStatusToDiverged() + { + if (!(_status is CalculationDiverged)) + { + _status = new CalculationDiverged(); + } + } + + /// + /// Set status to + /// + private void SetStatusToConverged() + { + if (!(_status is CalculationConverged)) + { + _status = new CalculationConverged(); + } + } + + /// + /// Set status to + /// + private void SetStatusToRunning() + { + if (!(_status is CalculationRunning)) + { + _status = new CalculationRunning(); + } + } + + /// + /// Gets the current calculation status. + /// + public ICalculationStatus Status + { + [DebuggerStepThrough] + get + { + return _status; + } + } + + /// + /// Resets the to the pre-calculation state. + /// + public void ResetToPrecalculationState() + { + _status = DefaultStatus; + _iterationCount = 0; + _lastIteration = DefaultLastIterationNumber; + } + + /// + /// Gets the which indicates what sort of stop criterium this + /// monitors. + /// + /// Returns . + public StopLevel StopLevel + { + [DebuggerStepThrough] + get + { + return StopLevel.Convergence; + } + } + + /// + /// Clones the current and its settings. + /// + /// A new instance of the class. + public IIterationStopCriterium Clone() + { + return new ResidualStopCriterium(_maximum, _minimumIterationsBelowMaximum); + } + +#if !SILVERLIGHT + /// + /// Clones the current and its settings. + /// + /// A new instance of the object. + object ICloneable.Clone() + { + return Clone(); + } +#endif + } +} diff --git a/src/Numerics/LinearAlgebra/Single/SparseMatrix.cs b/src/Numerics/LinearAlgebra/Single/SparseMatrix.cs new file mode 100644 index 00000000..ce1e31b4 --- /dev/null +++ b/src/Numerics/LinearAlgebra/Single/SparseMatrix.cs @@ -0,0 +1,1707 @@ +// +// 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. +// + +namespace MathNet.Numerics.LinearAlgebra.Single +{ + using System; + using System.Text; + using Distributions; + using Generic; + using Properties; + using Threading; + + /// + /// A Matrix class with sparse storage. The underlying storage scheme is 3-array CSR Format. + /// + public class SparseMatrix : Matrix + { + /// + /// Object for use in "lock" + /// + private readonly object _lockObject = new object(); + + /// + /// The array containing the row indices of the existing rows. Element "j" of the array gives the index of the + /// element in the array that is first non-zero element in a row "j" + /// + private readonly int[] _rowIndex = new int[0]; + + /// + /// Array that contains the non-zero elements of matrix. Values of the non-zero elements of matrix are mapped into the values + /// array using the row-major storage mapping described in a compressed sparse row (CSR) format. + /// + private float[] _nonZeroValues = new float[0]; + + /// + /// Gets the number of non zero elements in the matrix. + /// + /// The number of non zero elements. + public int NonZerosCount + { + get; + private set; + } + + /// + /// An array containing the column indices of the non-zero values. Element "I" of the array + /// is the number of the column in matrix that contains the I-th value in the array. + /// + private int[] _columnIndices = new int[0]; + + /// + /// Initializes a new instance of the class. + /// + /// + /// The number of rows. + /// + /// + /// The number of columns. + /// + public SparseMatrix(int rows, int columns) : base(rows, columns) + { + _rowIndex = new int[rows]; + } + + /// + /// Initializes a new instance of the class. This matrix is square with a given size. + /// + /// the size of the square matrix. + /// + /// If is less than one. + /// + public SparseMatrix(int order) : this(order, order) + { + } + + /// + /// Initializes a new instance of the class with all entries set to a particular value. + /// + /// + /// The number of rows. + /// + /// + /// The number of columns. + /// + /// The value which we assign to each element of the matrix. + public SparseMatrix(int rows, int columns, float value) : this(rows, columns) + { + if (value == 0.0) + { + return; + } + + NonZerosCount = rows * columns; + _nonZeroValues = new float[NonZerosCount]; + _columnIndices = new int[NonZerosCount]; + + for (int i = 0, j = 0; i < _nonZeroValues.Length; i++, j++) + { + // Reset column position to "0" + if (j == columns) + { + j = 0; + } + + _nonZeroValues[i] = value; + _columnIndices[i] = j; + } + + // Set proper row pointers + for (var i = 0; i < _rowIndex.Length; i++) + { + _rowIndex[i] = ((i + 1) * columns) - columns; + } + } + + /// + /// Initializes a new instance of the class from a one dimensional array. + /// + /// The number of rows. + /// The number of columns. + /// The one dimensional array to create this matrix from. This array should store the matrix in column-major order. + /// If length is less than * . + /// + public SparseMatrix(int rows, int columns, float[] array) : this(rows, columns) + { + if (rows * columns > array.Length) + { + throw new ArgumentOutOfRangeException(Resources.ArgumentMatrixDimensions); + } + + for (var i = 0; i < rows; i++) + { + for (var j = 0; j < columns; j++) + { + SetValueAt(i, j, array[i + (j * rows)]); + } + } + } + + /// + /// Initializes a new instance of the class from a 2D array. + /// + /// The 2D array to create this matrix from. + public SparseMatrix(float[,] array) : this(array.GetLength(0), array.GetLength(1)) + { + var rows = array.GetLength(0); + var columns = array.GetLength(1); + + for (var i = 0; i < rows; i++) + { + for (var j = 0; j < columns; j++) + { + SetValueAt(i, j, array[i, j]); + } + } + } + + /// + /// Creates a SparseMatrix for the given number of rows and columns. + /// + /// + /// The number of rows. + /// + /// + /// The number of columns. + /// + /// + /// A SparseMatrix with the given dimensions. + /// + public override Matrix CreateMatrix(int numberOfRows, int numberOfColumns) + { + return new SparseMatrix(numberOfRows, numberOfColumns); + } + + /// + /// Creates a with a the given dimension. + /// + /// The size of the vector. + /// + /// A with the given dimension. + /// + public override Vector CreateVector(int size) + { + return new SparseVector(size); + } + + /// + /// Returns a new matrix containing the lower triangle of this matrix. + /// + /// The lower triangle of this matrix. + public override Matrix LowerTriangle() + { + var result = CreateMatrix(RowCount, ColumnCount); + LowerTriangleImpl(result); + return result; + } + + /// + /// Puts the lower triangle of this matrix into the result matrix. + /// + /// Where to store the lower triangle. + /// If is . + /// If the result matrix's dimensions are not the same as this matrix. + public override void LowerTriangle(Matrix result) + { + if (result == null) + { + throw new ArgumentNullException("result"); + } + + if (result.RowCount != RowCount || result.ColumnCount != ColumnCount) + { + throw new ArgumentException(Resources.ArgumentMatrixDimensions, "result"); + } + + if (ReferenceEquals(this, result)) + { + var tmp = result.CreateMatrix(result.RowCount, result.ColumnCount); + LowerTriangle(tmp); + tmp.CopyTo(result); + } + else + { + result.Clear(); + LowerTriangleImpl(result); + } + } + + /// + /// Puts the lower triangle of this matrix into the result matrix. + /// + /// Where to store the lower triangle. + private void LowerTriangleImpl(Matrix result) + { + for (var row = 0; row < result.RowCount; row++) + { + var startIndex = _rowIndex[row]; + var endIndex = row < _rowIndex.Length - 1 ? _rowIndex[row + 1] : NonZerosCount; + for (var j = startIndex; j < endIndex; j++) + { + if (row >= _columnIndices[j]) + { + result.At(row, _columnIndices[j], _nonZeroValues[j]); + } + } + } + } + + /// + /// Returns a new matrix containing the upper triangle of this matrix. + /// + /// The upper triangle of this matrix. + public override Matrix UpperTriangle() + { + var result = CreateMatrix(RowCount, ColumnCount); + UpperTriangleImpl(result); + return result; + } + + /// + /// Puts the upper triangle of this matrix into the result matrix. + /// + /// Where to store the lower triangle. + /// If is . + /// If the result matrix's dimensions are not the same as this matrix. + public override void UpperTriangle(Matrix result) + { + if (result == null) + { + throw new ArgumentNullException("result"); + } + + if (result.RowCount != RowCount || result.ColumnCount != ColumnCount) + { + throw new ArgumentException(Resources.ArgumentMatrixDimensions, "result"); + } + + if (ReferenceEquals(this, result)) + { + var tmp = result.CreateMatrix(result.RowCount, result.ColumnCount); + UpperTriangle(tmp); + tmp.CopyTo(result); + } + else + { + result.Clear(); + UpperTriangleImpl(result); + } + } + + /// + /// Puts the upper triangle of this matrix into the result matrix. + /// + /// Where to store the lower triangle. + private void UpperTriangleImpl(Matrix result) + { + for (var row = 0; row < result.RowCount; row++) + { + var startIndex = _rowIndex[row]; + var endIndex = row < _rowIndex.Length - 1 ? _rowIndex[row + 1] : NonZerosCount; + for (var j = startIndex; j < endIndex; j++) + { + if (row <= _columnIndices[j]) + { + result.At(row, _columnIndices[j], _nonZeroValues[j]); + } + } + } + } + + /// + /// Creates a matrix that contains the values from the requested sub-matrix. + /// + /// The row to start copying from. + /// The number of rows to copy. Must be positive. + /// The column to start copying from. + /// The number of columns to copy. Must be positive. + /// The requested sub-matrix. + /// If: is + /// negative, or greater than or equal to the number of rows. + /// is negative, or greater than or equal to the number + /// of columns. + /// (columnIndex + columnLength) >= Columns + /// (rowIndex + rowLength) >= Rows + /// If or + /// is not positive. + public override Matrix SubMatrix(int rowIndex, int rowLength, int columnIndex, int columnLength) + { + if (rowIndex >= RowCount || rowIndex < 0) + { + throw new ArgumentOutOfRangeException("rowIndex"); + } + + if (columnIndex >= ColumnCount || columnIndex < 0) + { + throw new ArgumentOutOfRangeException("columnIndex"); + } + + if (rowLength < 1) + { + throw new ArgumentException(Resources.ArgumentMustBePositive, "rowLength"); + } + + if (columnLength < 1) + { + throw new ArgumentException(Resources.ArgumentMustBePositive, "columnLength"); + } + + var colMax = columnIndex + columnLength; + var rowMax = rowIndex + rowLength; + + if (rowMax > RowCount) + { + throw new ArgumentOutOfRangeException("rowLength"); + } + + if (colMax > ColumnCount) + { + throw new ArgumentOutOfRangeException("columnLength"); + } + + var result = (SparseMatrix)CreateMatrix(rowLength, columnLength); + + for (int i = rowIndex, row = 0; i < rowMax; i++, row++) + { + var startIndex = _rowIndex[i]; + var endIndex = row < _rowIndex.Length - 1 ? _rowIndex[i + 1] : NonZerosCount; + + for (int j = startIndex; j < endIndex; j++) + { + // check if the column index is in the range + if ((_columnIndices[j] >= columnIndex) && (_columnIndices[j] < columnIndex + columnLength)) + { + var column = _columnIndices[j] - columnIndex; + result.SetValueAt(row, column, _nonZeroValues[j]); + } + } + } + + return result; + } + + /// + /// Returns a new matrix containing the lower triangle of this matrix. The new matrix + /// does not contain the diagonal elements of this matrix. + /// + /// The lower triangle of this matrix. + public override Matrix StrictlyLowerTriangle() + { + var result = CreateMatrix(RowCount, ColumnCount); + StrictlyLowerTriangleImpl(result); + return result; + } + + /// + /// Puts the strictly lower triangle of this matrix into the result matrix. + /// + /// Where to store the lower triangle. + /// If is . + /// If the result matrix's dimensions are not the same as this matrix. + public override void StrictlyLowerTriangle(Matrix result) + { + if (result == null) + { + throw new ArgumentNullException("result"); + } + + if (result.RowCount != RowCount || result.ColumnCount != ColumnCount) + { + throw new ArgumentException(Resources.ArgumentMatrixDimensions, "result"); + } + + if (ReferenceEquals(this, result)) + { + var tmp = result.CreateMatrix(result.RowCount, result.ColumnCount); + StrictlyLowerTriangle(tmp); + tmp.CopyTo(result); + } + else + { + result.Clear(); + StrictlyLowerTriangleImpl(result); + } + } + + /// + /// Puts the strictly lower triangle of this matrix into the result matrix. + /// + /// Where to store the lower triangle. + private void StrictlyLowerTriangleImpl(Matrix result) + { + for (var row = 0; row < result.RowCount; row++) + { + var startIndex = _rowIndex[row]; + var endIndex = row < _rowIndex.Length - 1 ? _rowIndex[row + 1] : NonZerosCount; + for (var j = startIndex; j < endIndex; j++) + { + if (row > _columnIndices[j]) + { + result.At(row, _columnIndices[j], _nonZeroValues[j]); + } + } + } + } + + /// + /// Returns a new matrix containing the upper triangle of this matrix. The new matrix + /// does not contain the diagonal elements of this matrix. + /// + /// The upper triangle of this matrix. + public override Matrix StrictlyUpperTriangle() + { + var result = CreateMatrix(RowCount, ColumnCount); + StrictlyUpperTriangleImpl(result); + return result; + } + + /// + /// Puts the strictly upper triangle of this matrix into the result matrix. + /// + /// Where to store the lower triangle. + /// If is . + /// If the result matrix's dimensions are not the same as this matrix. + public override void StrictlyUpperTriangle(Matrix result) + { + if (result == null) + { + throw new ArgumentNullException("result"); + } + + if (result.RowCount != RowCount || result.ColumnCount != ColumnCount) + { + throw new ArgumentException(Resources.ArgumentMatrixDimensions, "result"); + } + + if (ReferenceEquals(this, result)) + { + var tmp = result.CreateMatrix(result.RowCount, result.ColumnCount); + StrictlyUpperTriangle(tmp); + tmp.CopyTo(result); + } + else + { + result.Clear(); + StrictlyUpperTriangleImpl(result); + } + } + + /// + /// Puts the strictly upper triangle of this matrix into the result matrix. + /// + /// Where to store the lower triangle. + private void StrictlyUpperTriangleImpl(Matrix result) + { + for (var row = 0; row < result.RowCount; row++) + { + var startIndex = _rowIndex[row]; + var endIndex = row < _rowIndex.Length - 1 ? _rowIndex[row + 1] : NonZerosCount; + for (var j = startIndex; j < endIndex; j++) + { + if (row < _columnIndices[j]) + { + result.At(row, _columnIndices[j], _nonZeroValues[j]); + } + } + } + } + + /// + /// Returns the matrix's elements as an array with the data laid out column-wise. + /// + ///
+        /// 1, 2, 3
+        /// 4, 5, 6  will be returned as  1, 4, 7, 2, 5, 8, 3, 6, 9
+        /// 7, 8, 9
+        /// 
+ /// An array containing the matrix's elements. + public override float[] ToColumnWiseArray() + { + var ret = new float[RowCount * ColumnCount]; + for (var j = 0; j < ColumnCount; j++) + { + for (var i = 0; i < RowCount; i++) + { + var index = FindItem(i, j); + ret[(j * RowCount) + i] = index >= 0 ? _nonZeroValues[index] : 0.0f; + } + } + + return ret; + } + + /// + /// Retrieves the requested element without range checking. + /// + /// + /// The row of the element. + /// + /// + /// The column of the element. + /// + /// + /// The requested element. + /// + public override float At(int row, int column) + { + lock (_lockObject) + { + var index = FindItem(row, column); + return index >= 0 ? _nonZeroValues[index] : 0.0f; + } + } + + /// + /// Sets the value of the given element. + /// + /// + /// The row of the element. + /// + /// + /// The column of the element. + /// + /// + /// The value to set the element to. + /// + public override void At(int row, int column, float value) + { + lock (_lockObject) + { + SetValueAt(row, column, value); + } + } + + #region Internal methods - CRS storage implementation + /// + /// Created this method because we cannot call "virtual At" in constructor of the class, but we need to do it + /// + /// The row of the element. + /// The column of the element. + /// The value to set the element to. + /// WARNING: This method is not thread safe. Use "lock" with it and be sure to avoid deadlocks + private void SetValueAt(int row, int column, float value) + { + var index = FindItem(row, column); + if (index >= 0) + { + // Non-zero item found in matrix + if (value == 0.0) + { + // Delete existing item + DeleteItemByIndex(index, row); + } + else + { + // Update item + _nonZeroValues[index] = value; + } + } + else + { + // Item not found. Add new value + if (value == 0.0) + { + return; + } + + index = ~index; + + // Check if the storage needs to be increased + if ((NonZerosCount == _nonZeroValues.Length) && (NonZerosCount < (RowCount * ColumnCount))) + { + // Value array is completely full so we increase the size + // Determine the increase in size. We will not grow beyond the size of the matrix + var size = Math.Min(_nonZeroValues.Length + GrowthSize(), RowCount * ColumnCount); + Array.Resize(ref _nonZeroValues, size); + Array.Resize(ref _columnIndices, size); + } + + // Move all values (with an position larger than index) in the value array to the next position + // move all values (with an position larger than index) in the columIndices array to the next position + for (var i = NonZerosCount - 1; i > index - 1; i--) + { + _nonZeroValues[i + 1] = _nonZeroValues[i]; + _columnIndices[i + 1] = _columnIndices[i]; + } + + // Add the value and the column index + _nonZeroValues[index] = value; + _columnIndices[index] = column; + + // increase the number of non-zero numbers by one + NonZerosCount += 1; + + // add 1 to all the row indices for rows bigger than rowIndex + // so that they point to the correct part of the value array again. + for (var i = row + 1; i < _rowIndex.Length; i++) + { + _rowIndex[i] += 1; + } + } + } + + /// + /// Delete value from internal storage + /// + /// Index of value in nonZeroValues array + /// Row number of matrix + /// WARNING: This method is not thread safe. Use "lock" with it and be sure to avoid deadlocks + private void DeleteItemByIndex(int itemIndex, int row) + { + // Move all values (with an position larger than index) in the value array to the previous position + // move all values (with an position larger than index) in the columIndices array to the previous position + for (var i = itemIndex + 1; i < NonZerosCount; i++) + { + _nonZeroValues[i - 1] = _nonZeroValues[i]; + _columnIndices[i - 1] = _columnIndices[i]; + } + + // Decrease value in Row + for (var i = row + 1; i < _rowIndex.Length; i++) + { + _rowIndex[i] -= 1; + } + + NonZerosCount -= 1; + + // Check if the storage needs to be shrink. This is reasonable to do if + // there are a lot of non-zero elements and storage is two times bigger + if ((NonZerosCount > 1024) && (NonZerosCount < _nonZeroValues.Length / 2)) + { + Array.Resize(ref _nonZeroValues, NonZerosCount); + Array.Resize(ref _columnIndices, NonZerosCount); + } + } + + /// + /// Find item Index in nonZeroValues array + /// + /// Matrix row index + /// Matrix column index + /// Item index + /// WARNING: This method is not thread safe. Use "lock" with it and be sure to avoid deadlocks + private int FindItem(int row, int column) + { + // Determin bounds in columnIndices array where this item should be searched (using rowIndex) + var startIndex = _rowIndex[row]; + var endIndex = row < _rowIndex.Length - 1 ? _rowIndex[row + 1] : NonZerosCount; + return Array.BinarySearch(_columnIndices, startIndex, endIndex - startIndex, column); + } + + /// + /// Calculates the amount with which to grow the storage array's if they need to be + /// increased in size. + /// + /// The amount grown. + private int GrowthSize() + { + int delta; + if (_nonZeroValues.Length > 1024) + { + delta = _nonZeroValues.Length / 4; + } + else + { + if (_nonZeroValues.Length > 256) + { + delta = 512; + } + else + { + delta = _nonZeroValues.Length > 64 ? 128 : 32; + } + } + + return delta; + } + #endregion + + /// + /// Sets all values to zero. + /// + public override void Clear() + { + NonZerosCount = 0; + Array.Clear(_rowIndex, 0, _rowIndex.Length); + } + + /// + /// Copies the elements of this matrix to the given matrix. + /// + /// + /// The matrix to copy values into. + /// + /// + /// If target is . + /// + /// + /// If this and the target matrix do not have the same dimensions.. + /// + public override void CopyTo(Matrix target) + { + var sparseTarget = target as SparseMatrix; + + if (sparseTarget == null) + { + base.CopyTo(target); + } + else + { + if (ReferenceEquals(this, target)) + { + return; + } + + if (RowCount != target.RowCount || ColumnCount != target.ColumnCount) + { + throw new ArgumentException(Resources.ArgumentMatrixDimensions, "target"); + } + + // Lets copy only needed data. Portion of needed data is determined by NonZerosCount value + sparseTarget._nonZeroValues = new float[NonZerosCount]; + sparseTarget._columnIndices = new int[NonZerosCount]; + sparseTarget.NonZerosCount = NonZerosCount; + + Buffer.BlockCopy(_nonZeroValues, 0, sparseTarget._nonZeroValues, 0, NonZerosCount * Constants.SizeOfFloat); + Buffer.BlockCopy(_columnIndices, 0, sparseTarget._columnIndices, 0, NonZerosCount * Constants.SizeOfInt); + Buffer.BlockCopy(_rowIndex, 0, sparseTarget._rowIndex, 0, RowCount * Constants.SizeOfInt); + } + } + + /// + /// Returns a hash code for this instance. + /// + /// + /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. + /// + public override int GetHashCode() + { + var hashNum = Math.Min(NonZerosCount, 25); + long hash = 0; + for (var i = 0; i < hashNum; i++) + { +#if SILVERLIGHT + hash ^= Precision.DoubleToInt64Bits(_nonZeroValues[i]); +#else + hash ^= BitConverter.DoubleToInt64Bits(_nonZeroValues[i]); +#endif + } + + return BitConverter.ToInt32(BitConverter.GetBytes(hash), 4); + } + + /// + /// Returns the transpose of this matrix. + /// + /// The transpose of this matrix. + public override Matrix Transpose() + { + var ret = new SparseMatrix(ColumnCount, RowCount) + { + _columnIndices = new int[NonZerosCount], + _nonZeroValues = new float[NonZerosCount] + }; + + // Do an 'inverse' CopyTo iterate over the rows + for (var i = 0; i < _rowIndex.Length; i++) + { + // Get the begin / end index for the current row + var startIndex = _rowIndex[i]; + var endIndex = i < _rowIndex.Length - 1 ? _rowIndex[i + 1] : NonZerosCount; + + // Get the values for the current row + if (startIndex == endIndex) + { + // Begin and end are equal. There are no values in the row, Move to the next row + continue; + } + + for (var j = startIndex; j < endIndex; j++) + { + ret.SetValueAt(_columnIndices[j], i, _nonZeroValues[j]); + } + } + + return ret; + } + + /// Calculates the Frobenius norm of this matrix. + /// The Frobenius norm of this matrix. + public override double FrobeniusNorm() + { + var transpose = (SparseMatrix)Transpose(); + var aat = this * transpose; + + var norm = 0.0; + + for (var i = 0; i < aat._rowIndex.Length; i++) + { + // Get the begin / end index for the current row + var startIndex = aat._rowIndex[i]; + var endIndex = i < aat._rowIndex.Length - 1 ? aat._rowIndex[i + 1] : aat.NonZerosCount; + + // Get the values for the current row + if (startIndex == endIndex) + { + // Begin and end are equal. There are no values in the row, Move to the next row + continue; + } + + for (var j = startIndex; j < endIndex; j++) + { + if (i == aat._columnIndices[j]) + { + norm += Math.Abs(aat._nonZeroValues[j]); + } + } + } + + norm = Math.Sqrt(norm); + return norm; + } + + /// Calculates the infinity norm of this matrix. + /// The infinity norm of this matrix. + public override double InfinityNorm() + { + var norm = 0.0; + for (var i = 0; i < _rowIndex.Length; i++) + { + // Get the begin / end index for the current row + var startIndex = _rowIndex[i]; + var endIndex = i < _rowIndex.Length - 1 ? _rowIndex[i + 1] : NonZerosCount; + + // Get the values for the current row + if (startIndex == endIndex) + { + // Begin and end are equal. There are no values in the row, Move to the next row + continue; + } + + var s = 0.0; + for (var j = startIndex; j < endIndex; j++) + { + s += Math.Abs(_nonZeroValues[j]); + } + + norm = Math.Max(norm, s); + } + + return norm; + } + + /// + /// Copies the requested row elements into a new . + /// + /// The row to copy elements from. + /// The column to start copying from. + /// The number of elements to copy. + /// The to copy the column into. + /// If the result is . + /// If is negative, + /// or greater than or equal to the number of columns. + /// If is negative, + /// or greater than or equal to the number of rows. + /// If + + /// is greater than or equal to the number of rows. + /// If is not positive. + /// If result.Count < length. + public override void Row(int rowIndex, int columnIndex, int length, Vector result) + { + if (result == null) + { + throw new ArgumentNullException("result"); + } + + if (rowIndex >= RowCount || rowIndex < 0) + { + throw new ArgumentOutOfRangeException("rowIndex"); + } + + if (columnIndex >= ColumnCount || columnIndex < 0) + { + throw new ArgumentOutOfRangeException("columnIndex"); + } + + if (columnIndex + length > ColumnCount) + { + throw new ArgumentOutOfRangeException("length"); + } + + if (length < 1) + { + throw new ArgumentException(Resources.ArgumentMustBePositive, "length"); + } + + if (result.Count < length) + { + throw new ArgumentException(Resources.ArgumentVectorsSameLength, "result"); + } + + // Determine bounds in columnIndices array where this item should be searched (using rowIndex) + var startIndex = _rowIndex[rowIndex]; + var endIndex = rowIndex < _rowIndex.Length - 1 ? _rowIndex[rowIndex + 1] : NonZerosCount; + + if (startIndex == endIndex) + { + result.Clear(); + } + else + { + // If there are non-zero elements use base class implementation + for (int i = columnIndex, j = 0; i < columnIndex + length; i++, j++) + { + // Copy code from At(row, column) to avoid unnecessary lock + var index = FindItem(rowIndex, i); + result[j] = index >= 0 ? _nonZeroValues[index] : 0.0f; + } + } + } + + /// + /// Diagonally stacks this matrix on top of the given matrix and places the combined matrix into the result matrix. + /// + /// The lower, right matrix. + /// The combined matrix + /// If lower is . + /// If the result matrix is . + /// If the result matrix's dimensions are not (Rows + lower.rows) x (Columns + lower.Columns). + public override void DiagonalStack(Matrix lower, Matrix result) + { + var lowerSparseMatrix = lower as SparseMatrix; + var resultSparseMatrix = result as SparseMatrix; + + if ((lowerSparseMatrix == null) || (resultSparseMatrix == null)) + { + base.DiagonalStack(lower, result); + } + else + { + if (resultSparseMatrix.RowCount != RowCount + lowerSparseMatrix.RowCount || resultSparseMatrix.ColumnCount != ColumnCount + lowerSparseMatrix.ColumnCount) + { + throw new ArgumentException(Resources.ArgumentMatrixDimensions, "result"); + } + + resultSparseMatrix.NonZerosCount = NonZerosCount + lowerSparseMatrix.NonZerosCount; + resultSparseMatrix._nonZeroValues = new float[resultSparseMatrix.NonZerosCount]; + resultSparseMatrix._columnIndices = new int[resultSparseMatrix.NonZerosCount]; + + Array.Copy(_nonZeroValues, 0, resultSparseMatrix._nonZeroValues, 0, NonZerosCount); + Array.Copy(lowerSparseMatrix._nonZeroValues, 0, resultSparseMatrix._nonZeroValues, NonZerosCount, lowerSparseMatrix.NonZerosCount); + + Array.Copy(_columnIndices, 0, resultSparseMatrix._columnIndices, 0, NonZerosCount); + Array.Copy(_rowIndex, 0, resultSparseMatrix._rowIndex, 0, RowCount); + + // Copy and adjust lower column indices and rowIndex + for (int i = NonZerosCount, j = 0; i < resultSparseMatrix.NonZerosCount; i++, j++) + { + resultSparseMatrix._columnIndices[i] = lowerSparseMatrix._columnIndices[j] + ColumnCount; + } + + for (int i = RowCount, j = 0; i < resultSparseMatrix.RowCount; i++, j++) + { + resultSparseMatrix._rowIndex[i] = lowerSparseMatrix._rowIndex[j] + NonZerosCount; + } + } + } + + #region Elementary operations + + /// + /// Adds another matrix to this matrix. The result will be written into this matrix. + /// + /// The matrix to add to this matrix. + /// If the other matrix is . + /// If the two matrices don't have the same dimensions. + public override void Add(Matrix other) + { + if (ReferenceEquals(this, other)) + { + Multiply(2); + return; + } + + var m = other as SparseMatrix; + if (m == null) + { + base.Add(other); + } + else + { + Add(m); + } + } + + /// + /// Adds another to this matrix. The result will be written into this matrix. + /// + /// The to add to this matrix. + /// If the other matrix is . + /// If the two matrices don't have the same dimensions. + public void Add(SparseMatrix other) + { + if (other == null) + { + throw new ArgumentNullException("other"); + } + + if (other.RowCount != RowCount || other.ColumnCount != ColumnCount) + { + throw new ArgumentOutOfRangeException(Resources.ArgumentMatrixDimensions); + } + + for (var i = 0; i < other.RowCount; i++) + { + // Get the begin / end index for the current row + var startIndex = other._rowIndex[i]; + var endIndex = i < other._rowIndex.Length - 1 ? other._rowIndex[i + 1] : other.NonZerosCount; + + for (var j = startIndex; j < endIndex; j++) + { + var index = FindItem(i, other._columnIndices[j]); + if (index >= 0) + { + if (_nonZeroValues[index] + other._nonZeroValues[j] == 0.0) + { + DeleteItemByIndex(index, i); + } + else + { + _nonZeroValues[index] += other._nonZeroValues[j]; + } + } + else + { + SetValueAt(i, other._columnIndices[j], other._nonZeroValues[j]); + } + } + } + } + + /// + /// Subtracts another matrix from this matrix. The result will be written into this matrix. + /// + /// The matrix to subtract. + /// If the other matrix is . + /// If the two matrices don't have the same dimensions. + public override void Subtract(Matrix other) + { + // We are substracting Matrix form itself + if (ReferenceEquals(this, other)) + { + Clear(); + return; + } + + var m = other as SparseMatrix; + if (m == null) + { + base.Subtract(other); + } + else + { + Subtract(m); + } + } + + /// + /// Subtracts another from this matrix. The result will be written into this matrix. + /// + /// The to subtract. + /// If the other matrix is . + /// If the two matrices don't have the same dimensions. + public void Subtract(SparseMatrix other) + { + if (other == null) + { + throw new ArgumentNullException("other"); + } + + if (other.RowCount != RowCount || other.ColumnCount != ColumnCount) + { + throw new ArgumentOutOfRangeException(Resources.ArgumentMatrixDimensions); + } + + for (var i = 0; i < other.RowCount; i++) + { + // Get the begin / end index for the current row + var startIndex = other._rowIndex[i]; + var endIndex = i < other._rowIndex.Length - 1 ? other._rowIndex[i + 1] : other.NonZerosCount; + + for (var j = startIndex; j < endIndex; j++) + { + var index = FindItem(i, other._columnIndices[j]); + if (index >= 0) + { + if (_nonZeroValues[index] - other._nonZeroValues[j] == 0.0) + { + DeleteItemByIndex(index, i); + } + else + { + _nonZeroValues[index] -= other._nonZeroValues[j]; + } + } + else + { + SetValueAt(i, other._columnIndices[j], -other._nonZeroValues[j]); + } + } + } + } + + /// + /// Multiplies each element of this matrix with a scalar. + /// + /// The scalar to multiply with. + public override void Multiply(float scalar) + { + if (1.0f.AlmostEqualInDecimalPlaces(scalar, 7)) + { + return; + } + + if (0.0f.AlmostEqualInDecimalPlaces(scalar, 7)) + { + Clear(); + return; + } + + Control.LinearAlgebraProvider.ScaleArray(scalar, _nonZeroValues); + } + + /// + /// Multiplies this sparse matrix with another sparse matrix and places the results into the result sparse matrix. + /// + /// The matrix to multiply with. + /// The result of the multiplication. + /// If the other matrix is . + /// If the result matrix is . + /// If Columns != other.Rows. + /// If the result matrix's dimensions are not the Rows x other.Columns. + public override void Multiply(Matrix other, Matrix result) + { + var otherSparseMatrix = other as SparseMatrix; + var resultSparseMatrix = result as SparseMatrix; + if (otherSparseMatrix == null || resultSparseMatrix == null) + { + base.Multiply(other, result); + return; + } + + if (ColumnCount != otherSparseMatrix.RowCount) + { + throw new ArgumentException(Resources.ArgumentMatrixDimensions); + } + + if (resultSparseMatrix.RowCount != RowCount || resultSparseMatrix.ColumnCount != otherSparseMatrix.ColumnCount) + { + throw new ArgumentException(Resources.ArgumentMatrixDimensions); + } + + resultSparseMatrix.Clear(); + var columnVector = new DenseVector(otherSparseMatrix.RowCount); + for (var row = 0; row < RowCount; row++) + { + // Get the begin / end index for the current row + var startIndex = _rowIndex[row]; + var endIndex = row < _rowIndex.Length - 1 ? _rowIndex[row + 1] : NonZerosCount; + if (startIndex == endIndex) + { + continue; + } + + for (var column = 0; column < otherSparseMatrix.ColumnCount; column++) + { + // Multiply row of matrix A on column of matrix B + otherSparseMatrix.Column(column, columnVector); + var sum = CommonParallel.Aggregate( + startIndex, + endIndex, + index => _nonZeroValues[index] * columnVector[_columnIndices[index]]); + resultSparseMatrix.SetValueAt(row, column, sum); + } + } + } + + /// + /// Multiplies this matrix with another matrix and returns the result. + /// + /// The matrix to multiply with. + /// If Columns != other.Rows. + /// If the other matrix is . + /// The result of multiplication. + public override Matrix Multiply(Matrix other) + { + var matrix = other as SparseMatrix; + if (matrix == null) + { + return base.Multiply(other); + } + + if (ColumnCount != matrix.RowCount) + { + throw new ArgumentException(Resources.ArgumentMatrixDimensions); + } + + var result = (SparseMatrix)CreateMatrix(RowCount, matrix.ColumnCount); + Multiply(matrix, result); + return result; + } + + /// + /// Multiplies this dense matrix with transpose of another dense matrix and places the results into the result dense matrix. + /// + /// The matrix to multiply with. + /// The result of the multiplication. + /// If the other matrix is . + /// If the result matrix is . + /// If this.Columns != other.Rows. + /// If the result matrix's dimensions are not the this.Rows x other.Columns. + public override void TransposeAndMultiply(Matrix other, Matrix result) + { + var otherSparse = other as SparseMatrix; + var resultSparse = result as SparseMatrix; + + if (otherSparse == null || resultSparse == null) + { + base.TransposeAndMultiply(other, result); + return; + } + + if (ColumnCount != otherSparse.ColumnCount) + { + throw new ArgumentException(Resources.ArgumentMatrixDimensions); + } + + if ((resultSparse.RowCount != RowCount) || (resultSparse.ColumnCount != otherSparse.RowCount)) + { + throw new ArgumentException(Resources.ArgumentMatrixDimensions); + } + + resultSparse.Clear(); + for (var j = 0; j < RowCount; j++) + { + // Get the begin / end index for the row + var startIndexOther = otherSparse._rowIndex[j]; + var endIndexOther = j < otherSparse._rowIndex.Length - 1 ? otherSparse._rowIndex[j + 1] : otherSparse.NonZerosCount; + if (startIndexOther == endIndexOther) + { + continue; + } + + for (var i = 0; i < RowCount; i++) + { + // Multiply row of matrix A on row of matrix B + // Get the begin / end index for the row + var startIndexThis = _rowIndex[i]; + var endIndexThis = i < _rowIndex.Length - 1 ? _rowIndex[i + 1] : NonZerosCount; + if (startIndexThis == endIndexThis) + { + continue; + } + + var i1 = i; + var sum = CommonParallel.Aggregate( + startIndexOther, + endIndexOther, + index => + { + var ind = FindItem(i1, otherSparse._columnIndices[index]); + return ind >= 0 ? otherSparse._nonZeroValues[index] * _nonZeroValues[ind] : 0.0f; + }); + + resultSparse.SetValueAt(i, j, sum + result.At(i, j)); + } + } + } + + /// + /// Multiplies this matrix with transpose of another matrix and returns the result. + /// + /// The matrix to multiply with. + /// If this.Columns != other.Rows. + /// If the other matrix is . + /// The result of multiplication. + public override Matrix TransposeAndMultiply(Matrix other) + { + var otherSparse = other as SparseMatrix; + if (otherSparse == null) + { + return base.TransposeAndMultiply(other); + } + + if (ColumnCount != otherSparse.ColumnCount) + { + throw new ArgumentException(Resources.ArgumentMatrixDimensions); + } + + var result = (SparseMatrix)CreateMatrix(RowCount, other.RowCount); + TransposeAndMultiply(other, result); + return result; + } + + /// + /// Multiplies two sparse matrices. + /// + /// The left matrix to multiply. + /// The right matrix to multiply. + /// The result of multiplication. + /// If or is . + /// If the dimensions of or don't conform. + public static SparseMatrix operator *(SparseMatrix leftSide, SparseMatrix 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 (SparseMatrix)leftSide.Multiply(rightSide); + } + + /// + /// Pointwise multiplies this matrix with another matrix and stores the result into the result matrix. + /// + /// The matrix to pointwise multiply with this one. + /// The matrix to store the result of the pointwise multiplication. + /// If the other matrix is . + /// If the result matrix is . + /// If this matrix and are not the same size. + /// If this matrix and are not the same size. + public override void PointwiseMultiply(Matrix other, Matrix result) + { + if (other == null) + { + throw new ArgumentNullException("other"); + } + + if (result == null) + { + throw new ArgumentNullException("result"); + } + + if (ColumnCount != other.ColumnCount || RowCount != other.RowCount) + { + throw new ArgumentException(Resources.ArgumentMatrixDimensions, "result"); + } + + if (ColumnCount != result.ColumnCount || RowCount != result.RowCount) + { + throw new ArgumentException(Resources.ArgumentMatrixDimensions, "result"); + } + + result.Clear(); + for (var i = 0; i < other.RowCount; i++) + { + // Get the begin / end index for the current row + var startIndex = _rowIndex[i]; + var endIndex = i < _rowIndex.Length - 1 ? _rowIndex[i + 1] : NonZerosCount; + + for (var j = startIndex; j < endIndex; j++) + { + var resVal = _nonZeroValues[j] * other[i, _columnIndices[j]]; + if (resVal != 0.0) + { + result[i, _columnIndices[j]] = resVal; + } + } + } + } + + /// + /// Generates matrix with random elements. + /// + /// Number of rows. + /// Number of columns. + /// Continuous Random Distribution or Source + /// + /// An numberOfRows-by-numberOfColumns matrix with elements distributed according to the provided distribution. + /// + /// If the parameter is not positive. + /// If the parameter is not positive. + public override Matrix 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 = (SparseMatrix)CreateMatrix(numberOfRows, numberOfColumns); + for (var i = 0; i < matrix.RowCount; i++) + { + for (var j = 0; j < matrix.ColumnCount; j++) + { + var value = (float)distribution.Sample(); + if (value != 0.0) + { + matrix.SetValueAt(i, j, value); + } + } + } + + return matrix; + } + + /// + /// Generates matrix with random elements. + /// + /// Number of rows. + /// Number of columns. + /// Continuous Random Distribution or Source + /// + /// An numberOfRows-by-numberOfColumns matrix with elements distributed according to the provided distribution. + /// + /// If the parameter is not positive. + /// If the parameter is not positive. + public override Matrix 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 = (SparseMatrix)CreateMatrix(numberOfRows, numberOfColumns); + for (var i = 0; i < matrix.RowCount; i++) + { + for (var j = 0; j < matrix.ColumnCount; j++) + { + var value = distribution.Sample(); + if (value != 0.0) + { + matrix.SetValueAt(i, j, value); + } + } + } + + return matrix; + } + + #endregion + + #region Static constructors for special matrices. + /// + /// Initializes a square with all zero's except for ones on the diagonal. + /// + /// the size of the square matrix. + /// Identity SparseMatrix + /// + /// If is less than one. + /// + public static SparseMatrix Identity(int order) + { + var m = new SparseMatrix(order) + { + NonZerosCount = order, + _nonZeroValues = new float[order], + _columnIndices = new int[order] + }; + + for (var i = 0; i < order; i++) + { + m._nonZeroValues[i] = 1.0f; + m._columnIndices[i] = i; + m._rowIndex[i] = i; + } + + return m; + } + #endregion + + /// + /// Negates each element of this matrix. + /// + public override void Negate() + { + Multiply(-1); + } + + /// + /// Indicates whether the current object is equal to another object of the same type. + /// + /// + /// An object to compare with this object. + /// + /// + /// true if the current object is equal to the parameter; otherwise, false. + /// + public override bool Equals(Matrix other) + { + if (other == null) + { + return false; + } + + if (ColumnCount != other.ColumnCount || RowCount != other.RowCount) + { + return false; + } + + // Accept if the argument is the same object as this. + if (ReferenceEquals(this, other)) + { + return true; + } + + var sparseMatrix = other as SparseMatrix; + + if (sparseMatrix == null) + { + return base.Equals(other); + } + + if (NonZerosCount != sparseMatrix.NonZerosCount) + { + return false; + } + + // If all else fails, perform element wise comparison. + for (var index = 0; index < NonZerosCount; index++) + { + if (!_nonZeroValues[index].AlmostEqual(sparseMatrix._nonZeroValues[index]) || _columnIndices[index] != sparseMatrix._columnIndices[index]) + { + return false; + } + } + + return true; + } + + /// + /// Returns a that represents this instance. + /// + /// + /// The format to use. + /// + /// + /// The format provider to use. + /// + /// + /// A that represents this instance. + /// + public override string ToString(string format, IFormatProvider formatProvider) + { + var stringBuilder = new StringBuilder(); + for (var row = 0; row < RowCount; row++) + { + for (var column = 0; column < ColumnCount; column++) + { + stringBuilder.Append(At(row, column).ToString(format, formatProvider)); + if (column != ColumnCount - 1) + { + stringBuilder.Append(formatProvider.GetTextInfo().ListSeparator); + } + } + + if (row != RowCount - 1) + { + stringBuilder.Append(Environment.NewLine); + } + } + + return stringBuilder.ToString(); + } + + #region Simple arithmetic of type T + /// + /// Add two values T+T + /// + /// Left operand value + /// Right operand value + /// Result of addition + protected sealed override float AddT(float val1, float val2) + { + return val1 + val2; + } + + /// + /// Subtract two values T-T + /// + /// Left operand value + /// Right operand value + /// Result of subtract + protected sealed override float SubtractT(float val1, float val2) + { + return val1 - val2; + } + + /// + /// Multiply two values T*T + /// + /// Left operand value + /// Right operand value + /// Result of multiplication + protected sealed override float MultiplyT(float val1, float val2) + { + return val1 * val2; + } + + /// + /// Divide two values T/T + /// + /// Left operand value + /// Right operand value + /// Result of divide + protected sealed override float DivideT(float val1, float val2) + { + return val1 / val2; + } + + /// + /// Take absolute value + /// + /// Source alue + /// True if one; otherwise false + protected sealed override double AbsoluteT(float val1) + { + return Math.Abs(val1); + } + #endregion + } +} diff --git a/src/Numerics/LinearAlgebra/Single/SparseVector.cs b/src/Numerics/LinearAlgebra/Single/SparseVector.cs new file mode 100644 index 00000000..9193bd89 --- /dev/null +++ b/src/Numerics/LinearAlgebra/Single/SparseVector.cs @@ -0,0 +1,1683 @@ +// +// 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. +// + +namespace MathNet.Numerics.LinearAlgebra.Single +{ + using System; + using System.Collections.Generic; + using System.Globalization; + using System.Linq; + using Distributions; + using Generic; + using NumberTheory; + using Properties; + using Threading; + + /// + /// A vector with sparse storage. + /// + public class SparseVector : Vector + { + /// + /// Lock object for the indexer. + /// + private readonly object _lockObject = new object(); + + /// + /// Gets the vector's internal data. The array containing the actual values; only the non-zero values are stored. + /// + private float[] _nonZeroValues = new float[0]; + + /// + /// The indices of the non-zero entries. + /// + private int[] _nonZeroIndices = new int[0]; + + /// + /// Gets the number of non zero elements in the vector. + /// + /// The number of non zero elements. + public int NonZerosCount + { + get; + private set; + } + + #region Constructors + + /// + /// Initializes a new instance of the class with a given size. + /// + /// + /// the size of the vector. + /// + /// + /// If is less than one. + /// + public SparseVector(int size) : base(size) + { + } + + /// + /// Initializes a new instance of the class with a given size + /// and each element set to the given value; + /// + /// + /// the size of the vector. + /// + /// + /// the value to set each element to. + /// + /// + /// If is less than one. + /// + public SparseVector(int size, float value) : this(size) + { + if (value == 0.0) + { + // Skip adding values + return; + } + + // We already know that this vector is "full", let's allocate all needed memory + _nonZeroValues = new float[size]; + _nonZeroIndices = new int[size]; + NonZerosCount = size; + + CommonParallel.For( + 0, + Count, + index => + { + _nonZeroValues[index] = value; + _nonZeroIndices[index] = index; + }); + } + + /// + /// Initializes a new instance of the class by + /// copying the values from another. + /// + /// + /// The vector to create the new vector from. + /// + public SparseVector(Vector other) : this(other.Count) + { + var vector = other as SparseVector; + if (vector == null) + { + for (var i = 0; i < other.Count; i++) + { + this[i] = other[i]; + } + } + else + { + _nonZeroValues = new float[vector.NonZerosCount]; + _nonZeroIndices = new int[vector.NonZerosCount]; + NonZerosCount = vector.NonZerosCount; + + // Lets copy only needed data. Portion of needed data is determined by NonZerosCount value + Buffer.BlockCopy(vector._nonZeroValues, 0, _nonZeroValues, 0, vector.NonZerosCount * Constants.SizeOfFloat); + Buffer.BlockCopy(vector._nonZeroIndices, 0, _nonZeroIndices, 0, vector.NonZerosCount * Constants.SizeOfInt); + } + } + + /// + /// Initializes a new instance of the class by + /// copying the values from another. + /// + /// + /// The vector to create the new vector from. + /// + public SparseVector(SparseVector other) : this(other.Count) + { + // Lets copy only needed data. Portion of needed data is determined by NonZerosCount value + _nonZeroValues = new float[other.NonZerosCount]; + _nonZeroIndices = new int[other.NonZerosCount]; + NonZerosCount = other.NonZerosCount; + + Buffer.BlockCopy(other._nonZeroValues, 0, _nonZeroValues, 0, other.NonZerosCount * Constants.SizeOfFloat); + Buffer.BlockCopy(other._nonZeroIndices, 0, _nonZeroIndices, 0, other.NonZerosCount * Constants.SizeOfInt); + } + + /// + /// Initializes a new instance of the class for an array. + /// + /// The array to create this vector from. + /// The vector copy the array. Any changes to the vector will NOT change the array. + public SparseVector(float[] array) : this(array.Length) + { + for (var i = 0; i < array.Length; i++) + { + this[i] = array[i]; + } + } + + #endregion + + /// + /// Create a matrix based on this vector in column form (one single column). + /// + /// This vector as a column matrix. + public override Matrix ToColumnMatrix() + { + var matrix = new SparseMatrix(Count, 1); + for (var i = 0; i < NonZerosCount; i++) + { + matrix[_nonZeroIndices[i], 0] = _nonZeroValues[i]; + } + + return matrix; + } + + /// + /// Create a matrix based on this vector in row form (one single row). + /// + /// This vector as a row matrix. + public override Matrix ToRowMatrix() + { + var matrix = new SparseMatrix(1, Count); + for (var i = 0; i < NonZerosCount; i++) + { + matrix[0, _nonZeroIndices[i]] = _nonZeroValues[i]; + } + + return matrix; + } + + /// Gets or sets the value at the given . + /// The index of the value to get or set. + /// The value of the vector at the given . + /// If is negative or + /// greater than the size of the vector. + public override float this[int index] + { + get + { + // If index is out of bounds + if ((index < 0) || (index >= Count)) + { + throw new IndexOutOfRangeException(); + } + + lock (_lockObject) + { + // Search if item idex exists in NonZeroIndices array in range "0 - real nonzero values count" + var itemIndex = Array.BinarySearch(_nonZeroIndices, 0, NonZerosCount, index); + if (itemIndex >= 0) + { + return _nonZeroValues[itemIndex]; + } + } + + return 0.0f; + } + + set + { + // If index is out of bounds + if ((index < 0) || (index >= Count)) + { + throw new IndexOutOfRangeException(); + } + + lock (_lockObject) + { + SetValue(index, value); + } + } + } + + /// + /// Creates a matrix with the given dimensions using the same storage type + /// as this vector. + /// + /// + /// The number of rows. + /// + /// + /// The number of columns. + /// + /// + /// A matrix with the given dimensions. + /// + public override Matrix CreateMatrix(int rows, int columns) + { + return new SparseMatrix(rows, columns); + } + + /// + /// Creates a Vector of the given size using the same storage type + /// as this vector. + /// + /// + /// The size of the Vector to create. + /// + /// + /// The new Vector. + /// + public override Vector CreateVector(int size) + { + return new SparseVector(size); + } + + /// + /// Resets all values to zero. + /// + public override void Clear() + { + NonZerosCount = 0; + } + + /// + /// Copies the values of this vector into the target vector. + /// + /// + /// The vector to copy elements into. + /// + /// + /// If is . + /// + /// + /// If is not the same size as this vector. + /// + public override void CopyTo(Vector target) + { + if (target == null) + { + throw new ArgumentNullException("target"); + } + + if (Count != target.Count) + { + throw new ArgumentException(Resources.ArgumentVectorsSameLength, "target"); + } + + if (ReferenceEquals(this, target)) + { + return; + } + + var otherVector = target as SparseVector; + if (otherVector == null) + { + CommonParallel.For( + 0, + Count, + index => target[index] = this[index]); + } + else + { + // Lets copy only needed data. Portion of needed data is determined by NonZerosCount value + otherVector._nonZeroValues = new float[NonZerosCount]; + otherVector._nonZeroIndices = new int[NonZerosCount]; + otherVector.NonZerosCount = NonZerosCount; + + Buffer.BlockCopy(_nonZeroValues, 0, otherVector._nonZeroValues, 0, NonZerosCount * Constants.SizeOfFloat); + Buffer.BlockCopy(_nonZeroIndices, 0, otherVector._nonZeroIndices, 0, NonZerosCount * Constants.SizeOfInt); + } + } + + #region Operators and supplementary functions + + /// + /// Adds a scalar to each element of the vector. + /// + /// The scalar to add. + /// A copy of the vector with the scalar added. + public override Vector Add(float scalar) + { + if (scalar == 0.0) + { + return Clone(); + } + + var copy = (SparseVector)Clone(); + for (var i = 0; i < Count; i++) + { + copy[i] += scalar; + } + + return copy; + } + + /// + /// Adds a scalar to each element of the vector and stores the result in the result vector. + /// + /// The scalar to add. + /// The vector to store the result of the addition. + /// If the result vector is . + /// If this vector and are not the same size. + public override void Add(float scalar, Vector result) + { + if (result == null) + { + throw new ArgumentNullException("result"); + } + + if (Count != result.Count) + { + throw new ArgumentException(Resources.ArgumentVectorsSameLength, "result"); + } + + base.Add(scalar, result); + } + + /// + /// Adds another vector to this vector. + /// + /// The vector to add to this one. + /// A new vector containing the sum of both vectors. + /// If the other vector is . + /// If this vector and are not the same size. + public override Vector Add(Vector other) + { + if (other == null) + { + throw new ArgumentNullException("other"); + } + + if (Count != other.Count) + { + throw new ArgumentException(Resources.ArgumentVectorsSameLength, "other"); + } + + var sparseVector = other as SparseVector; + + if (sparseVector == null) + { + return base.Add(other); + } + + var copy = (SparseVector)Clone(); + copy.AddScaledSparseVector(1.0f, sparseVector); + return copy; + } + + /// + /// Adds the scaled sparse vector. + /// + /// The alpha. + /// The other. + private void AddScaledSparseVector(float alpha, SparseVector other) + { + if (other == null) + { + throw new ArgumentNullException("other"); + } + + if (Count != other.Count) + { + throw new ArgumentException(Resources.ArgumentVectorsSameLength, "other"); + } + + if (alpha == 0.0) + { + return; + } + + // I don't use ILinearAlgebraProvider because we will get no benefit due to "lock" in this[index] + // Possible fucniton in ILinearAlgebraProvider may be AddSparseVectorToScaledSparseVector(T[] y, int[] yIndices, T alpha, T[] x, int[] xIndices); + // But it require to develop value setting algorithm and due to "lock" it will be even more greedy then implemented below + if (ReferenceEquals(this, other)) + { + // Adding the same instance of sparse vector. That means if we modify "this" then "other" will be modified too. + // To avoid such problem lets change values in internal storage of "this" + if (alpha == 1.0) + { + for (var i = 0; i < NonZerosCount; i++) + { + _nonZeroValues[i] += _nonZeroValues[i]; + } + } + else if (alpha == -1.0) + { + Clear(); // Vector is subtracted from itself + return; + } + else + { + for (var i = 0; i < NonZerosCount; i++) + { + _nonZeroValues[i] += alpha * _nonZeroValues[i]; + } + } + } + else + { + // "this" and "other" are different objects, so by modifying "this" the "other" object will not be changed + if (alpha == 1.0) + { + for (var i = 0; i < other.NonZerosCount; i++) + { + this[other._nonZeroIndices[i]] += other._nonZeroValues[i]; + } + } + else + { + for (var i = 0; i < other.NonZerosCount; i++) + { + this[other._nonZeroIndices[i]] += alpha * other._nonZeroValues[i]; + } + } + } + } + + /// + /// Adds another vector to this vector and stores the result into the result vector. + /// + /// The vector to add to this one. + /// The vector to store the result of the addition. + /// If the other vector is . + /// If the result vector is . + /// If this vector and are not the same size. + /// If this vector and are not the same size. + public override void Add(Vector other, Vector result) + { + if (result == null) + { + throw new ArgumentNullException("result"); + } + + if (Count != other.Count) + { + throw new ArgumentException(Resources.ArgumentVectorsSameLength, "other"); + } + + if (Count != result.Count) + { + throw new ArgumentException(Resources.ArgumentVectorsSameLength, "result"); + } + + if (ReferenceEquals(this, result) || ReferenceEquals(other, result)) + { + var tmp = Add(other); + tmp.CopyTo(result); + } + else + { + var sparse = result as SparseVector; + if (sparse == null) + { + base.Add(other, result); + } + else + { + var sparseother = other as SparseVector; + if (sparseother == null) + { + sparse.AddScaledSparseVector(1.0f, sparseother); + } + else + { + base.Add(other, result); + } + } + } + } + + /// + /// Returns a Vector containing the same values of . + /// + /// This method is included for completeness. + /// The vector to get the values from. + /// A vector containing a the same values as . + /// If is . + public static Vector operator +(SparseVector rightSide) + { + if (rightSide == null) + { + throw new ArgumentNullException("rightSide"); + } + + return rightSide.Plus(); + } + + /// + /// Adds two Vectors together and returns the results. + /// + /// One of the vectors to add. + /// The other vector to add. + /// The result of the addition. + /// If and are not the same size. + /// If or is . + public static Vector operator +(SparseVector leftSide, SparseVector rightSide) + { + if (rightSide == null) + { + throw new ArgumentNullException("rightSide"); + } + + if (leftSide == null) + { + throw new ArgumentNullException("leftSide"); + } + + if (leftSide.Count != rightSide.Count) + { + throw new ArgumentException(Resources.ArgumentVectorsSameLength, "rightSide"); + } + + return leftSide.Add(rightSide); + } + + /// + /// Subtracts a scalar from each element of the vector. + /// + /// The scalar to subtract. + /// A new vector containing the subtraction of this vector and the scalar. + public override Vector Subtract(float scalar) + { + if (scalar == 0.0) + { + return Clone(); + } + + var copy = (SparseVector)Clone(); + for (var i = 0; i < Count; i++) + { + copy[i] -= scalar; + } + + return copy; + } + + /// + /// Subtracts a scalar from each element of the vector and stores the result in the result vector. + /// + /// The scalar to subtract. + /// The vector to store the result of the subtraction. + /// If the result vector is . + /// If this vector and are not the same size. + public override void Subtract(float scalar, Vector result) + { + if (result == null) + { + throw new ArgumentNullException("result"); + } + + if (Count != result.Count) + { + throw new ArgumentException(Resources.ArgumentVectorsSameLength, "result"); + } + + base.Subtract(scalar, result); + } + + /// + /// Subtracts another vector from this vector. + /// + /// The vector to subtract from this one. + /// A new vector containing the subtraction of the the two vectors. + /// If the other vector is . + /// If this vector and are not the same size. + public override Vector Subtract(Vector other) + { + if (other == null) + { + throw new ArgumentNullException("other"); + } + + if (Count != other.Count) + { + throw new ArgumentException(Resources.ArgumentVectorsSameLength, "other"); + } + + var sparseVector = other as SparseVector; + + if (sparseVector == null) + { + return base.Subtract(other); + } + + var copy = (SparseVector)Clone(); + copy.AddScaledSparseVector(-1.0f, sparseVector); + return copy; + } + + /// + /// Subtracts another vector to this vector and stores the result into the result vector. + /// + /// The vector to subtract from this one. + /// The vector to store the result of the subtraction. + /// If the other vector is . + /// If the result vector is . + /// If this vector and are not the same size. + /// If this vector and are not the same size. + public override void Subtract(Vector other, Vector result) + { + if (result == null) + { + throw new ArgumentNullException("result"); + } + + if (Count != other.Count) + { + throw new ArgumentException(Resources.ArgumentVectorsSameLength, "other"); + } + + if (Count != result.Count) + { + throw new ArgumentException(Resources.ArgumentVectorsSameLength, "result"); + } + + if (ReferenceEquals(this, result) || ReferenceEquals(other, result)) + { + var tmp = Subtract(other); + tmp.CopyTo(result); + } + else + { + var sparse = result as SparseVector; + if (sparse == null) + { + base.Subtract(other, result); + } + else + { + var sparseother = other as SparseVector; + if (sparseother == null) + { + sparse.AddScaledSparseVector(-1.0f, sparseother); + } + else + { + base.Subtract(other, result); + } + } + } + } + + /// + /// Returns a Vector containing the negated values of . + /// + /// The vector to get the values from. + /// A vector containing the negated values as . + /// If is . + public static Vector operator -(SparseVector rightSide) + { + if (rightSide == null) + { + throw new ArgumentNullException("rightSide"); + } + + return rightSide.Negate(); + } + + /// + /// Subtracts two Vectors and returns the results. + /// + /// The vector to subtract from. + /// The vector to subtract. + /// The result of the subtraction. + /// If and are not the same size. + /// If or is . + public static Vector operator -(SparseVector leftSide, SparseVector rightSide) + { + if (rightSide == null) + { + throw new ArgumentNullException("rightSide"); + } + + if (leftSide == null) + { + throw new ArgumentNullException("leftSide"); + } + + if (leftSide.Count != rightSide.Count) + { + throw new ArgumentException(Resources.ArgumentVectorsSameLength, "rightSide"); + } + + return leftSide.Subtract(rightSide); + } + + /// + /// Returns a negated vector. + /// + /// The negated vector. + /// Added as an alternative to the unary negation operator. + public override Vector Negate() + { + var result = new SparseVector(Count) + { + _nonZeroValues = new float[NonZerosCount], + _nonZeroIndices = new int[NonZerosCount], + NonZerosCount = NonZerosCount + }; + + if (NonZerosCount != 0) + { + CommonParallel.For( + 0, + NonZerosCount, + index => result._nonZeroValues[index] = -_nonZeroValues[index]); + Buffer.BlockCopy(_nonZeroIndices, 0, result._nonZeroIndices, 0, NonZerosCount * Constants.SizeOfInt); + } + + return result; + } + + /// + /// Multiplies a scalar to each element of the vector. + /// + /// The scalar to multiply. + /// A new vector that is the multiplication of the vector and the scalar. + public override Vector Multiply(float scalar) + { + if (scalar == 1.0) + { + return Clone(); + } + + if (scalar == 0) + { + var copy = Clone(); + copy.Clear(); // Set array empty + return copy; + } + else + { + var copy = (SparseVector)Clone(); + Control.LinearAlgebraProvider.ScaleArray(scalar, copy._nonZeroValues); + return copy; + } + } + + /// + /// Computes the dot product between this vector and another vector. + /// + /// The other vector to add. + /// The result of the addition. + /// If is not of the same size. + /// If is . + public override float DotProduct(Vector other) + { + if (other == null) + { + throw new ArgumentNullException("other"); + } + + if (Count != other.Count) + { + throw new ArgumentException(Resources.ArgumentVectorsSameLength, "other"); + } + + float result = 0; + + // base implementation iterates though all elements, but we need only take non-zeros + for (var i = 0; i < NonZerosCount; i++) + { + result += _nonZeroValues[i] * other[_nonZeroIndices[i]]; + } + + return result; + } + + /// + /// Multiplies a vector with a scalar. + /// + /// The vector to scale. + /// The scalar value. + /// The result of the multiplication. + /// If is . + public static SparseVector operator *(SparseVector leftSide, float rightSide) + { + if (leftSide == null) + { + throw new ArgumentNullException("leftSide"); + } + + return (SparseVector)leftSide.Multiply(rightSide); + } + + /// + /// Multiplies a vector with a scalar. + /// + /// The scalar value. + /// The vector to scale. + /// The result of the multiplication. + /// If is . + public static SparseVector operator *(float leftSide, SparseVector rightSide) + { + if (rightSide == null) + { + throw new ArgumentNullException("rightSide"); + } + + return (SparseVector)rightSide.Multiply(leftSide); + } + + /// + /// Computes the dot product between two Vectors. + /// + /// The left row vector. + /// The right column vector. + /// The dot product between the two vectors. + /// If and are not the same size. + /// If or is . + public static float operator *(SparseVector leftSide, SparseVector rightSide) + { + if (rightSide == null) + { + throw new ArgumentNullException("rightSide"); + } + + if (leftSide == null) + { + throw new ArgumentNullException("leftSide"); + } + + if (leftSide.Count != rightSide.Count) + { + throw new ArgumentException(Resources.ArgumentVectorsSameLength, "rightSide"); + } + + return leftSide.DotProduct(rightSide); + } + + /// + /// Divides a vector with a scalar. + /// + /// The vector to divide. + /// The scalar value. + /// The result of the division. + /// If is . + public static SparseVector operator /(SparseVector leftSide, float rightSide) + { + if (leftSide == null) + { + throw new ArgumentNullException("leftSide"); + } + + return (SparseVector)leftSide.Multiply(1.0f / rightSide); + } + + /// + /// Returns the index of the absolute minimum element. + /// + /// The index of absolute minimum element. + public override int AbsoluteMinimumIndex() + { + if (NonZerosCount == 0) + { + // No non-zero elements. Return 0 + return 0; + } + + var index = 0; + var min = Math.Abs(_nonZeroValues[index]); + for (var i = 1; i < NonZerosCount; i++) + { + var test = Math.Abs(_nonZeroValues[i]); + if (test < min) + { + index = i; + min = test; + } + } + + return _nonZeroIndices[index]; + } + + /// + /// Creates a vector containing specified elements. + /// + /// The first element to begin copying from. + /// The number of elements to copy. + /// A vector containing a copy of the specified elements. + /// If is not positive or + /// greater than or equal to the size of the vector. + /// If + is greater than or equal to the size of the vector. + /// + /// If is not positive. + public override Vector SubVector(int index, int length) + { + if (index < 0 || index >= Count) + { + throw new ArgumentOutOfRangeException("index"); + } + + if (length <= 0) + { + throw new ArgumentOutOfRangeException("length"); + } + + if (index + length > Count) + { + throw new ArgumentOutOfRangeException("length"); + } + + var result = new SparseVector(length); + for (var i = index; i < index + length; i++) + { + result[i - index] = this[i]; + } + + return result; + } + + /// + /// Set the values of this vector to the given values. + /// + /// The array containing the values to use. + /// If is . + /// If is not the same size as this vector. + public override void SetValues(float[] values) + { + if (values == null) + { + throw new ArgumentNullException("values"); + } + + if (values.Length != Count) + { + throw new ArgumentException(Resources.ArgumentVectorsSameLength, "values"); + } + + for (var i = 0; i < values.Length; i++) + { + this[i] = values[i]; + } + } + + /// + /// Returns the index of the absolute maximum element. + /// + /// The index of absolute maximum element. + public override int MaximumIndex() + { + if (NonZerosCount == 0) + { + return 0; + } + + var index = 0; + var max = _nonZeroValues[0]; + for (var i = 1; i < NonZerosCount; i++) + { + if (max < _nonZeroValues[i]) + { + index = i; + max = _nonZeroValues[i]; + } + } + + return _nonZeroIndices[index]; + } + + /// + /// Returns the index of the minimum element. + /// + /// The index of minimum element. + public override int MinimumIndex() + { + if (NonZerosCount == 0) + { + return 0; + } + + var index = 0; + var min = _nonZeroValues[0]; + for (var i = 1; i < NonZerosCount; i++) + { + if (min > _nonZeroValues[i]) + { + index = i; + min = _nonZeroValues[i]; + } + } + + return _nonZeroIndices[index]; + } + + /// + /// Computes the sum of the vector's elements. + /// + /// The sum of the vector's elements. + public override float Sum() + { + float result = 0; + for (var i = 0; i < NonZerosCount; i++) + { + result += _nonZeroValues[i]; + } + + return result; + } + + /// + /// Computes the sum of the absolute value of the vector's elements. + /// + /// The sum of the absolute value of the vector's elements. + public override double SumMagnitudes() + { + double result = 0; + for (var i = 0; i < NonZerosCount; i++) + { + result += Math.Abs(_nonZeroValues[i]); + } + + return result; + } + + /// + /// Pointwise multiplies this vector with another vector. + /// + /// The vector to pointwise multiply with this one. + /// A new vector which is the pointwise multiplication of the two vectors. + /// If the other vector is . + /// If this vector and are not the same size. + public override Vector PointwiseMultiply(Vector other) + { + if (other == null) + { + throw new ArgumentNullException("other"); + } + + if (Count != other.Count) + { + throw new ArgumentException(Resources.ArgumentVectorsSameLength, "other"); + } + + var copy = new SparseVector(Count); + for (var i = 0; i < _nonZeroIndices.Length; i++) + { + var d = _nonZeroValues[i] * other[_nonZeroIndices[i]]; + if (d != 0.0) + { + copy[_nonZeroIndices[i]] = d; + } + } + + return copy; + } + + /// + /// Outer product of two vectors + /// + /// First vector + /// Second vector + /// Matrix M[i,j] = u[i]*v[j] + /// If the u vector is . + /// If the v vector is . + public static Matrix /*SparseMatrix*/ OuterProduct(SparseVector u, SparseVector v) + { + if (u == null) + { + throw new ArgumentNullException("u"); + } + + if (v == null) + { + throw new ArgumentNullException("v"); + } + + var matrix = new SparseMatrix(u.Count, v.Count); + for (var i = 0; i < u.NonZerosCount; i++) + { + for (var j = 0; j < v.NonZerosCount; j++) + { + if (u._nonZeroIndices[i] == v._nonZeroIndices[j]) + { + matrix.At(i, j, u._nonZeroValues[i] * v._nonZeroValues[j]); + } + } + } + + return matrix; + } + + /// + /// Generates a vector with random elements + /// + /// Number of elements in the vector. + /// Continuous Random Distribution or Source + /// + /// A vector with n-random elements distributed according + /// to the specified random distribution. + /// + /// If the length vector is non positive. + public override Vector Random(int length, IContinuousDistribution randomDistribution) + { + if (length < 1) + { + throw new ArgumentException(Resources.ArgumentMustBePositive, "length"); + } + + var v = (SparseVector)CreateVector(length); + for (var index = 0; index < v.Count; index++) + { + v[index] = (float)randomDistribution.Sample(); + } + + return v; + } + + /// + /// Generates a vector with random elements + /// + /// Number of elements in the vector. + /// Continuous Random Distribution or Source + /// + /// A vector with n-random elements distributed according + /// to the specified random distribution. + /// + /// If the n vector is non positive. + public override Vector Random(int length, IDiscreteDistribution randomDistribution) + { + if (length < 1) + { + throw new ArgumentException(Resources.ArgumentMustBePositive, "length"); + } + + var v = (SparseVector)CreateVector(length); + for (var index = 0; index < v.Count; index++) + { + v[index] = randomDistribution.Sample(); + } + + return v; + } + + /// + /// Tensor Product (Outer) of this and another vector. + /// + /// The vector to operate on. + /// + /// Matrix M[i,j] = this[i] * v[j]. + /// + /// + public Matrix TensorMultiply(SparseVector v) + { + return OuterProduct(this, v); + } + + #endregion + + #region Vector Norms + + /// + /// Computes the p-Norm. + /// + /// The p value. + /// Scalar ret = (sum(abs(this[i])^p))^(1/p) + public override double Norm(double p) + { + if (1 > p) + { + throw new ArgumentOutOfRangeException("p"); + } + + if (NonZerosCount == 0) + { + return 0.0; + } + + if (2.0 == p) + { + return _nonZeroValues.Aggregate(0f, SpecialFunctions.Hypotenuse); + } + + if (Double.IsPositiveInfinity(p)) + { + return CommonParallel.Select(0, NonZerosCount, (index, localData) => Math.Max(localData, Math.Abs(_nonZeroValues[index])), Math.Max); + } + + var sum = CommonParallel.Aggregate( + 0, + NonZerosCount, + index => Math.Pow(Math.Abs(_nonZeroValues[index]), p)); + + return Math.Pow(sum, 1.0 / p); + } + + /// + /// Normalizes this vector to a unit vector with respect to the p-norm. + /// + /// + /// The p value. + /// + /// + /// This vector normalized to a unit vector with respect to the p-norm. + /// + public override Vector Normalize(double p) + { + if (p < 0.0) + { + throw new ArgumentOutOfRangeException("p"); + } + + var norm = Norm(p); + var clone = Clone(); + if (norm == 0.0) + { + return clone; + } + + clone.Multiply(1.0f / (float)norm, clone); + + return clone; + } + #endregion + + #region Parse Functions + + /// + /// Creates a float sparse vector based on a string. The string can be in the following formats (without the + /// quotes): 'n', 'n,n,..', '(n,n,..)', '[n,n,...]', where n is a float. + /// + /// + /// A float sparse vector containing the values specified by the given string. + /// + /// + /// The string to parse. + /// + public static SparseVector Parse(string value) + { + return Parse(value, null); + } + + /// + /// Creates a float sparse vector based on a string. The string can be in the following formats (without the + /// quotes): 'n', 'n,n,..', '(n,n,..)', '[n,n,...]', where n is a float. + /// + /// + /// A float sparse vector containing the values specified by the given string. + /// + /// + /// the string to parse. + /// + /// + /// An that supplies culture-specific formatting information. + /// + public static SparseVector Parse(string value, IFormatProvider formatProvider) + { + if (value == null) + { + throw new ArgumentNullException(value); + } + + value = value.Trim(); + if (value.Length == 0) + { + throw new FormatException(); + } + + // strip out parens + if (value.StartsWith("(", StringComparison.Ordinal)) + { + if (!value.EndsWith(")", StringComparison.Ordinal)) + { + throw new FormatException(); + } + + value = value.Substring(1, value.Length - 2).Trim(); + } + + if (value.StartsWith("[", StringComparison.Ordinal)) + { + if (!value.EndsWith("]", StringComparison.Ordinal)) + { + throw new FormatException(); + } + + value = value.Substring(1, value.Length - 2).Trim(); + } + + // keywords + var textInfo = formatProvider.GetTextInfo(); + var keywords = new[] { textInfo.ListSeparator }; + + // lexing + var tokens = new LinkedList(); + GlobalizationHelper.Tokenize(tokens.AddFirst(value), keywords, 0); + var token = tokens.First; + + if (token == null || tokens.Count.IsEven()) + { + throw new FormatException(); + } + + // parsing + var data = new float[(tokens.Count + 1) >> 1]; + for (var i = 0; i < data.Length; i++) + { + if (token == null || token.Value == textInfo.ListSeparator) + { + throw new FormatException(); + } + + data[i] = float.Parse(token.Value, NumberStyles.Any, formatProvider); + + token = token.Next; + if (token != null) + { + token = token.Next; + } + } + + return new SparseVector(data); + } + + /// + /// Converts the string representation of a real sparse vector to float-precision sparse vector equivalent. + /// A return value indicates whether the conversion succeeded or failed. + /// + /// + /// A string containing a real vector to convert. + /// + /// + /// The parsed value. + /// + /// + /// If the conversion succeeds, the result will contain a complex number equivalent to value. + /// Otherwise the result will be null. + /// + public static bool TryParse(string value, out SparseVector result) + { + return TryParse(value, null, out result); + } + + /// + /// Converts the string representation of a real sparse vector to float-precision sparse vector equivalent. + /// A return value indicates whether the conversion succeeded or failed. + /// + /// + /// A string containing a real vector to convert. + /// + /// + /// An that supplies culture-specific formatting information about value. + /// + /// + /// The parsed value. + /// + /// + /// If the conversion succeeds, the result will contain a complex number equivalent to value. + /// Otherwise the result will be null. + /// + public static bool TryParse(string value, IFormatProvider formatProvider, out SparseVector result) + { + bool ret; + try + { + result = Parse(value, formatProvider); + ret = true; + } + catch (ArgumentNullException) + { + result = null; + ret = false; + } + catch (FormatException) + { + result = null; + ret = false; + } + + return ret; + } + + #endregion + + /// + /// Delete, Add or Update the value in NonZeroValues and NonZeroIndices + /// + /// Value real index in array + /// The value to set. + /// This method assume that index is between 0 and Array Size + private void SetValue(int index, float value) + { + // Search if "index" already exists in range "0 - real nonzero values count" + var itemIndex = Array.BinarySearch(_nonZeroIndices, 0, NonZerosCount, index); + + if (itemIndex >= 0) + { + // Item already exist at itemIndex + if (value == 0.0) + { + // Value is zero. Let's delete it from Values and Indices array + for (var i = itemIndex + 1; i < NonZerosCount; i++) + { + _nonZeroValues[i - 1] = _nonZeroValues[i]; + _nonZeroIndices[i - 1] = _nonZeroIndices[i]; + } + + NonZerosCount -= 1; + + // Check if the storage needs to be shrink. This is reasonable to do if + // there are a lot of non-zero elements and storage is two times bigger + if ((NonZerosCount > 1024) && (NonZerosCount < _nonZeroIndices.Length / 2)) + { + Array.Resize(ref _nonZeroValues, NonZerosCount); + Array.Resize(ref _nonZeroIndices, NonZerosCount); + } + } + else + { + _nonZeroValues[itemIndex] = value; + } + } + else + { + if (value == 0.0) + { + return; + } + + itemIndex = ~itemIndex; // Index where to put new value + + // Check if the storage needs to be increased + if ((NonZerosCount == _nonZeroValues.Length) && (NonZerosCount < Count)) + { + // Value and Indices arrays are completely full so we increase the size + var size = Math.Min(_nonZeroValues.Length + GrowthSize(), Count); + Array.Resize(ref _nonZeroValues, size); + Array.Resize(ref _nonZeroIndices, size); + } + + // Move all values (with an position larger than index) in the value array + // to the next position + // move all values (with an position larger than index) in the columIndices + // array to the next position + for (var i = NonZerosCount - 1; i > itemIndex - 1; i--) + { + _nonZeroValues[i + 1] = _nonZeroValues[i]; + _nonZeroIndices[i + 1] = _nonZeroIndices[i]; + } + + // Add the value and the column index + _nonZeroValues[itemIndex] = value; + _nonZeroIndices[itemIndex] = index; + + // increase the number of non-zero numbers by one + NonZerosCount += 1; + } + } + + /// + /// Calculates the amount with which to grow the storage array's if they need to be + /// increased in size. + /// + /// The amount grown. + private int GrowthSize() + { + int delta; + if (_nonZeroValues.Length > 1024) + { + delta = _nonZeroValues.Length / 4; + } + else + { + if (_nonZeroValues.Length > 256) + { + delta = 512; + } + else + { + delta = _nonZeroValues.Length > 64 ? 128 : 32; + } + } + + return delta; + } + + #region System.Object override + + /// + /// Returns a hash code for this instance. + /// + /// + /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. + /// + public override int GetHashCode() + { + var hashNum = Math.Min(NonZerosCount, 20); + long hash = 0; + for (var i = 0; i < hashNum; i++) + { +#if SILVERLIGHT + hash ^= Precision.DoubleToInt64Bits(this._nonZeroValues[i].GetHashCode()); +#else + hash ^= BitConverter.DoubleToInt64Bits(_nonZeroValues[i].GetHashCode()); +#endif + } + + return BitConverter.ToInt32(BitConverter.GetBytes(hash), 4); + } + + #endregion + + /// + /// Indicates whether the current object is equal to another object of the same type. + /// + /// + /// An object to compare with this object. + /// + /// + /// true if the current object is equal to the parameter; otherwise, false. + /// + public override bool Equals(Vector other) + { + // Reject equality when the argument is null or has a different length. + if (other == null) + { + return false; + } + + if (Count != other.Count) + { + return false; + } + + // Accept if the argument is the same object as this. + if (ReferenceEquals(this, other)) + { + return true; + } + + var sparseVector = other as SparseVector; + + if (sparseVector == null) + { + return base.Equals(other); + } + + if (NonZerosCount != sparseVector.NonZerosCount) + { + return false; + } + + // If all else fails, perform element wise comparison. + for (var index = 0; index < NonZerosCount; index++) + { + if (!_nonZeroValues[index].AlmostEqual(sparseVector._nonZeroValues[index]) || (_nonZeroIndices[index] != sparseVector._nonZeroIndices[index])) + { + return false; + } + } + + return true; + } + + #region Simple arithmetic of type T + /// + /// Add two values T+T + /// + /// Left operand value + /// Right operand value + /// Result of addition + protected sealed override float AddT(float val1, float val2) + { + return val1 + val2; + } + + /// + /// Subtract two values T-T + /// + /// Left operand value + /// Right operand value + /// Result of subtract + protected sealed override float SubtractT(float val1, float val2) + { + return val1 - val2; + } + + /// + /// Multiply two values T*T + /// + /// Left operand value + /// Right operand value + /// Result of multiplication + protected sealed override float MultiplyT(float val1, float val2) + { + return val1 * val2; + } + + /// + /// Divide two values T/T + /// + /// Left operand value + /// Right operand value + /// Result of divide + protected sealed override float DivideT(float val1, float val2) + { + return val1 / val2; + } + + /// + /// Take absolute value + /// + /// Source alue + /// True if one; otherwise false + protected sealed override double AbsoluteT(float val1) + { + return Math.Abs(val1); + } + #endregion + } +} diff --git a/src/Numerics/Numerics.csproj b/src/Numerics/Numerics.csproj index 2f9c896b..6738df22 100644 --- a/src/Numerics/Numerics.csproj +++ b/src/Numerics/Numerics.csproj @@ -139,6 +139,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Numerics/Precision.cs b/src/Numerics/Precision.cs index 825dcea7..14fdf856 100644 --- a/src/Numerics/Precision.cs +++ b/src/Numerics/Precision.cs @@ -250,6 +250,24 @@ namespace MathNet.Numerics return (result >= 0) ? result : (long.MinValue - result); } + /// + /// Returns a 'directional' int value. This is a int value which acts the same as a float, + /// e.g. a negative float value will return a negative int value starting at 0 and going + /// more negative as the float value gets more negative. + /// + /// The input float value. + /// An int value which is roughly the equivalent of the double value. + private static int GetDirectionalIntFromFloat(float value) + { + // Convert in the normal way. + int result = FloatToInt32Bits(value); + + // Now find out where we're at in the range + // If the value is larger/equal to zero then we can just return the value + // if the value is negative we subtract int.MinValue from it. + return (result >= 0) ? result : (int.MinValue - result); + } + /// /// Increments a floating point number to the next bigger number representable by the data type. /// @@ -783,14 +801,14 @@ namespace MathNet.Numerics } /// - /// Compares two doubles and determines if they are equal within + /// Compares two complex and determines if they are equal within /// the specified maximum error. /// /// The first value. /// The second value. /// The accuracy required for being almost equal. /// - /// if both doubles are almost equal up to the + /// if both complex are almost equal up to the /// specified maximum error, otherwise. /// public static bool AlmostEqualWithError(this Complex a, Complex b, double maximumError) @@ -799,6 +817,22 @@ namespace MathNet.Numerics return AlmostEqualWithError(a.Norm(), b.Norm(), diff, maximumError); } + /// + /// Compares two complex and determines if they are equal within + /// the specified maximum error. + /// + /// The first value. + /// The second value. + /// The accuracy required for being almost equal. + /// + /// if both complex are almost equal up to the + /// specified maximum error, otherwise. + /// + public static bool AlmostEqualWithError(this float a, float b, double maximumError) + { + return AlmostEqualWithError(a, b, a - b, maximumError); + } + /// /// Compares two doubles and determines if they are equal within /// the specified maximum error. @@ -1150,7 +1184,7 @@ namespace MathNet.Numerics // If A or B are a NAN, return false. NANs are equal to nothing, // not even themselves. - if (double.IsNaN(a) || double.IsNaN(b)) + if (float.IsNaN(a) || float.IsNaN(b)) { return false; } @@ -1158,7 +1192,7 @@ namespace MathNet.Numerics // If A or B are infinity (positive or negative) then // only return true if they are exactly equal to each other - // that is, if they are both infinities of the same sign. - if (double.IsInfinity(a) || double.IsInfinity(b)) + if (float.IsInfinity(a) || float.IsInfinity(b)) { return a == b; } @@ -1220,6 +1254,48 @@ namespace MathNet.Numerics } } + /// + /// Compares two floats and determines if they are equal to within the specified number of decimal places or not. + /// + /// + /// + /// The values are equal if the difference between the two numbers is smaller than 10^(-numberOfDecimalPlaces). We divide by + /// two so that we have half the range on each side of the numbers, e.g. if == 2, then 0.01 will equal between + /// 0.005 and 0.015, but not 0.02 and not 0.00 + /// + /// + /// The first value. + /// The second value. + /// The number of decimal places. + /// if both floats are equal to each other within the specified number of decimal places; otherwise . + private static bool AlmostEqualWithRelativeDecimalPlaces(this float a, float b, int decimalPlaces) + { + // If the magnitudes of the two numbers are equal to within one magnitude the numbers could potentially be equal + int magnitudeOfFirst = Magnitude(a); + int magnitudeOfSecond = Magnitude(b); + if (Math.Max(magnitudeOfFirst, magnitudeOfSecond) > (Math.Min(magnitudeOfFirst, magnitudeOfSecond) + 1)) + { + return false; + } + + // Get the power of the number of decimalPlaces + var decimalPlaceMagnitude = (float)Math.Pow(10, -(decimalPlaces - 1)); + + // The values are equal if the difference between the two numbers is smaller than + // 10^(-numberOfDecimalPlaces). We divide by two so that we have half the range + // on each side of the numbers, e.g. if decimalPlaces == 2, + // then 0.01 will equal between 0.005 and 0.015, but not 0.02 and not 0.00 + float maxDifference = decimalPlaceMagnitude / 2.0f; + if (a > b) + { + return (a * (float)Math.Pow(10, -magnitudeOfFirst)) - maxDifference < (b * (float)Math.Pow(10, -magnitudeOfFirst)); + } + else + { + return (b * (float)Math.Pow(10, -magnitudeOfSecond)) - maxDifference < (a * (float)Math.Pow(10, -magnitudeOfSecond)); + } + } + /// /// Compares two doubles and determines if they are equal to within the specified number of decimal places or not, using the /// number of decimal places as an absolute measure. @@ -1246,6 +1322,32 @@ namespace MathNet.Numerics return Math.Abs((a - b)) < decimalPlaceMagnitude / 2.0; } + /// + /// Compares two floats and determines if they are equal to within the specified number of decimal places or not, using the + /// number of decimal places as an absolute measure. + /// + /// + /// + /// The values are equal if the difference between the two numbers is smaller than 10^(-numberOfDecimalPlaces). We divide by + /// two so that we have half the range on each side of the numbers, e.g. if == 2, then 0.01 will equal between + /// 0.005 and 0.015, but not 0.02 and not 0.00 + /// + /// + /// The first value. + /// The second value. + /// The number of decimal places. + /// if both floats are equal to each other within the specified number of decimal places; otherwise . + private static bool AlmostEqualWithAbsoluteDecimalPlaces(this float a, float b, int decimalPlaces) + { + var decimalPlaceMagnitude = (float)Math.Pow(10, -(decimalPlaces - 1)); + + // The values are equal if the difference between the two numbers is smaller than + // 10^(-numberOfDecimalPlaces). We divide by two so that we have half the range + // on each side of the numbers, e.g. if decimalPlaces == 2, + // then 0.01 will equal between 0.005 and 0.015, but not 0.02 and not 0.00 + return Math.Abs((a - b)) < decimalPlaceMagnitude / 2.0f; + } + /// /// Compares two doubles and determines if they are equal to within the tolerance or not. Equality comparison is based on the binary representation. /// @@ -1305,6 +1407,52 @@ namespace MathNet.Numerics return (a > b) ? (secondUlong + maxNumbersBetween >= firstUlong) : (firstUlong + maxNumbersBetween >= secondUlong); } + /// + /// Compares two floats and determines if they are equal to within the tolerance or not. Equality comparison is based on the binary representation. + /// + /// The first value. + /// The second value. + /// The maximum number of floating point values between the two values. Must be 1 or larger. + /// if both floats are equal to each other within the specified tolerance; otherwise . + /// + /// Thrown if is smaller than one. + /// + public static bool AlmostEqual(this float a, float b, int maxNumbersBetween) + { + // Make sure maxNumbersBetween is non-negative and small enough that the + // default NAN won't compare as equal to anything. + if (maxNumbersBetween < 1) + { + throw new ArgumentOutOfRangeException("maxNumbersBetween"); + } + + // If A or B are infinity (positive or negative) then + // only return true if they are exactly equal to each other - + // that is, if they are both infinities of the same sign. + if (float.IsInfinity(a) || float.IsInfinity(b)) + { + return a == b; + } + + // If A or B are a NAN, return false. NANs are equal to nothing, + // not even themselves. + if (float.IsNaN(a) || float.IsNaN(b)) + { + return false; + } + + // Get the first float and convert it to an integer value (by using the binary representation) + int firstUlong = GetDirectionalIntFromFloat(a); + + // Get the second float and convert it to an integer value (by using the binary representation) + int secondUlong = GetDirectionalIntFromFloat(b); + + // Now compare the values. + // Note that this comparison can overflow so we'll approach this differently + // Do note that we could overflow this way too. We should probably check that we don't. + return (a > b) ? (secondUlong + maxNumbersBetween >= firstUlong) : (firstUlong + maxNumbersBetween >= secondUlong); + } + /// /// Compares two doubles and determines if the first value is larger than the second /// value to within the tolerance or not. Equality comparison is based on the binary representation. @@ -1375,6 +1523,27 @@ namespace MathNet.Numerics return CompareTo(a, b, maxNumbersBetween) < 0; } + /// + /// Compares two floats and determines if the first value is smaller than the second + /// value to within the tolerance or not. Equality comparison is based on the binary representation. + /// + /// The first value. + /// The second value. + /// The maximum number of floating point values for which the two values are considered equal. Must be 1 or larger. + /// true if the first value is smaller than the second value; otherwise false. + public static bool IsSmaller(this float a, float b, long maxNumbersBetween) + { + // If A or B are a NAN, return false. NANs are equal to nothing, + // not even themselves, and thus they're not bigger or + // smaller than anything either + if (float.IsNaN(a) || float.IsNaN(b)) + { + return false; + } + + return CompareTo(a, b, maxNumbersBetween) < 0; + } + /// /// Compares two doubles and determines if the first value is smaller than the second /// value to within the specified number of decimal places or not. @@ -1572,6 +1741,11 @@ namespace MathNet.Numerics return 2 * EpsilonOf(value); } + internal static int FloatToInt32Bits(float value) + { + return BitConverter.ToInt32(BitConverter.GetBytes(value), 0); + } + #if SILVERLIGHT internal static long DoubleToInt64Bits(double value) { diff --git a/src/Numerics/SpecialFunctions/Stability.cs b/src/Numerics/SpecialFunctions/Stability.cs index 286490bd..984fe9a1 100644 --- a/src/Numerics/SpecialFunctions/Stability.cs +++ b/src/Numerics/SpecialFunctions/Stability.cs @@ -90,6 +90,30 @@ namespace MathNet.Numerics return 0d; } + /// + /// Numerically stable hypotenuse of a right angle triangle, i.e. (a,b) -> sqrt(a^2 + b^2) + /// + /// The length of side a of the triangle. + /// The length of side b of the triangle. + /// Returns sqrt(a2 + b2) without underflow/overflow. + public static float Hypotenuse(float a, float b) + { + if (Math.Abs(a) > Math.Abs(b)) + { + float r = b / a; + return Math.Abs(a) * (float)Math.Sqrt(1 + (r * r)); + } + + if (b != 0.0) + { + // NOTE (ruegg): not "!b.AlmostZero()" to avoid convergence issues (e.g. in SVD algorithm) + float r = a / b; + return Math.Abs(b) * (float)Math.Sqrt(1 + (r * r)); + } + + return 0f; + } + /// /// Numerically stable series summation /// diff --git a/src/Numerics/Threading/CommonParallel.cs b/src/Numerics/Threading/CommonParallel.cs index e7f89909..cea569da 100644 --- a/src/Numerics/Threading/CommonParallel.cs +++ b/src/Numerics/Threading/CommonParallel.cs @@ -140,6 +140,67 @@ namespace MathNet.Numerics.Threading return sum; } + /// + /// Aggregates a function over a loop. + /// + /// Starting index of the loop. + /// Ending index of the loop + /// The function to aggregate. + /// The sum of the function over the loop. + public static float Aggregate(int fromInclusive, int toExclusive, Func body) + { + var sync = new object(); + var sum = 0.0f; + +#if SILVERLIGHT + Parallel.For( + fromInclusive, + toExclusive, + () => 0.0f, + (i, localData) => localData += body(i), + localResult => + { + lock (sync) + { + sum += localResult; + } + }); +#else + + if (Control.DisableParallelization || Control.NumberOfParallelWorkerThreads < 2) + { + for (var index = fromInclusive; index < toExclusive; index++) + { + sum += body(index); + } + } + else + { + Parallel.ForEach( + Partitioner.Create(fromInclusive, toExclusive), + new ParallelOptions { MaxDegreeOfParallelism = Control.NumberOfParallelWorkerThreads }, + () => 0.0f, + (range, loopState, localData) => + { + for (var i = range.Item1; i < range.Item2; i++) + { + localData += body(i); + } + + return localData; + }, + localResult => + { + lock (sync) + { + sum += localResult; + } + }); + } +#endif + return sum; + } + /// /// Aggregates a function over a loop for Complex data type. /// diff --git a/src/Silverlight/Silverlight.csproj b/src/Silverlight/Silverlight.csproj index 0d853513..ecc893bc 100644 --- a/src/Silverlight/Silverlight.csproj +++ b/src/Silverlight/Silverlight.csproj @@ -413,6 +413,93 @@ LinearAlgebra\Double\SparseVector.cs + + LinearAlgebra\Single\DenseMatrix.cs + + + LinearAlgebra\Single\DenseVector.cs + + + LinearAlgebra\Single\DiagonalMatrix.cs + + + LinearAlgebra\Single\Factorization\DenseCholesky.cs + + + LinearAlgebra\Single\Factorization\DenseLU.cs + + + LinearAlgebra\Single\Factorization\DenseQR.cs + + + LinearAlgebra\Single\Factorization\DenseSvd.cs + + + LinearAlgebra\Single\Factorization\GramSchmidt.cs + + + LinearAlgebra\Single\Factorization\UserCholesky.cs + + + LinearAlgebra\Single\Factorization\UserLU.cs + + + LinearAlgebra\Single\Factorization\UserQR.cs + + + LinearAlgebra\Single\Factorization\UserSvd.cs + + + LinearAlgebra\Single\Solvers\Iterative\BiCgStab.cs + + + LinearAlgebra\Single\Solvers\Iterative\CompositeSolver.cs + + + LinearAlgebra\Single\Solvers\Iterative\GpBiCg.cs + + + LinearAlgebra\Single\Solvers\Iterative\MlkBiCgStab.cs + + + LinearAlgebra\Single\Solvers\Iterative\TFQMR.cs + + + LinearAlgebra\Single\Solvers\Iterator.cs + + + LinearAlgebra\Single\Solvers\Preconditioners\Diagonal.cs + + + LinearAlgebra\Single\Solvers\Preconditioners\Ilutp.cs + + + LinearAlgebra\Single\Solvers\Preconditioners\IlutpElementSorter.cs + + + LinearAlgebra\Single\Solvers\Preconditioners\IncompleteLU.cs + + + LinearAlgebra\Single\Solvers\Preconditioners\UnitPreconditioner.cs + + + LinearAlgebra\Single\Solvers\StopCriterium\DivergenceStopCriterium.cs + + + LinearAlgebra\Single\Solvers\StopCriterium\FailureStopCriterium.cs + + + LinearAlgebra\Single\Solvers\StopCriterium\IterationCountStopCriterium.cs + + + LinearAlgebra\Single\Solvers\StopCriterium\ResidualStopCriterium.cs + + + LinearAlgebra\Single\SparseMatrix.cs + + + LinearAlgebra\Single\SparseVector.cs + LinearAlgebra\Generic\Factorization\Cholesky.cs diff --git a/src/UnitTests/AssertHelpers.cs b/src/UnitTests/AssertHelpers.cs index ffd22bb2..7cb8e14e 100644 --- a/src/UnitTests/AssertHelpers.cs +++ b/src/UnitTests/AssertHelpers.cs @@ -83,7 +83,29 @@ namespace MathNet.Numerics.UnitTests return; } - bool pass = Precision.AlmostEqualInDecimalPlaces(expected, actual, decimalPlaces); + bool pass = expected.AlmostEqualInDecimalPlaces(actual, decimalPlaces); + if (!pass) + { + // signals Gallio that the test failed. + Assert.Fail("Not equal within {0} places. Expected:{1}; Actual:{2}", decimalPlaces, expected, actual); + } + } + + /// + /// Asserts that the expected value and the actual value are equal up to a certain number of decimal places. If both + /// and are NaN then no assert is thrown. + /// + /// The expected value. + /// The actual value. + /// The number of decimal places to agree on. + public static void AlmostEqual(float expected, float actual, int decimalPlaces) + { + if (float.IsNaN(expected) && float.IsNaN(actual)) + { + return; + } + + bool pass = expected.AlmostEqualInDecimalPlaces(actual, decimalPlaces); if (!pass) { // signals Gallio that the test failed. @@ -169,6 +191,24 @@ namespace MathNet.Numerics.UnitTests } } + /// + /// Asserts that the expected value and the actual value are equal up to a certain + /// maximum error. + /// + /// The expected value list. + /// The actual value list. + /// The accuracy required for being almost equal. + public static void AlmostEqualList(IList expected, IList actual, double maximumError) + { + for (int i = 0; i < expected.Count; i++) + { + if (!actual[i].AlmostEqualWithError(expected[i], maximumError)) + { + Assert.Fail("Not equal within a maximum error {0}. Expected:{1}; Actual:{2}", maximumError, expected[i], actual[i]); + } + } + } + /// /// Asserts that the expected value and the actual value are equal up to a certain /// maximum error. diff --git a/src/UnitTests/LinearAlgebraTests/Complex/Solvers/Preconditioners/IluptElementSorterTest.cs b/src/UnitTests/LinearAlgebraTests/Complex/Solvers/Preconditioners/IluptElementSorterTest.cs index bc74fa5e..2fd14896 100644 --- a/src/UnitTests/LinearAlgebraTests/Complex/Solvers/Preconditioners/IluptElementSorterTest.cs +++ b/src/UnitTests/LinearAlgebraTests/Complex/Solvers/Preconditioners/IluptElementSorterTest.cs @@ -4,8 +4,10 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex.Solvers.Preconditioners { + using System.Numerics; using LinearAlgebra.Complex; using LinearAlgebra.Complex.Solvers.Preconditioners; + using LinearAlgebra.Generic; using MbUnit.Framework; [TestFixture] @@ -140,7 +142,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex.Solvers.Precondi public void HeapSortWithIncreasingDoubleArray() { var sortedIndices = new int[10]; - Vector values = new DenseVector(10); + Vector values = new DenseVector(10); values[0] = 0; values[1] = 1; values[2] = 2; @@ -168,7 +170,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex.Solvers.Precondi public void HeapSortWithDecreasingDoubleArray() { var sortedIndices = new int[10]; - Vector values = new DenseVector(10); + Vector values = new DenseVector(10); values[0] = 9; values[1] = 8; values[2] = 7; @@ -196,7 +198,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex.Solvers.Precondi public void HeapSortWithRandomDoubleArray() { var sortedIndices = new int[10]; - Vector values = new DenseVector(10); + Vector values = new DenseVector(10); values[0] = 5; values[1] = 2; values[2] = 8; @@ -256,7 +258,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex.Solvers.Precondi public void HeapSortWithDuplicateDoubleEntries() { var sortedIndices = new int[10]; - Vector values = new DenseVector(10); + Vector values = new DenseVector(10); values[0] = 1; values[1] = 1; values[2] = 1; @@ -317,7 +319,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex.Solvers.Precondi public void HeapSortWithSpecialConstructedDoubleArray() { var sortedIndices = new int[10]; - Vector values = new DenseVector(10); + Vector values = new DenseVector(10); values[0] = 0; values[1] = 0; values[2] = 0; @@ -420,7 +422,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex.Solvers.Precondi public void HeapSortWithIncreasingDoubleArrayWithLowerBound() { var sortedIndices = new int[10]; - Vector values = new DenseVector(10); + Vector values = new DenseVector(10); values[0] = 0; values[1] = 1; values[2] = 2; @@ -448,7 +450,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex.Solvers.Precondi public void HeapSortWithIncreasingDoubleArrayWithUpperBound() { var sortedIndices = new int[10]; - Vector values = new DenseVector(10); + Vector values = new DenseVector(10); values[0] = 0; values[1] = 1; values[2] = 2; @@ -476,7 +478,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex.Solvers.Precondi public void HeapSortWithIncreasingDoubleArrayWithLowerAndUpperBound() { var sortedIndices = new int[10]; - Vector values = new DenseVector(10); + Vector values = new DenseVector(10); values[0] = 0; values[1] = 1; values[2] = 2; diff --git a/src/UnitTests/LinearAlgebraTests/Complex/UserDefinedMatrixTests.cs b/src/UnitTests/LinearAlgebraTests/Complex/UserDefinedMatrixTests.cs index 18a28c15..f8564ffa 100644 --- a/src/UnitTests/LinearAlgebraTests/Complex/UserDefinedMatrixTests.cs +++ b/src/UnitTests/LinearAlgebraTests/Complex/UserDefinedMatrixTests.cs @@ -165,11 +165,6 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex return val1 / val2; } - protected sealed override bool IsOneT(Complex val1) - { - return Complex.One.AlmostEqual(val1); - } - protected sealed override double AbsoluteT(Complex val1) { return val1.Magnitude; diff --git a/src/UnitTests/LinearAlgebraTests/Complex/UserDefinedVectorTests.cs b/src/UnitTests/LinearAlgebraTests/Complex/UserDefinedVectorTests.cs index 2675ebe4..fa1633e5 100644 --- a/src/UnitTests/LinearAlgebraTests/Complex/UserDefinedVectorTests.cs +++ b/src/UnitTests/LinearAlgebraTests/Complex/UserDefinedVectorTests.cs @@ -168,11 +168,6 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex return val1 / val2; } - protected sealed override bool IsOneT(Complex val1) - { - return Complex.One.AlmostEqual(val1); - } - protected sealed override double AbsoluteT(Complex val1) { return val1.Magnitude; diff --git a/src/UnitTests/LinearAlgebraTests/Double/Solvers/Preconditioners/IluptElementSorterTest.cs b/src/UnitTests/LinearAlgebraTests/Double/Solvers/Preconditioners/IluptElementSorterTest.cs index 649a6a34..66276968 100644 --- a/src/UnitTests/LinearAlgebraTests/Double/Solvers/Preconditioners/IluptElementSorterTest.cs +++ b/src/UnitTests/LinearAlgebraTests/Double/Solvers/Preconditioners/IluptElementSorterTest.cs @@ -6,6 +6,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double.Solvers.Precondit { using LinearAlgebra.Double; using LinearAlgebra.Double.Solvers.Preconditioners; + using LinearAlgebra.Generic; using MbUnit.Framework; [TestFixture] @@ -140,7 +141,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double.Solvers.Precondit public void HeapSortWithIncreasingDoubleArray() { var sortedIndices = new int[10]; - Vector values = new DenseVector(10); + Vector values = new DenseVector(10); values[0] = 0; values[1] = 1; values[2] = 2; @@ -168,7 +169,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double.Solvers.Precondit public void HeapSortWithDecreasingDoubleArray() { var sortedIndices = new int[10]; - Vector values = new DenseVector(10); + Vector values = new DenseVector(10); values[0] = 9; values[1] = 8; values[2] = 7; @@ -196,7 +197,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double.Solvers.Precondit public void HeapSortWithRandomDoubleArray() { var sortedIndices = new int[10]; - Vector values = new DenseVector(10); + Vector values = new DenseVector(10); values[0] = 5; values[1] = 2; values[2] = 8; @@ -256,7 +257,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double.Solvers.Precondit public void HeapSortWithDuplicateDoubleEntries() { var sortedIndices = new int[10]; - Vector values = new DenseVector(10); + Vector values = new DenseVector(10); values[0] = 1; values[1] = 1; values[2] = 1; @@ -317,7 +318,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double.Solvers.Precondit public void HeapSortWithSpecialConstructedDoubleArray() { var sortedIndices = new int[10]; - Vector values = new DenseVector(10); + Vector values = new DenseVector(10); values[0] = 0; values[1] = 0; values[2] = 0; @@ -420,7 +421,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double.Solvers.Precondit public void HeapSortWithIncreasingDoubleArrayWithLowerBound() { var sortedIndices = new int[10]; - Vector values = new DenseVector(10); + Vector values = new DenseVector(10); values[0] = 0; values[1] = 1; values[2] = 2; @@ -448,7 +449,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double.Solvers.Precondit public void HeapSortWithIncreasingDoubleArrayWithUpperBound() { var sortedIndices = new int[10]; - Vector values = new DenseVector(10); + Vector values = new DenseVector(10); values[0] = 0; values[1] = 1; values[2] = 2; @@ -476,7 +477,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double.Solvers.Precondit public void HeapSortWithIncreasingDoubleArrayWithLowerAndUpperBound() { var sortedIndices = new int[10]; - Vector values = new DenseVector(10); + Vector values = new DenseVector(10); values[0] = 0; values[1] = 1; values[2] = 2; diff --git a/src/UnitTests/LinearAlgebraTests/Double/UserDefinedMatrixTests.cs b/src/UnitTests/LinearAlgebraTests/Double/UserDefinedMatrixTests.cs index 7b83c327..aab37f52 100644 --- a/src/UnitTests/LinearAlgebraTests/Double/UserDefinedMatrixTests.cs +++ b/src/UnitTests/LinearAlgebraTests/Double/UserDefinedMatrixTests.cs @@ -165,20 +165,10 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double return val1 / val2; } - protected sealed override bool IsOneT(double val1) - { - return 1.0.AlmostEqualInDecimalPlaces(val1, 15); - } - protected sealed override double AbsoluteT(double val1) { return Math.Abs(val1); } - - public override Matrix ConjugateTranspose() - { - throw new NotSupportedException(); - } } public class UserDefinedMatrixTests : MatrixTests diff --git a/src/UnitTests/LinearAlgebraTests/Double/UserDefinedVectorTests.cs b/src/UnitTests/LinearAlgebraTests/Double/UserDefinedVectorTests.cs index 5403dc33..7b21fe80 100644 --- a/src/UnitTests/LinearAlgebraTests/Double/UserDefinedVectorTests.cs +++ b/src/UnitTests/LinearAlgebraTests/Double/UserDefinedVectorTests.cs @@ -190,20 +190,10 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double return val1 / val2; } - protected sealed override bool IsOneT(double val1) - { - return val1 == 1.0; - } - protected sealed override double AbsoluteT(double val1) { return Math.Abs(val1); } - - public override void Conjugate(Vector target) - { - throw new NotSupportedException(); - } } public class UserDefinedVectorTests : VectorTests diff --git a/src/UnitTests/LinearAlgebraTests/Single/DenseMatrixTests.cs b/src/UnitTests/LinearAlgebraTests/Single/DenseMatrixTests.cs new file mode 100644 index 00000000..c9c130f5 --- /dev/null +++ b/src/UnitTests/LinearAlgebraTests/Single/DenseMatrixTests.cs @@ -0,0 +1,156 @@ +// +// 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. +// + +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 CreateMatrix(int rows, int columns) + { + return new DenseMatrix(rows, columns); + } + + protected override Matrix CreateMatrix(float[,] data) + { + return new DenseMatrix(data); + } + + protected override Vector CreateVector(int size) + { + return new DenseVector(size); + } + + protected override Vector CreateVector(float[] data) + { + return new DenseVector(data); + } + + [Test] + public void CanCreateMatrixFrom1DArray() + { + var testData = new Dictionary> + { + { "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); + } + } +} diff --git a/src/UnitTests/LinearAlgebraTests/Single/DenseVectorTest.TextHandling.cs b/src/UnitTests/LinearAlgebraTests/Single/DenseVectorTest.TextHandling.cs new file mode 100644 index 00000000..137c44f6 --- /dev/null +++ b/src/UnitTests/LinearAlgebraTests/Single/DenseVectorTest.TextHandling.cs @@ -0,0 +1,133 @@ +// +// 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. +// + +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(() => DenseVector.Parse("(1")); + Assert.Throws(() => 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); + } + } +} diff --git a/src/UnitTests/LinearAlgebraTests/Single/DenseVectorTests.cs b/src/UnitTests/LinearAlgebraTests/Single/DenseVectorTests.cs new file mode 100644 index 00000000..5f802927 --- /dev/null +++ b/src/UnitTests/LinearAlgebraTests/Single/DenseVectorTests.cs @@ -0,0 +1,290 @@ +// +// 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. +// + +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 CreateVector(int size) + { + return new DenseVector(size); + } + + protected override Vector CreateVector(IList 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)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 m = Vector.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.OuterProduct(vector1, vector2); + } + + [Test] + [ExpectedArgumentNullException] + public void OuterProductForDenseVectorWithSecondParameterNullShouldThrowException() + { + var vector1 = CreateVector(Data); + DenseVector vector2 = null; + Vector.OuterProduct(vector1, vector2); + } + } +} \ No newline at end of file diff --git a/src/UnitTests/LinearAlgebraTests/Single/DiagonalMatrixTests.cs b/src/UnitTests/LinearAlgebraTests/Single/DiagonalMatrixTests.cs new file mode 100644 index 00000000..e8c07fa6 --- /dev/null +++ b/src/UnitTests/LinearAlgebraTests/Single/DiagonalMatrixTests.cs @@ -0,0 +1,450 @@ +// +// 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. +// + +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 + { + { "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])); + } + } + + protected override Matrix CreateMatrix(int rows, int columns) + { + return new DiagonalMatrix(rows, columns); + } + + protected override Matrix CreateMatrix(float[,] data) + { + return new DiagonalMatrix(data); + } + + protected override Vector CreateVector(int size) + { + return new DenseVector(size); + } + + protected override Vector CreateVector(float[] data) + { + return new DenseVector(data); + } + + [Test] + public void CanCreateMatrixFromDiagonalArray() + { + var testData = new Dictionary> + { + { "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(); + } + } +} diff --git a/src/UnitTests/LinearAlgebraTests/Single/Factorization/CholeskyTests.cs b/src/UnitTests/LinearAlgebraTests/Single/Factorization/CholeskyTests.cs new file mode 100644 index 00000000..0f12354b --- /dev/null +++ b/src/UnitTests/LinearAlgebraTests/Single/Factorization/CholeskyTests.cs @@ -0,0 +1,300 @@ +// +// 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. +// + +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]); + } + } + } + } +} diff --git a/src/UnitTests/LinearAlgebraTests/Single/Factorization/GramSchmidtTests.cs b/src/UnitTests/LinearAlgebraTests/Single/Factorization/GramSchmidtTests.cs new file mode 100644 index 00000000..608f1954 --- /dev/null +++ b/src/UnitTests/LinearAlgebraTests/Single/Factorization/GramSchmidtTests.cs @@ -0,0 +1,331 @@ +// +// 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. +// + +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]); + } + } + } + } +} diff --git a/src/UnitTests/LinearAlgebraTests/Single/Factorization/LUTests.cs b/src/UnitTests/LinearAlgebraTests/Single/Factorization/LUTests.cs new file mode 100644 index 00000000..b34e5f7b --- /dev/null +++ b/src/UnitTests/LinearAlgebraTests/Single/Factorization/LUTests.cs @@ -0,0 +1,364 @@ +// +// 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. +// + +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); + } + } + } +} diff --git a/src/UnitTests/LinearAlgebraTests/Single/Factorization/QRTests.cs b/src/UnitTests/LinearAlgebraTests/Single/Factorization/QRTests.cs new file mode 100644 index 00000000..1f66945a --- /dev/null +++ b/src/UnitTests/LinearAlgebraTests/Single/Factorization/QRTests.cs @@ -0,0 +1,318 @@ +// +// 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. +// + +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]); + } + } + } + } +} diff --git a/src/UnitTests/LinearAlgebraTests/Single/Factorization/SvdTests.cs b/src/UnitTests/LinearAlgebraTests/Single/Factorization/SvdTests.cs new file mode 100644 index 00000000..b6443dee --- /dev/null +++ b/src/UnitTests/LinearAlgebraTests/Single/Factorization/SvdTests.cs @@ -0,0 +1,371 @@ +// +// 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. +// + +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]); + } + } + } + } +} diff --git a/src/UnitTests/LinearAlgebraTests/Single/Factorization/UserCholeskyTests.cs b/src/UnitTests/LinearAlgebraTests/Single/Factorization/UserCholeskyTests.cs new file mode 100644 index 00000000..5b8a2b68 --- /dev/null +++ b/src/UnitTests/LinearAlgebraTests/Single/Factorization/UserCholeskyTests.cs @@ -0,0 +1,299 @@ +// +// 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. +// + +namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single.Factorization +{ + using LinearAlgebra.Generic.Factorization; + using MbUnit.Framework; + + public class UserCholeskyTests + { + [Test] + [Row(1)] + [Row(10)] + [Row(100)] + public void CanFactorizeIdentity(int order) + { + var I = UserDefinedMatrix.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 = UserDefinedMatrix.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 UserDefinedMatrix(row, col); + I.Cholesky(); + } + + [Test] + [Row(1)] + [Row(10)] + [Row(100)] + public void IdentityDeterminantIsOne(int order) + { + var I = UserDefinedMatrix.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.GenerateRandomPositiveDefiniteUserDefinedMatrix(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.GenerateRandomPositiveDefiniteUserDefinedMatrix(order); + var matrixACopy = matrixA.Clone(); + var chol = matrixA.Cholesky(); + var b = MatrixLoader.GenerateRandomUserDefinedVector(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.GenerateRandomPositiveDefiniteUserDefinedMatrix(row); + var matrixACopy = matrixA.Clone(); + var chol = matrixA.Cholesky(); + var matrixB = MatrixLoader.GenerateRandomUserDefinedMatrix(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.GenerateRandomPositiveDefiniteUserDefinedMatrix(order); + var matrixACopy = matrixA.Clone(); + var chol = matrixA.Cholesky(); + var b = MatrixLoader.GenerateRandomUserDefinedVector(order); + var bCopy = b.Clone(); + var x = new UserDefinedVector(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.GenerateRandomPositiveDefiniteUserDefinedMatrix(row); + var matrixACopy = matrixA.Clone(); + var chol = matrixA.Cholesky(); + var matrixB = MatrixLoader.GenerateRandomUserDefinedMatrix(row, col); + var matrixBCopy = matrixB.Clone(); + var matrixX = new UserDefinedMatrix(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]); + } + } + } + } +} diff --git a/src/UnitTests/LinearAlgebraTests/Single/Factorization/UserLUTests.cs b/src/UnitTests/LinearAlgebraTests/Single/Factorization/UserLUTests.cs new file mode 100644 index 00000000..cd766233 --- /dev/null +++ b/src/UnitTests/LinearAlgebraTests/Single/Factorization/UserLUTests.cs @@ -0,0 +1,363 @@ +// +// 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. +// + +namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single.Factorization +{ + using LinearAlgebra.Generic.Factorization; + using MbUnit.Framework; + + public class UserLUTests + { + [Test] + [Row(1)] + [Row(10)] + [Row(100)] + public void CanFactorizeIdentity(int order) + { + var matrixI = UserDefinedMatrix.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 UserDefinedMatrix(row, col); + I.LU(); + } + + [Test] + [Row(1)] + [Row(10)] + [Row(100)] + public void IdentityDeterminantIsOne(int order) + { + var I = UserDefinedMatrix.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.GenerateRandomUserDefinedMatrix(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.GenerateRandomUserDefinedMatrix(order, order); + var matrixACopy = matrixA.Clone(); + var factorLU = matrixA.LU(); + + var vectorb = MatrixLoader.GenerateRandomUserDefinedVector(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.GenerateRandomUserDefinedMatrix(order, order); + var matrixACopy = matrixA.Clone(); + var factorLU = matrixA.LU(); + + var matrixB = MatrixLoader.GenerateRandomUserDefinedMatrix(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.GenerateRandomUserDefinedMatrix(order, order); + var matrixACopy = matrixA.Clone(); + var factorLU = matrixA.LU(); + var vectorb = MatrixLoader.GenerateRandomUserDefinedVector(order); + var vectorbCopy = vectorb.Clone(); + var resultx = new UserDefinedVector(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.GenerateRandomUserDefinedMatrix(order, order); + var matrixACopy = matrixA.Clone(); + var factorLU = matrixA.LU(); + + var matrixB = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order); + var matrixBCopy = matrixB.Clone(); + + var matrixX = new UserDefinedMatrix(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.GenerateRandomUserDefinedMatrix(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); + } + } + } +} diff --git a/src/UnitTests/LinearAlgebraTests/Single/Factorization/UserQRTests.cs b/src/UnitTests/LinearAlgebraTests/Single/Factorization/UserQRTests.cs new file mode 100644 index 00000000..c2f9f8e9 --- /dev/null +++ b/src/UnitTests/LinearAlgebraTests/Single/Factorization/UserQRTests.cs @@ -0,0 +1,317 @@ +// +// 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. +// + +namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single.Factorization +{ + using LinearAlgebra.Generic.Factorization; + using MbUnit.Framework; + using LinearAlgebra.Single.Factorization; + + public class UserQRTests + { + + [Test] + [ExpectedArgumentNullException] + public void ConstructorNull() + { + new UserQR(null); + } + + [Test] + [ExpectedArgumentException] + public void WideMatrixThrowsInvalidMatrixOperationException() + { + new UserQR(new UserDefinedMatrix(3, 4)); + } + + [Test] + [Row(1)] + [Row(10)] + [Row(100)] + public void CanFactorizeIdentity(int order) + { + var I = UserDefinedMatrix.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 = UserDefinedMatrix.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.GenerateRandomUserDefinedMatrix(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.GenerateRandomUserDefinedMatrix(order, order); + var matrixACopy = matrixA.Clone(); + var factorQR = matrixA.QR(); + + var vectorb = MatrixLoader.GenerateRandomUserDefinedVector(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.GenerateRandomUserDefinedMatrix(order, order); + var matrixACopy = matrixA.Clone(); + var factorQR = matrixA.QR(); + + var matrixB = MatrixLoader.GenerateRandomUserDefinedMatrix(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.GenerateRandomUserDefinedMatrix(order, order); + var matrixACopy = matrixA.Clone(); + var factorQR = matrixA.QR(); + var vectorb = MatrixLoader.GenerateRandomUserDefinedVector(order); + var vectorbCopy = vectorb.Clone(); + var resultx = new UserDefinedVector(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.GenerateRandomUserDefinedMatrix(order, order); + var matrixACopy = matrixA.Clone(); + var factorQR = matrixA.QR(); + + var matrixB = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order); + 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.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]); + } + } + } + } +} diff --git a/src/UnitTests/LinearAlgebraTests/Single/Factorization/UserSvdTests.cs b/src/UnitTests/LinearAlgebraTests/Single/Factorization/UserSvdTests.cs new file mode 100644 index 00000000..d82bd8c4 --- /dev/null +++ b/src/UnitTests/LinearAlgebraTests/Single/Factorization/UserSvdTests.cs @@ -0,0 +1,370 @@ +// +// 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. +// + +namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single.Factorization +{ + using System; + using LinearAlgebra.Generic.Factorization; + using MbUnit.Framework; + using LinearAlgebra.Single.Factorization; + + public class UserSvdTests + { + + [Test] + [ExpectedArgumentNullException] + public void ConstructorNull() + { + new UserSvd(null, true); + } + + [Test] + [Row(1)] + [Row(10)] + [Row(100)] + public void CanFactorizeIdentity(int order) + { + var I = UserDefinedMatrix.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.GenerateRandomUserDefinedMatrix(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.GenerateRandomUserDefinedMatrix(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.GenerateRandomUserDefinedMatrix(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 UserDefinedMatrix(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); + Assert.AreEqual(factorSvd.Rank, order - 1); + } + + [Test] + [ExpectedException(typeof(InvalidOperationException))] + public void CannotSolveMatrixIfVectorsNotComputed() + { + var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(10, 10); + var factorSvd = matrixA.Svd(false); + + var matrixB = MatrixLoader.GenerateRandomUserDefinedMatrix(10, 10); + factorSvd.Solve(matrixB); + } + + [Test] + [ExpectedException(typeof(InvalidOperationException))] + public void CannotSolveVectorIfVectorsNotComputed() + { + var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(10, 10); + var factorSvd = matrixA.Svd(false); + + var vectorb = MatrixLoader.GenerateRandomUserDefinedVector(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.GenerateRandomUserDefinedMatrix(row, column); + var matrixACopy = matrixA.Clone(); + var factorSvd = matrixA.Svd(true); + + var vectorb = MatrixLoader.GenerateRandomUserDefinedVector(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.GenerateRandomUserDefinedMatrix(row, count); + var matrixACopy = matrixA.Clone(); + var factorSvd = matrixA.Svd(true); + + var matrixB = MatrixLoader.GenerateRandomUserDefinedMatrix(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.GenerateRandomUserDefinedMatrix(row, column); + var matrixACopy = matrixA.Clone(); + var factorSvd = matrixA.Svd(true); + var vectorb = MatrixLoader.GenerateRandomUserDefinedVector(row); + var vectorbCopy = vectorb.Clone(); + var resultx = new UserDefinedVector(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.GenerateRandomUserDefinedMatrix(row, column); + var matrixACopy = matrixA.Clone(); + var factorSvd = matrixA.Svd(true); + + var matrixB = MatrixLoader.GenerateRandomUserDefinedMatrix(row, column); + var matrixBCopy = matrixB.Clone(); + + var matrixX = new UserDefinedMatrix(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]); + } + } + } + } +} diff --git a/src/UnitTests/LinearAlgebraTests/Single/IO/DelimitedReaderTests.cs b/src/UnitTests/LinearAlgebraTests/Single/IO/DelimitedReaderTests.cs new file mode 100644 index 00000000..6c1054d7 --- /dev/null +++ b/src/UnitTests/LinearAlgebraTests/Single/IO/DelimitedReaderTests.cs @@ -0,0 +1,121 @@ +namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single.IO +{ + using System; + using System.Globalization; + using System.IO; + using System.Text; + using LinearAlgebra.Single; + using LinearAlgebra.Single.IO; + using MbUnit.Framework; + + [TestFixture] + public class DelimitedReaderTests + { + [Test] + [MultipleAsserts] + public void CanParseCommaDelimitedData() + { + const string data = + @"a,b,c +1 +""2.2"",0.3e1 +'4',5,6 +"; + var reader = new DelimitedReader(',') + { + HasHeaderRow = true + }; + var matrix = reader.ReadMatrix(new MemoryStream(Encoding.UTF8.GetBytes(data))); + Assert.AreEqual(3, matrix.RowCount); + Assert.AreEqual(3, matrix.ColumnCount); + Assert.AreEqual(1.0, matrix[0, 0]); + Assert.AreEqual(0.0, matrix[0, 1]); + Assert.AreEqual(0.0, matrix[0, 2]); + Assert.AreEqual(2.2, matrix[1, 0]); + Assert.AreEqual(3.0, matrix[1, 1]); + Assert.AreEqual(0.0, matrix[1, 2]); + Assert.AreEqual(4.0, matrix[2, 0]); + Assert.AreEqual(5.0, matrix[2, 1]); + Assert.AreEqual(6.0, matrix[2, 2]); + } + + [Test] + [MultipleAsserts] + public void CanParseTabDelimtedData() + { + var data = "1" + + Environment.NewLine + + "\"2.2\"\t\t0.3e1" + + Environment.NewLine + + "'4'\t5\t6"; + + var reader = new DelimitedReader('\t'); + var matrix = reader.ReadMatrix(new MemoryStream(Encoding.UTF8.GetBytes(data))); + Assert.AreEqual(3, matrix.RowCount); + Assert.AreEqual(3, matrix.ColumnCount); + Assert.AreEqual(1.0, matrix[0, 0]); + Assert.AreEqual(0.0, matrix[0, 1]); + Assert.AreEqual(0.0, matrix[0, 2]); + Assert.AreEqual(2.2, matrix[1, 0]); + Assert.AreEqual(3.0, matrix[1, 1]); + Assert.AreEqual(0.0, matrix[1, 2]); + Assert.AreEqual(4.0, matrix[2, 0]); + Assert.AreEqual(5.0, matrix[2, 1]); + Assert.AreEqual(6.0, matrix[2, 2]); + } + + [Test] + [MultipleAsserts] + public void CanParseWhiteSpaceDelimitedData() + { + const string data = + @"1 +""2.2"" 0.3e1 +'4' 5 6 +"; + var reader = new DelimitedReader(); + var matrix = reader.ReadMatrix(new MemoryStream(Encoding.UTF8.GetBytes(data))); + Assert.AreEqual(3, matrix.RowCount); + Assert.AreEqual(3, matrix.ColumnCount); + Assert.AreEqual(1.0, matrix[0, 0]); + Assert.AreEqual(0.0, matrix[0, 1]); + Assert.AreEqual(0.0, matrix[0, 2]); + Assert.AreEqual(2.2, matrix[1, 0]); + Assert.AreEqual(3.0, matrix[1, 1]); + Assert.AreEqual(0.0, matrix[1, 2]); + Assert.AreEqual(4.0, matrix[2, 0]); + Assert.AreEqual(5.0, matrix[2, 1]); + Assert.AreEqual(6.0, matrix[2, 2]); + } + + [Test] + [MultipleAsserts] + public void CanParsePeriodDelimitedData() + { + const string data = + @"a.b.c +1 +""2,2"".0,3e1 +'4,0'.5,0.6,0 +"; + var reader = new DelimitedReader('.') + { + HasHeaderRow = true, + CultureInfo = new CultureInfo("tr-TR") + }; + + var matrix = reader.ReadMatrix(new MemoryStream(Encoding.UTF8.GetBytes(data))); + Assert.AreEqual(3, matrix.RowCount); + Assert.AreEqual(3, matrix.ColumnCount); + Assert.AreEqual(1.0, matrix[0, 0]); + Assert.AreEqual(0.0, matrix[0, 1]); + Assert.AreEqual(0.0, matrix[0, 2]); + Assert.AreEqual(2.2, matrix[1, 0]); + Assert.AreEqual(3.0, matrix[1, 1]); + Assert.AreEqual(0.0, matrix[1, 2]); + Assert.AreEqual(4.0, matrix[2, 0]); + Assert.AreEqual(5.0, matrix[2, 1]); + Assert.AreEqual(6.0, matrix[2, 2]); + } + } +} diff --git a/src/UnitTests/LinearAlgebraTests/Single/IO/DelimitedWriterTests.cs b/src/UnitTests/LinearAlgebraTests/Single/IO/DelimitedWriterTests.cs new file mode 100644 index 00000000..38ea6e58 --- /dev/null +++ b/src/UnitTests/LinearAlgebraTests/Single/IO/DelimitedWriterTests.cs @@ -0,0 +1,90 @@ +namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single.IO +{ + using System; + using System.Globalization; + using System.IO; + using System.Text; + using LinearAlgebra.Single; + using LinearAlgebra.Single.IO; + using MbUnit.Framework; + + [TestFixture] + public class DelimitedWriterTests + { + [Test] + public void CanWriteCommaDelimitedData() + { + var matrix = new DenseMatrix(new[,] { { 1.1f, 2.2f, 3.3f }, { 4.4f, 5.5f, 6.6f }, { 7.7f, 8.8f, 9.9f } }); + var writer = new DelimitedWriter(','); + var stream = new MemoryStream(); + writer.WriteMatrix(matrix, stream); + var data = stream.ToArray(); + var reader = new StreamReader(new MemoryStream(data)); + var text = reader.ReadToEnd(); + const string expected = @"1.1,2.2,3.3 +4.4,5.5,6.6 +7.7,8.8,9.9"; + Assert.AreEqual(expected, text); + } + + [Test] + public void CanWritePeriodDelimitedData() + { + var matrix = new DenseMatrix(new[,] { { 1.1f, 2.2f, 3.3f }, { 4.4f, 5.5f, 6.6f }, { 7.7f, 8.8f, 9.9f } }); + var culture = new CultureInfo("tr-TR"); + var writer = new DelimitedWriter('.') + { + CultureInfo = culture + }; + var stream = new MemoryStream(); + writer.WriteMatrix(matrix, stream); + var data = stream.ToArray(); + var reader = new StreamReader(new MemoryStream(data)); + var text = reader.ReadToEnd(); + const string expected = @"1,1.2,2.3,3 +4,4.5,5.6,6 +7,7.8,8.9,9"; + Assert.AreEqual(expected, text); + } + + [Test] + public void CanWriteSpaceDelimitedData() + { + var matrix = new SparseMatrix(new[,] { { 1.1f, 0, 0 }, { 0, 5.5f, 0 }, { 0, 0, 9.9f } }); + var writer = new DelimitedWriter(' '); + var stream = new MemoryStream(); + writer.WriteMatrix(matrix, stream); + var data = stream.ToArray(); + var reader = new StreamReader(new MemoryStream(data)); + var text = reader.ReadToEnd(); + const string expected = @"1.1 0 0 +0 5.5 0 +0 0 9.9"; + Assert.AreEqual(expected, text); + } + + [Test] + public void CanWriteTabDelimitedData() + { + var matrix = new UserDefinedMatrix(new[,] { { 1.1f, 2.2f, 3.3f }, { 4.4f, 5.5f, 6.6f }, { 7.7f, 8.8f, 9.9f } }); + var headers = new[] { "a", "b", "c" }; + var writer = new DelimitedWriter('\t') + { + ColumnHeaders = headers + }; + var stream = new MemoryStream(); + writer.WriteMatrix(matrix, stream); + var data = stream.ToArray(); + var reader = new StreamReader(new MemoryStream(data)); + var text = reader.ReadToEnd(); + var expected = "a\tb\tc" + + Environment.NewLine + + "1.1\t2.2\t3.3" + + Environment.NewLine + + "4.4\t5.5\t6.6" + + Environment.NewLine + + "7.7\t8.8\t9.9"; + Assert.AreEqual(expected, text); + } + } +} diff --git a/src/UnitTests/LinearAlgebraTests/Single/IO/MatlabReaderTests.cs b/src/UnitTests/LinearAlgebraTests/Single/IO/MatlabReaderTests.cs new file mode 100644 index 00000000..6b887d97 --- /dev/null +++ b/src/UnitTests/LinearAlgebraTests/Single/IO/MatlabReaderTests.cs @@ -0,0 +1,70 @@ + +namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single.IO +{ + using LinearAlgebra.Single; + using LinearAlgebra.Single.IO; + using MbUnit.Framework; + + [TestFixture] + public class MatlabMatrixReaderTest + { + [Test] + public void CanReadAllMatrices() + { + var dmr = new MatlabMatrixReader("./data/Matlab/collection.mat"); + var matrices = dmr.ReadMatrices(); + Assert.AreEqual(30, matrices.Length); + foreach (var matrix in matrices) + { + Assert.AreEqual(typeof(DenseMatrix), matrix.GetType()); + } + } + + [Test] + public void CanReadFirstMatrix() + { + var dmr = new MatlabMatrixReader("./data/Matlab/A.mat"); + var matrix = dmr.ReadMatrix(); + Assert.AreEqual(100, matrix.RowCount); + Assert.AreEqual(100, matrix.ColumnCount); + Assert.AreEqual(typeof(DenseMatrix), matrix.GetType()); + AssertHelpers.AlmostEqual(100.108979553704, matrix.FrobeniusNorm(), 13); + } + + + [Test] + public void CanReadNamedMatrices() + { + var dmr = new MatlabMatrixReader("./data/Matlab/collection.mat"); + var matrices = dmr.ReadMatrices(new[] { "Ad", "Au64" }); + Assert.AreEqual(2, matrices.Length); + foreach (var matrix in matrices) + { + Assert.AreEqual(typeof(DenseMatrix), matrix.GetType()); + } + } + + [Test] + public void CanReadNamedMatrix() + { + var dmr = new MatlabMatrixReader("./data/Matlab/collection.mat"); + var matrices = dmr.ReadMatrices(new[] { "Ad" }); + Assert.AreEqual(1, matrices.Length); + Assert.AreEqual(100, matrices[0].RowCount); + Assert.AreEqual(100, matrices[0].ColumnCount); + AssertHelpers.AlmostEqual(100.431635988639, matrices[0].FrobeniusNorm(), 13); + Assert.AreEqual(typeof(DenseMatrix), matrices[0].GetType()); + } + + [Test] + public void CanReadNamedSparseMatrix() + { + var dmr = new MatlabMatrixReader("./data/Matlab/sparse-small.mat"); + var matrix = dmr.ReadMatrix("S"); + Assert.AreEqual(100, matrix.RowCount); + Assert.AreEqual(100, matrix.ColumnCount); + Assert.AreEqual(typeof(SparseMatrix), matrix.GetType()); + AssertHelpers.AlmostEqual(17.6385090630805, matrix.FrobeniusNorm(), 12); + } + } +} diff --git a/src/UnitTests/LinearAlgebraTests/Single/LinearAlgebraProviderTests.cs b/src/UnitTests/LinearAlgebraTests/Single/LinearAlgebraProviderTests.cs new file mode 100644 index 00000000..5912c70e --- /dev/null +++ b/src/UnitTests/LinearAlgebraTests/Single/LinearAlgebraProviderTests.cs @@ -0,0 +1,367 @@ +// +// 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. +// + +namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single +{ + using System; + using Algorithms.LinearAlgebra; + using LinearAlgebra.Single; + using LinearAlgebra.Generic; + using MbUnit.Framework; + + [TestFixture] + public abstract class LinearAlgebraProviderTests : MatrixLoader + { + protected ILinearAlgebraProvider Provider{ get; set;} + + private float[] y = new [] { 1.1f, 2.2f, 3.3f, 4.4f, 5.5f }; + private float[] x = new[] { 6.6f, 7.7f, 8.8f, 9.9f, 10.1f }; + + [Test, MultipleAsserts] + public void CanAddVectorToScaledVector() + { + var result = new float[y.Length]; + Array.Copy(y, result, y.Length); + + Provider.AddVectorToScaledVector(result, 0, x); + for (var i = 0; i < y.Length; i++) + { + Assert.AreEqual(y[i], result[i]); + } + + Array.Copy(y, result, y.Length); + Provider.AddVectorToScaledVector(result, 1, x); + for (var i = 0; i < y.Length; i++) + { + Assert.AreEqual(y[i] + x[i], result[i]); + } + + Array.Copy(y, result, y.Length); + Provider.AddVectorToScaledVector(result, (float)Math.PI, x); + for( var i = 0; i < y.Length; i++) + { + Assert.AreEqual(y[i] + (float)Math.PI * x[i], result[i]); + } + } + + [Test, MultipleAsserts] + public void CanScaleArray() + { + var result = new float[y.Length]; + + Array.Copy(y, result, y.Length); + Provider.ScaleArray(1, result); + for (var i = 0; i < y.Length; i++) + { + Assert.AreEqual(y[i], result[i]); + } + + Array.Copy(y, result, y.Length); + Provider.ScaleArray((float)Math.PI, result); + for (var i = 0; i < y.Length; i++) + { + Assert.AreEqual(y[i] * (float)Math.PI, result[i]); + } + } + + [Test] + public void CanComputeDotProduct() + { + var result = Provider.DotProduct(x, y); + AssertHelpers.AlmostEqual(152.35f, result, 15); + } + + [Test] + public void CanAddArrays() + { + var result = new float[y.Length]; + Provider.AddArrays(x, y, result); + for (var i = 0; i < result.Length; i++) + { + Assert.AreEqual(x[i] + y[i], result[i]); + } + } + + [Test] + public void CanSubtractArrays() + { + var result = new float[y.Length]; + Provider.SubtractArrays(x, y, result); + for (var i = 0; i < result.Length; i++) + { + Assert.AreEqual(x[i] - y[i], result[i]); + } + } + + [Test] + public void CanPointWiseMultiplyArrays() + { + var result = new float[y.Length]; + Provider.PointWiseMultiplyArrays(x, y, result); + for (var i = 0; i < result.Length; i++) + { + Assert.AreEqual(x[i] * y[i], result[i]); + } + } + + [Test, Ignore] + public void CanComputeMatrixNorm(Norm norm, float[] matrix){} + + [Test, Ignore] + public void CanComputeMatrixNorm(Norm norm, float[] matrix, float[] work) + { + + } + + [Test, MultipleAsserts] + [Row("Singular3x3", "Square3x3")] + [Row("Singular4x4", "Square4x4")] + [Row("Wide2x3", "Square3x3")] + [Row("Wide2x3", "Tall3x2")] + [Row("Tall3x2", "Wide2x3")] + public void CanMatrixMultiply(string nameX, string nameY) + { + var x = (DenseMatrix)TestMatrices[nameX]; + var y = (DenseMatrix)TestMatrices[nameY]; + var c = (DenseMatrix)CreateMatrix(x.RowCount, y.ColumnCount); + + Provider.MatrixMultiply(x.Data, x.RowCount, x.ColumnCount, y.Data, y.RowCount, y.ColumnCount, c.Data); + + for (int i = 0; i < c.RowCount; i++) + { + for (int j = 0; j < c.ColumnCount; j++) + { + AssertHelpers.AlmostEqual(x.Row(i) * y.Column(j), c[i, j], 7); + } + } + } + + [Test, MultipleAsserts] + [Row("Singular3x3", "Square3x3")] + [Row("Singular4x4", "Square4x4")] + [Row("Wide2x3", "Square3x3")] + [Row("Wide2x3", "Tall3x2")] + [Row("Tall3x2", "Wide2x3")] + public void CanMatrixMultiplyWithUpdate(string nameX, string nameY) + { + var x = (DenseMatrix)TestMatrices[nameX]; + var y = (DenseMatrix)TestMatrices[nameY]; + var c = (DenseMatrix)CreateMatrix(x.RowCount, y.ColumnCount); + + Provider.MatrixMultiplyWithUpdate(Transpose.DontTranspose, Transpose.DontTranspose, 2.0f, x.Data, x.RowCount, x.ColumnCount, y.Data, y.RowCount, y.ColumnCount, 1.0f, c.Data); + + for (int i = 0; i < c.RowCount; i++) + { + for (int j = 0; j < c.ColumnCount; j++) + { + AssertHelpers.AlmostEqual(2.0f * (x.Row(i) * y.Column(j)), c[i, j], 7); + } + } + + Provider.MatrixMultiplyWithUpdate(Transpose.DontTranspose, Transpose.DontTranspose, 2.0f, x.Data, x.RowCount, x.ColumnCount, y.Data, y.RowCount, y.ColumnCount, 1.0f, c.Data); + + for (int i = 0; i < c.RowCount; i++) + { + for (int j = 0; j < c.ColumnCount; j++) + { + AssertHelpers.AlmostEqual(4.0f * (x.Row(i) * y.Column(j)), c[i, j], 7); + } + } + } + + [Test, Ignore] + public void CanComputeLUFactor(float[] a, int[] ipiv) + { + + } + + [Test, Ignore] + public void CanComputeLUInverse(float[] a) + { + + } + + public void CanComputeLUInverseFactored(float[] a, int[] ipiv) + { + + } + + [Test, Ignore] + public void CanComputeLUInverse(float[] a, float[] work) + { + + } + + [Test, Ignore] + public void CanComputeLUInverseFactored(float[] a, int[] ipiv, float[] work) + { + + } + + [Test, Ignore] + public void CanComputeLUSolve(int columnsOfB, float[] a, float[] b) + { + + } + + [Test, Ignore] + public void CanComputeLUSolveFactored(int columnsOfB, float[] a, int ipiv, float[] b) + { + + } + + [Test, Ignore] + public void CanComputeLUSolve(Transpose transposeA, int columnsOfB, float[] a, float[] b) + { + + } + + [Test, Ignore] + public void CanComputeLUSolveFactored(Transpose transposeA, int columnsOfB, float[] a, int ipiv, float[] b) + { + + } + + [Test] + public void CanComputeCholeskyFactor() + { + var matrix = new float[] { 1, 1, 1, 1, 1, 5, 5, 5, 1, 5, 14, 14, 1, 5, 14, 15 }; + Provider.CholeskyFactor(matrix, 4); + Assert.AreEqual(matrix[0], 1); + Assert.AreEqual(matrix[1], 1); + Assert.AreEqual(matrix[2], 1); + Assert.AreEqual(matrix[3], 1); + Assert.AreEqual(matrix[4], 0); + Assert.AreEqual(matrix[5], 2); + Assert.AreEqual(matrix[6], 2); + Assert.AreEqual(matrix[7], 2); + Assert.AreEqual(matrix[8], 0); + Assert.AreEqual(matrix[9], 0); + Assert.AreEqual(matrix[10], 3); + Assert.AreEqual(matrix[11], 3); + Assert.AreEqual(matrix[12], 0); + Assert.AreEqual(matrix[13], 0); + Assert.AreEqual(matrix[14], 0); + Assert.AreEqual(matrix[15], 1); + } + + [Test, Ignore] + public void CanComputeCholeskySolve(int columnsOfB, float[] a, float[] b) + { + + } + + [Test, Ignore] + public void CanComputeCholeskySolveFactored(int columnsOfB, float[] a, float[] b) + { + + } + + [Test, Ignore] + public void CanComputeQRFactor(float[] r, float[] q) + { + + } + + [Test, Ignore] + public void CanComputeQRFactor(float[] r, float[] q, float[] work) + { + + } + + [Test, Ignore] + public void CanComputeQRSolve(int columnsOfB, float[] r, float[] q, float[] b, float[] x) + { + + } + + [Test, Ignore] + public void CanComputeQRSolve(int columnsOfB, float[] r, float[] q, float[] b, float[] x, float[] work) + { + + } + + [Test, Ignore] + public void CanComputeQRSolveFactored(int columnsOfB, float[] q, float[] r, float[] b, float[] x) + { + + } + + [Test, Ignore] + public void CanComputeSinguarValueDecomposition(bool computeVectors, float[] a, float[] s, float[] u, float[] vt) + { + + } + + [Test, Ignore] + public void CanComputeSingularValueDecomposition(bool computeVectors, float[] a, float[] s, float[] u, float[] vt, float[] work) + { + + } + + [Test, Ignore] + public void CanComputeSvdSolve(float[] a, float[] s, float[] u, float[] vt, float[] b, float[] x) + { + + } + + [Test, Ignore] + public void CanComputeSvdSolve(float[] a, float[] s, float[] u, float[] vt, float[] b, float[] x, float[] work) + { + + } + + [Test, Ignore] + public void CanComputeSvdSolveFactored(int columnsOfB, float[] s, float[] u, float[] vt, float[] b, float[] x) + { + + } + + protected override Matrix CreateMatrix(int rows, int columns) + { + return new DenseMatrix(rows, columns); + } + + protected override Matrix CreateMatrix(float[,] data) + { + return new DenseMatrix(data); + } + + protected override Vector CreateVector(int size) + { + return new DenseVector(size); + } + + protected override Vector CreateVector(float[] data) + { + return new DenseVector(data); + } + } +} \ No newline at end of file diff --git a/src/UnitTests/LinearAlgebraTests/Single/ManagedLinearAlgebraProviderTests.cs b/src/UnitTests/LinearAlgebraTests/Single/ManagedLinearAlgebraProviderTests.cs new file mode 100644 index 00000000..f3b90720 --- /dev/null +++ b/src/UnitTests/LinearAlgebraTests/Single/ManagedLinearAlgebraProviderTests.cs @@ -0,0 +1,43 @@ +// +// 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. +// + +namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single +{ + using MbUnit.Framework; + + public class ManagedLinearAlgebraProviderTests : LinearAlgebraProviderTests + { + [FixtureSetUp] + public void SetProvider() + { + Provider = new Algorithms.LinearAlgebra.ManagedLinearAlgebraProvider(); + } + } +} diff --git a/src/UnitTests/LinearAlgebraTests/Single/MatrixLoader.cs b/src/UnitTests/LinearAlgebraTests/Single/MatrixLoader.cs new file mode 100644 index 00000000..682f35a0 --- /dev/null +++ b/src/UnitTests/LinearAlgebraTests/Single/MatrixLoader.cs @@ -0,0 +1,171 @@ +// +// 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. +// + + +namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single +{ + using System.Collections.Generic; + using LinearAlgebra.Single; + using LinearAlgebra.Generic; + using MbUnit.Framework; + + public abstract class MatrixLoader + { + protected Dictionary TestData2D; + protected Dictionary> TestMatrices; + + protected abstract Matrix CreateMatrix(int rows, int columns); + protected abstract Matrix CreateMatrix(float[,] data); + protected abstract Vector CreateVector(int size); + protected abstract Vector CreateVector(float[] data); + + [SetUp] + public virtual void SetupMatrices() + { + TestData2D = new Dictionary + { + { "Singular3x3", new [,] { { 1.0f, 1.0f, 2.0f }, { 1.0f, 1.0f, 2.0f }, { 1.0f, 1.0f, 2.0f } } }, + { "Square3x3", new[,] { { -1.1f, -2.2f, -3.3f }, { 0.0f, 1.1f, 2.2f }, { -4.4f, 5.5f, 6.6f } } }, + { "Square4x4", new[,] { { -1.1f, -2.2f, -3.3f, -4.4f }, { 0.0f, 1.1f, 2.2f, 3.3f }, { 1.0f, 2.1f, 6.2f, 4.3f }, { -4.4f, 5.5f, 6.6f, -7.7f } } }, + { "Singular4x4", new[,] { { -1.1f, -2.2f, -3.3f, -4.4f }, { -1.1f, -2.2f, -3.3f, -4.4f }, { -1.1f, -2.2f, -3.3f, -4.4f }, { -1.1f, -2.2f, -3.3f, -4.4f } } }, + { "Tall3x2", new[,] { { -1.1f, -2.2f }, { 0.0f, 1.1f }, { -4.4f, 5.5f } } }, + { "Wide2x3", new[,] { { -1.1f, -2.2f, -3.3f }, { 0.0f, 1.1f, 2.2f } } }, + }; + + TestMatrices = new Dictionary>(); + foreach (var name in TestData2D.Keys) + { + TestMatrices.Add(name, CreateMatrix(TestData2D[name])); + } + } + + public static Matrix GenerateRandomDenseMatrix(int row, int col) + { + // Fill a matrix with standard random numbers. + var normal = new Distributions.Normal(); + normal.RandomSource = new Random.MersenneTwister(1); + var matrixA = new DenseMatrix(row, col); + for (int i = 0; i < row; i++) + { + for (int j = 0; j < col; j++) + { + matrixA[i, j] = (float)normal.Sample(); + } + } + + // Generate a matrix which is positive definite. + return matrixA; + } + + public static Matrix GenerateRandomPositiveDefiniteDenseMatrix(int order) + { + // Fill a matrix with standard random numbers. + var normal = new Distributions.Normal(); + normal.RandomSource = new Random.MersenneTwister(1); + var matrixA = new DenseMatrix(order); + for (int i = 0; i < order; i++) + { + for (int j = 0; j < order; j++) + { + matrixA[i, j] = (float)normal.Sample(); + } + } + + // Generate a matrix which is positive definite. + return matrixA.Transpose() * matrixA; + } + + public static Vector GenerateRandomDenseVector(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] = (float)normal.Sample(); + } + + // Generate a matrix which is positive definite. + return v; + } + + public static Matrix GenerateRandomUserDefinedMatrix(int row, int col) + { + // Fill a matrix with standard random numbers. + var normal = new Distributions.Normal(); + normal.RandomSource = new Random.MersenneTwister(1); + var matrixA = new UserDefinedMatrix(row, col); + for (int i = 0; i < row; i++) + { + for (int j = 0; j < col; j++) + { + matrixA[i, j] = (float)normal.Sample(); + } + } + + // Generate a matrix which is positive definite. + return matrixA; + } + + public static Matrix GenerateRandomPositiveDefiniteUserDefinedMatrix(int order) + { + // Fill a matrix with standard random numbers. + var normal = new Distributions.Normal(); + normal.RandomSource = new Random.MersenneTwister(1); + var matrixA = new UserDefinedMatrix(order); + for (int i = 0; i < order; i++) + { + for (int j = 0; j < order; j++) + { + matrixA[i, j] = (float)normal.Sample(); + } + } + + // Generate a matrix which is positive definite. + return matrixA.Transpose() * matrixA; + } + + public static Vector GenerateRandomUserDefinedVector(int order) + { + // Fill a matrix with standard random numbers. + var normal = new Distributions.Normal(); + normal.RandomSource = new Random.MersenneTwister(1); + var v = new UserDefinedVector(order); + for (int i = 0; i < order; i++) + { + v[i] = (float)normal.Sample(); + } + + // Generate a matrix which is positive definite. + return v; + } + } +} diff --git a/src/UnitTests/LinearAlgebraTests/Single/MatrixTests.Arithmetic.cs b/src/UnitTests/LinearAlgebraTests/Single/MatrixTests.Arithmetic.cs new file mode 100644 index 00000000..cee17a32 --- /dev/null +++ b/src/UnitTests/LinearAlgebraTests/Single/MatrixTests.Arithmetic.cs @@ -0,0 +1,888 @@ +// +// 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. +// + +namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single +{ + using System; + using Distributions; + using LinearAlgebra.Single; + using LinearAlgebra.Generic; + using MbUnit.Framework; + + public abstract partial class MatrixTests + { + [Test] + [Row(0)] + [Row(1)] + [Row(2.2)] + [MultipleAsserts] + public void CanMultiplyWithScalar(float scalar) + { + var matrix = TestMatrices["Singular3x3"]; + var clone = matrix.Clone(); + clone.Multiply(scalar); + + for (var i = 0; i < matrix.RowCount; i++) + { + for (var j = 0; j < matrix.ColumnCount; j++) + { + Assert.AreEqual(matrix[i, j] * scalar, clone[i, j]); + } + } + } + + [Test] + public void CanMultiplyWithVector() + { + var A = TestMatrices["Singular3x3"]; + var x = new DenseVector(new[] { 1.0f, 2.0f, 3.0f }); + var y = A * x; + + Assert.AreEqual(A.RowCount, y.Count); + + for (var i = 0; i < A.RowCount; i++) + { + var ar = A.Row(i); + var dot = ar * x; + Assert.AreEqual(dot, y[i]); + } + } + + [Test] + public void CanMultiplyWithVectorIntoResult() + { + var A = TestMatrices["Singular3x3"]; + var x = new DenseVector(new[] { 1.0f, 2.0f, 3.0f }); + var y = new DenseVector(3); + A.Multiply(x, y); + + for (var i = 0; i < A.RowCount; i++) + { + var ar = A.Row(i); + var dot = ar * x; + Assert.AreEqual(dot, y[i]); + } + } + + [Test] + public void CanMultiplyWithVectorIntoResultWhenUpdatingInputArgument() + { + var A = TestMatrices["Singular3x3"]; + var x = new DenseVector(new[] { 1.0f, 2.0f, 3.0f }); + var y = x; + A.Multiply(x, x); + + Assert.AreSame(y, x); + + y = new DenseVector(new[] { 1.0f, 2.0f, 3.0f }); + for (var i = 0; i < A.RowCount; i++) + { + var ar = A.Row(i); + var dot = ar * y; + Assert.AreEqual(dot, x[i]); + } + } + + [Test] + [ExpectedArgumentNullException] + public void MultiplyWithVectorIntoResultFailsWhenResultIsNull() + { + var A = TestMatrices["Singular3x3"]; + var x = new DenseVector(new[] { 1.0f, 2.0f, 3.0f }); + Vector y = null; + A.Multiply(x, y); + } + + [Test] + [ExpectedArgumentException] + public void MultiplyWithVectorIntoResultFailsWhenResultIsTooLarge() + { + var A = TestMatrices["Singular3x3"]; + var x = new DenseVector(new[] { 1.0f, 2.0f, 3.0f }); + Vector y = new DenseVector(4); + A.Multiply(x, y); + } + + [Test] + [Row(0)] + [Row(1)] + [Row(2.2)] + [MultipleAsserts] + public void CanOperatorLeftMultiplyWithScalar(float scalar) + { + var matrix = TestMatrices["Singular3x3"]; + var clone = matrix * scalar; + + for (var i = 0; i < matrix.RowCount; i++) + { + for (var j = 0; j < matrix.ColumnCount; j++) + { + Assert.AreEqual(matrix[i, j] * scalar, clone[i, j]); + } + } + } + + [Test] + [Row(0)] + [Row(1)] + [Row(2.2)] + [MultipleAsserts] + public void CanOperatorRightMultiplyWithScalar(float scalar) + { + var matrix = TestMatrices["Singular3x3"]; + var clone = matrix * scalar; + + for (var i = 0; i < matrix.RowCount; i++) + { + for (var j = 0; j < matrix.ColumnCount; j++) + { + Assert.AreEqual(matrix[i, j] * scalar, clone[i, j]); + } + } + } + + [Test] + [Row(0)] + [Row(1)] + [Row(2.2)] + [MultipleAsserts] + public void CanMultiplyWithScalarIntoResult(float scalar) + { + var matrix = TestMatrices["Singular3x3"]; + var result = matrix.Clone(); + matrix.Multiply(scalar, result); + + for (var i = 0; i < matrix.RowCount; i++) + { + for (var j = 0; j < matrix.ColumnCount; j++) + { + Assert.AreEqual(matrix[i, j] * scalar, result[i, j]); + } + } + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void MultiplyWithScalarIntoResultFailsWhenResultIsNull() + { + var matrix = TestMatrices["Singular3x3"]; + Matrix result = null; + matrix.Multiply(2.3f, result); + } + + [Test] + [ExpectedArgumentException] + public void MultiplyWithScalarFailsWhenResultHasMoreRows() + { + var matrix = TestMatrices["Singular3x3"]; + var result = CreateMatrix(matrix.RowCount + 1, matrix.ColumnCount); + matrix.Multiply(2.3f, result); + } + + [Test] + [ExpectedArgumentException] + public void MultiplyWithScalarFailsWhenResultHasMoreColumns() + { + var matrix = TestMatrices["Singular3x3"]; + var result = CreateMatrix(matrix.RowCount, matrix.ColumnCount + 1); + matrix.Multiply(2.3f, result); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void OperatorLeftMultiplyWithScalarFailsWhenMatrixIsNull() + { + Matrix matrix = null; + var result = 2.3f * matrix; + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void OperatorRightMultiplyWithScalarFailsWhenMatrixIsNull() + { + Matrix matrix = null; + var result = matrix * 2.3f; + } + + [Test] + [Row("Singular3x3", "Square3x3")] + [Row("Singular4x4", "Square4x4")] + public void CanAddMatrix(string mtxA, string mtxB) + { + var A = TestMatrices[mtxA]; + var B = TestMatrices[mtxB]; + + var matrix = A.Clone(); + matrix.Add(B); + for (var i = 0; i < matrix.RowCount; i++) + { + for (var j = 0; j < matrix.ColumnCount; j++) + { + Assert.AreEqual(matrix[i, j], A[i, j] + B[i, j]); + } + } + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void AddMatrixThrowsExceptionWhenArgumentIsNull() + { + var matrix = TestMatrices["Singular4x4"]; + Matrix other = null; + matrix.Add(other); + } + + [Test] + [ExpectedException(typeof(ArgumentOutOfRangeException))] + public void AddMatrixThrowsExceptionArgumentHasTooFewColumns() + { + var matrix = TestMatrices["Singular3x3"]; + var other = TestMatrices["Tall3x2"]; + matrix.Add(other); + } + + [Test] + [ExpectedException(typeof(ArgumentOutOfRangeException))] + public void AddMatrixThrowsExceptionArgumentHasTooFewRows() + { + var matrix = TestMatrices["Singular3x3"]; + var other = TestMatrices["Wide2x3"]; + matrix.Add(other); + } + + [Test] + [Row("Singular3x3", "Square3x3")] + [Row("Singular4x4", "Square4x4")] + public void AddOperator(string mtxA, string mtxB) + { + var A = TestMatrices[mtxA]; + var B = TestMatrices[mtxB]; + + var result = A + B; + for (var i = 0; i < A.RowCount; i++) + { + for (var j = 0; j < A.ColumnCount; j++) + { + Assert.AreEqual(result[i, j], A[i, j] + B[i, j]); + } + } + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void AddOperatorThrowsExceptionWhenLeftsideIsNull() + { + Matrix matrix = null; + var other = TestMatrices["Singular3x3"]; + var result = matrix + other; + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void AddOperatorThrowsExceptionWhenRightsideIsNull() + { + var matrix = TestMatrices["Singular3x3"]; + Matrix other = null; + var result = matrix + other; + } + + [Test] + [ExpectedException(typeof(ArgumentOutOfRangeException))] + public void AddOperatorThrowsExceptionWhenRightsideHasTooFewColumns() + { + var matrix = TestMatrices["Singular3x3"]; + var other = TestMatrices["Tall3x2"]; + var result = matrix + other; + } + + [Test] + [ExpectedException(typeof(ArgumentOutOfRangeException))] + public void AddOperatorThrowsExceptionWhenRightsideHasTooFewRows() + { + var matrix = TestMatrices["Singular3x3"]; + var other = TestMatrices["Wide2x3"]; + var result = matrix + other; + } + + [Test] + [Row("Singular3x3", "Square3x3")] + [Row("Singular4x4", "Square4x4")] + public void CanSubtractMatrix(string mtxA, string mtxB) + { + var A = TestMatrices[mtxA]; + var B = TestMatrices[mtxB]; + + var matrix = A.Clone(); + matrix.Subtract(B); + for (var i = 0; i < matrix.RowCount; i++) + { + for (var j = 0; j < matrix.ColumnCount; j++) + { + Assert.AreEqual(matrix[i, j], A[i, j] - B[i, j]); + } + } + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void SubtractMatrixThrowsExceptionWhenRightSideIsNull() + { + var matrix = TestMatrices["Singular4x4"]; + Matrix other = null; + matrix.Subtract(other); + } + + [Test] + [ExpectedException(typeof(ArgumentOutOfRangeException))] + public void SubtractMatrixThrowsExceptionWhenRightSideHasTooFewColumns() + { + var matrix = TestMatrices["Singular3x3"]; + var other = TestMatrices["Tall3x2"]; + matrix.Subtract(other); + } + + [Test] + [ExpectedException(typeof(ArgumentOutOfRangeException))] + public void SubtractMatrixThrowsExceptionWhenRightSideHasTooFewRows() + { + var matrix = TestMatrices["Singular3x3"]; + var other = TestMatrices["Wide2x3"]; + matrix.Subtract(other); + } + + [Test] + [Row("Singular3x3", "Square3x3")] + [Row("Singular4x4", "Square4x4")] + public void SubtractOperator(string mtxA, string mtxB) + { + var A = TestMatrices[mtxA]; + var B = TestMatrices[mtxB]; + + var result = A - B; + for (var i = 0; i < A.RowCount; i++) + { + for (var j = 0; j < A.ColumnCount; j++) + { + Assert.AreEqual(result[i, j], A[i, j] - B[i, j]); + } + } + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void SubtractOperatorThrowsExceptionWhenLeftsideIsNull() + { + Matrix matrix = null; + var other = TestMatrices["Singular3x3"]; + var result = matrix - other; + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void SubtractOperatorThrowsExceptionWhenRightsideIsNull() + { + var matrix = TestMatrices["Singular3x3"]; + Matrix other = null; + var result = matrix - other; + } + + [Test] + [ExpectedException(typeof(ArgumentOutOfRangeException))] + public void SubtractOperatorThrowsExceptionWhenRightsideHasTooFewColumns() + { + var matrix = TestMatrices["Singular3x3"]; + var other = TestMatrices["Tall3x2"]; + var result = matrix - other; + } + + [Test] + [ExpectedException(typeof(ArgumentOutOfRangeException))] + public void SubtractOperatorThrowsExceptionWhenRightsideHasTooFewRows() + { + var matrix = TestMatrices["Singular3x3"]; + var other = TestMatrices["Wide2x3"]; + var result = matrix - other; + } + + [Test] + [Row("Singular3x3", "Square3x3")] + [Row("Singular4x4", "Square4x4")] + [Row("Wide2x3", "Square3x3")] + [Row("Wide2x3", "Tall3x2")] + [Row("Tall3x2", "Wide2x3")] + [MultipleAsserts] + public void CanMultiplyMatrixWithMatrix(string nameA, string nameB) + { + var A = TestMatrices[nameA]; + var B = TestMatrices[nameB]; + var C = A * B; + + Assert.AreEqual(C.RowCount, A.RowCount); + Assert.AreEqual(C.ColumnCount, B.ColumnCount); + + for (var i = 0; i < C.RowCount; i++) + { + for (var j = 0; j < C.ColumnCount; j++) + { + AssertHelpers.AlmostEqual(A.Row(i) * B.Column(j), C[i, j], 7); + } + } + } + + [Test] + [Row("Singular3x3")] + [Row("Singular4x4")] + [Row("Wide2x3")] + [Row("Tall3x2")] + [MultipleAsserts] + public void CanTransposeAndMultiplyMatrixWithMatrix(string nameA) + { + var A = TestMatrices[nameA]; + var B = TestMatrices[nameA]; + var C = A.TransposeAndMultiply(B); + + Assert.AreEqual(C.RowCount, A.RowCount); + Assert.AreEqual(C.ColumnCount, B.RowCount); + + for (var i = 0; i < C.RowCount; i++) + { + for (var j = 0; j < C.ColumnCount; j++) + { + AssertHelpers.AlmostEqual(A.Row(i) * B.Row(j), C[i, j], 7); + } + } + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void TransposeAndMultiplyMatrixMatrixFailsWhenSizesAreIncompatible() + { + var matrix = TestMatrices["Singular3x3"]; + var other = TestMatrices["Tall3x2"]; + var result = matrix.TransposeAndMultiply(other); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void TransposeAndMultiplyMatrixMatrixFailsWhenRightArgumentIsNull() + { + var matrix = TestMatrices["Wide2x3"]; + Matrix other = null; + var result = matrix.TransposeAndMultiply(other); + } + + [Test] + [Row("Singular3x3")] + [Row("Singular4x4")] + [Row("Wide2x3")] + [Row("Wide2x3")] + [Row("Tall3x2")] + [MultipleAsserts] + public void CanTransposeAndMultiplyMatrixWithMatrixIntoResult(string nameA) + { + var A = TestMatrices[nameA]; + var B = TestMatrices[nameA]; + var C = CreateMatrix(A.RowCount, B.RowCount); + A.TransposeAndMultiply(B, C); + + Assert.AreEqual(C.RowCount, A.RowCount); + Assert.AreEqual(C.ColumnCount, B.RowCount); + + for (var i = 0; i < C.RowCount; i++) + { + for (var j = 0; j < C.ColumnCount; j++) + { + AssertHelpers.AlmostEqual(A.Row(i) * B.Row(j), C[i, j], 7); + } + } + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void MultiplyMatrixMatrixFailsWhenSizesAreIncompatible() + { + var matrix = TestMatrices["Singular3x3"]; + var other = TestMatrices["Wide2x3"]; + var result = matrix * other; + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void MultiplyMatrixMatrixFailsWhenLeftArgumentIsNull() + { + Matrix matrix = null; + var other = TestMatrices["Wide2x3"]; + var result = matrix * other; + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void MultiplyMatrixMatrixFailsWhenRightArgumentIsNull() + { + var matrix = TestMatrices["Wide2x3"]; + Matrix other = null; + var result = matrix * other; + } + + [Test] + [Row("Singular3x3", "Square3x3")] + [Row("Singular4x4", "Square4x4")] + [Row("Wide2x3", "Square3x3")] + [Row("Wide2x3", "Tall3x2")] + [Row("Tall3x2", "Wide2x3")] + [MultipleAsserts] + public virtual void CanMultiplyMatrixWithMatrixIntoResult(string nameA, string nameB) + { + var A = TestMatrices[nameA]; + var B = TestMatrices[nameB]; + var C = CreateMatrix(A.RowCount, B.ColumnCount); + A.Multiply(B, C); + + Assert.AreEqual(C.RowCount, A.RowCount); + Assert.AreEqual(C.ColumnCount, B.ColumnCount); + + for (var i = 0; i < C.RowCount; i++) + { + for (var j = 0; j < C.ColumnCount; j++) + { + AssertHelpers.AlmostEqual(A.Row(i) * B.Column(j), C[i, j], 7); + } + } + } + + [Test] + [Row("Singular3x3")] + [Row("Square3x3")] + [Row("Square4x4")] + [Row("Tall3x2")] + [Row("Wide2x3")] + [MultipleAsserts] + public void CanNegate(string name) + { + var matrix = TestMatrices[name]; + var copy = matrix.Clone(); + + copy.Negate(); + + for (var i = 0; i < matrix.RowCount; i++) + { + for (var j = 0; j < matrix.ColumnCount; j++) + { + Assert.AreEqual(-matrix[i, j], copy[i, j]); + } + } + } + + [Test] + [Row("Singular3x3")] + [Row("Square3x3")] + [Row("Square4x4")] + [Row("Tall3x2")] + [Row("Wide2x3")] + [MultipleAsserts] + public void CanNegateIntoResult(string name) + { + var matrix = TestMatrices[name]; + var copy = matrix.Clone(); + + matrix.Negate(copy); + + for (var i = 0; i < matrix.RowCount; i++) + { + for (var j = 0; j < matrix.ColumnCount; j++) + { + Assert.AreEqual(-matrix[i, j], copy[i, j]); + } + } + } + + [Test] + [ExpectedArgumentNullException] + public void NegateIntoResultFailsWhenResultIsNull() + { + var matrix = TestMatrices["Singular3x3"]; + Matrix copy = null; + matrix.Negate(copy); + } + + [Test] + [ExpectedArgumentException] + public void NegateIntoResultFailsWhenResultHasMoreRows() + { + var matrix = TestMatrices["Singular3x3"]; + var target = CreateMatrix(matrix.RowCount + 1, matrix.ColumnCount); + matrix.Negate(target); + } + + [Test] + [ExpectedArgumentException] + public void NegateIntoResultFailsWhenResultHasMoreColumns() + { + var matrix = TestMatrices["Singular3x3"]; + var target = CreateMatrix(matrix.RowCount + 1, matrix.ColumnCount); + matrix.Negate(target); + } + + + [Test] + public void KroneckerProduct() + { + var A = TestMatrices["Wide2x3"]; + var B = TestMatrices["Square3x3"]; + var result = CreateMatrix(A.RowCount * B.RowCount, A.ColumnCount * B.ColumnCount); + A.KroneckerProduct(B, result); + for (var i = 0; i < A.RowCount; i++) + { + for (var j = 0; j < A.ColumnCount; j++) + { + for (var ii = 0; ii < B.RowCount; ii++) + { + for (var jj = 0; jj < B.ColumnCount; jj++) + { + Assert.AreEqual(result[i * B.RowCount + ii, j * B.ColumnCount + jj], A[i, j] * B[ii, jj]); + } + } + } + } + } + + [Test] + public void KroneckerProductResult() + { + var A = TestMatrices["Wide2x3"]; + var B = TestMatrices["Square3x3"]; + var result = A.KroneckerProduct(B); + for (var i = 0; i < A.RowCount; i++) + { + for (var j = 0; j < A.ColumnCount; j++) + { + for (var ii = 0; ii < B.RowCount; ii++) + { + for (var jj = 0; jj < B.ColumnCount; jj++) + { + Assert.AreEqual(result[i * B.RowCount + ii, j * B.ColumnCount + jj], A[i, j] * B[ii, jj]); + } + } + } + } + } + + [Test] + [Row(1)] + [Row(2)] + [Row(-4, ExpectedException = typeof(ArgumentOutOfRangeException))] + public void NormalizeColumns(int pValue) + { + var matrix = TestMatrices["Square4x4"]; + var result = matrix.NormalizeColumns(pValue); + for (var j = 0; j < result.ColumnCount; j++) + { + var col = result.Column(j); + Assert.AreApproximatelyEqual(1.0f, (float)col.Norm(pValue), 10e-6f); + } + } + + [Test] + [Row(1)] + [Row(2)] + [Row(-3, ExpectedException = typeof(ArgumentOutOfRangeException))] + public void NormalizeRows(int pValue) + { + var matrix = TestMatrices["Square4x4"].NormalizeRows(pValue); + for (var i = 0; i < matrix.RowCount; i++) + { + var row = matrix.Row(i); + Assert.AreApproximatelyEqual(1.0f, (float)row.Norm(pValue), 10e-6f); + } + } + + [Test] + public void PointwiseMultiplyResult() + { + foreach (var data in TestMatrices.Values) + { + var other = data.Clone(); + var result = data.Clone(); + data.PointwiseMultiply(other, result); + for (var i = 0; i < data.RowCount; i++) + { + for (var j = 0; j < data.ColumnCount; j++) + { + Assert.AreEqual(data[i, j] * other[i, j], result[i, j]); + } + } + + result = data.PointwiseMultiply(other); + for (var i = 0; i < data.RowCount; i++) + { + for (var j = 0; j < data.ColumnCount; j++) + { + Assert.AreEqual(data[i, j] * other[i, j], result[i, j]); + } + } + } + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void PointwiseMultiplyWithNullOtherShouldThrowException() + { + var matrix = TestMatrices["Wide2x3"]; + Matrix other = null; + var result = matrix.Clone(); + matrix.PointwiseMultiply(other, result); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void PointwiseMultiplyWithResultNullShouldThrowException() + { + var matrix = TestMatrices["Wide2x3"]; + var other = matrix.Clone(); + matrix.PointwiseMultiply(other, null); + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void PointwiseMultiplyWithInvalidOtherMatrixDimensionsShouldThrowException() + { + var matrix = TestMatrices["Wide2x3"]; + var other = CreateMatrix(matrix.RowCount + 1, matrix.ColumnCount); + var result = matrix.Clone(); + matrix.PointwiseMultiply(other, result); + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void PointwiseMultiplyWithInvalidResultMatrixDimensionsShouldThrowException() + { + var matrix = TestMatrices["Wide2x3"]; + var other = matrix.Clone(); + var result = CreateMatrix(matrix.RowCount + 1, matrix.ColumnCount); + matrix.PointwiseMultiply(other, result); + } + + [Test] + public virtual void PointwiseDivideResult() + { + foreach (var data in TestMatrices.Values) + { + var other = data.Clone(); + var result = data.Clone(); + data.PointwiseDivide(other, result); + for (var i = 0; i < data.RowCount; i++) + { + for (var j = 0; j < data.ColumnCount; j++) + { + Assert.AreEqual(data[i, j] / other[i, j], result[i, j]); + } + } + + result = data.PointwiseDivide(other); + for (var i = 0; i < data.RowCount; i++) + { + for (var j = 0; j < data.ColumnCount; j++) + { + Assert.AreEqual(data[i, j] / other[i, j], result[i, j]); + } + } + } + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void PointwiseDivideWithNullOtherShouldThrowException() + { + var matrix = TestMatrices["Wide2x3"]; + Matrix other = null; + var result = matrix.Clone(); + matrix.PointwiseDivide(other, result); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void PointwiseDivideWithResultNullShouldThrowException() + { + var matrix = TestMatrices["Wide2x3"]; + var other = matrix.Clone(); + matrix.PointwiseDivide(other, null); + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void PointwiseDivideWithInvalidOtherMatrixDimensionsShouldThrowException() + { + var matrix = TestMatrices["Wide2x3"]; + var other = CreateMatrix(matrix.RowCount + 1, matrix.ColumnCount); + var result = matrix.Clone(); + matrix.PointwiseDivide(other, result); + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void PointwiseDivideWithInvalidResultMatrixDimensionsShouldThrowException() + { + var matrix = TestMatrices["Wide2x3"]; + var other = matrix.Clone(); + var result = CreateMatrix(matrix.RowCount + 1, matrix.ColumnCount); + matrix.PointwiseDivide(other, result); + } + + [Test] + [Row(0, ExpectedException = typeof(ArgumentException))] + [Row(-2, ExpectedException = typeof(ArgumentException))] + public void RandomWithNonPositiveNumberOfRowsShouldThrowException(int numberOfRows) + { + var matrix = CreateMatrix(2, 3); + matrix = matrix.Random(numberOfRows, 4, new ContinuousUniform()); + } + + [Test] + [Row(0, ExpectedException = typeof(ArgumentException))] + [Row(-2, ExpectedException = typeof(ArgumentException))] + public void RandomWithNonPositiveNumberOfRowsShouldThrowException2(int numberOfRows) + { + var matrix = CreateMatrix(2, 3); + matrix = matrix.Random(numberOfRows, 4, new DiscreteUniform(0, 2)); + } + + [Test] + public void Trace() + { + var matrix = TestMatrices["Square3x3"]; + var trace = matrix.Trace(); + Assert.AreEqual(6.6f, trace); + } + + [Test] + [ExpectedArgumentException] + public void TraceOfNonSquareMatrixShouldThrowException() + { + var matrix = TestMatrices["Wide2x3"]; + var trace = matrix.Trace(); + } + } +} \ No newline at end of file diff --git a/src/UnitTests/LinearAlgebraTests/Single/MatrixTests.cs b/src/UnitTests/LinearAlgebraTests/Single/MatrixTests.cs new file mode 100644 index 00000000..5fe55245 --- /dev/null +++ b/src/UnitTests/LinearAlgebraTests/Single/MatrixTests.cs @@ -0,0 +1,1482 @@ +// +// 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. +// + +namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single +{ + using System; + using LinearAlgebra.Generic; + using MbUnit.Framework; + + [TestFixture] + public abstract partial class MatrixTests : MatrixLoader + { + [Test] + [Row("Singular3x3")] + [Row("Square3x3")] + [Row("Square4x4")] + [Row("Tall3x2")] + [Row("Wide2x3")] + [MultipleAsserts] + public void CanCloneMatrix(string name) + { + var matrix = CreateMatrix(TestData2D[name]); + var clone = matrix.Clone(); + + Assert.AreNotSame(matrix, clone); + Assert.AreEqual(matrix.RowCount, clone.RowCount); + Assert.AreEqual(matrix.ColumnCount, clone.ColumnCount); + for (var i = 0; i < matrix.RowCount; i++) + { + for (var j = 0; j < matrix.ColumnCount; j++) + { + Assert.AreEqual(matrix[i, j], clone[i, j]); + } + } + } + + [Test] + [Row("Singular3x3")] + [Row("Square3x3")] + [Row("Square4x4")] + [Row("Tall3x2")] + [Row("Wide2x3")] + [MultipleAsserts] + public void CanCloneMatrixUsingICloneable(string name) + { + var matrix = TestMatrices[name]; + var clone = (Matrix)((ICloneable)matrix).Clone(); + + Assert.AreNotSame(matrix, clone); + Assert.AreEqual(matrix.RowCount, clone.RowCount); + Assert.AreEqual(matrix.ColumnCount, clone.ColumnCount); + for (var i = 0; i < matrix.RowCount; i++) + { + for (var j = 0; j < matrix.ColumnCount; j++) + { + Assert.AreEqual(matrix[i, j], clone[i, j]); + } + } + } + + [Test] + [Row("Singular3x3")] + [Row("Square3x3")] + [Row("Square4x4")] + [Row("Tall3x2")] + [Row("Wide2x3")] + [MultipleAsserts] + public void CanCopyTo(string name) + { + var matrix = TestMatrices[name]; + var copy = CreateMatrix(matrix.RowCount, matrix.ColumnCount); + matrix.CopyTo(copy); + + Assert.AreNotSame(matrix, copy); + for (var i = 0; i < matrix.RowCount; i++) + { + for (var j = 0; j < matrix.ColumnCount; j++) + { + Assert.AreEqual(matrix[i, j], copy[i, j]); + } + } + } + + [Test] + [ExpectedArgumentNullException] + public void CopyToFailsWhenTargetIsNull() + { + var matrix = TestMatrices["Singular3x3"]; + Matrix target = null; + matrix.CopyTo(target); + } + + [Test] + [ExpectedArgumentException] + public void CopyToFailsWhenTargetHasMoreRows() + { + var matrix = TestMatrices["Singular3x3"]; + var target = CreateMatrix(matrix.RowCount + 1, matrix.ColumnCount); + matrix.CopyTo(target); + } + + [Test] + [ExpectedArgumentException] + public void CopyToFailsWhenTargetHasMoreColumns() + { + var matrix = TestMatrices["Singular3x3"]; + var target = CreateMatrix(matrix.RowCount + 1, matrix.ColumnCount); + matrix.CopyTo(target); + } + + [Test] + [Ignore] + public void CanConvertVectorToString() + { + } + + [Test] + public void CanCreateMatrix() + { + var expected = CreateMatrix(5, 6); + var actual = expected.CreateMatrix(5, 6); + Assert.AreEqual(expected.GetType(), actual.GetType(), "Matrices are same type."); + } + + [Test] + [Row("Singular3x3")] + [Row("Square3x3")] + [Row("Square4x4")] + [Row("Tall3x2")] + [Row("Wide2x3")] + [MultipleAsserts] + public void CanEquateMatrices(string name) + { + var matrix1 = CreateMatrix(TestData2D[name]); + var matrix2 = CreateMatrix(TestData2D[name]); + var matrix3 = CreateMatrix(TestData2D[name].GetLength(0), TestData2D[name].GetLength(1)); + Assert.IsTrue(matrix1.Equals(matrix1)); + Assert.IsTrue(matrix1.Equals(matrix2)); + Assert.IsFalse(matrix1.Equals(matrix3)); + Assert.IsFalse(matrix1.Equals(null)); + } + + [Test] + [Row(0, 2)] + [Row(2, 0)] + [Row(0, 0)] + [Row(-1, 1)] + [Row(1, -1)] + [ExpectedArgumentOutOfRangeException] + public void ThrowsArgumentExceptionIfSizeIsNotPositive(int rows, int columns) + { + var A = CreateMatrix(rows, columns); + } + + [Test] + [Row("Singular3x3")] + [Row("Square3x3")] + [Row("Square4x4")] + [Row("Tall3x2")] + [Row("Wide2x3")] + public void TestingForEqualityWithNonMatrixReturnsFalse(string name) + { + var matrix = CreateMatrix(TestData2D[name]); + Assert.IsFalse(matrix.Equals(2)); + } + + [Test] + [Row("Singular3x3")] + [Row("Square3x3")] + [Row("Square4x4")] + [Row("Tall3x2")] + [Row("Wide2x3")] + public void CanTestForEqualityUsingObjectEquals(string name) + { + var matrix1 = CreateMatrix(TestData2D[name]); + var matrix2 = CreateMatrix(TestData2D[name]); + Assert.IsTrue(matrix1.Equals((object)matrix2)); + } + + [Test] + [Row(-1, 1, "Singular3x3")] + [Row(1, -1, "Singular3x3")] + [Row(4, 2, "Square3x3")] + [ExpectedException(typeof(ArgumentOutOfRangeException))] + public void RangeCheckFails(int i, int j, string name) + { + var d = TestMatrices[name][i, j]; + } + + [Test] + [Ignore] + public void MatrixGetHashCode() + { + } + + [Test] + public void CanClearMatrix() + { + var matrix = TestMatrices["Singular3x3"].Clone(); + matrix.Clear(); + for (var i = 0; i < matrix.RowCount; i++) + { + for (var j = 0; j < matrix.ColumnCount; j++) + { + Assert.AreEqual(0, matrix[i, j]); + } + } + } + + [Test] + [Row(0, "Singular3x3")] + [Row(1, "Singular3x3")] + [Row(2, "Singular3x3")] + [Row(2, "Square3x3")] + public void CanGetRow(int rowIndex, string name) + { + var matrix = TestMatrices[name]; + var row = matrix.Row(rowIndex); + + Assert.AreEqual(matrix.ColumnCount, row.Count); + for (var j = 0; j < matrix.ColumnCount; j++) + { + Assert.AreEqual(matrix[rowIndex, j], row[j]); + } + } + + [Test] + [ExpectedException(typeof(ArgumentOutOfRangeException))] + public void GetRowThrowsArgumentOutOfRangeWithNegativeIndex() + { + var matrix = TestMatrices["Singular3x3"]; + matrix.Row(-1); + } + + [Test] + [ExpectedException(typeof(ArgumentOutOfRangeException))] + public void GetRowThrowsArgumentOutOfRangeWithOverflowingRowIndex() + { + var matrix = TestMatrices["Singular3x3"]; + matrix.Row(matrix.RowCount); + } + + [Test] + [Row(0, "Singular3x3")] + [Row(1, "Singular3x3")] + [Row(2, "Singular3x3")] + [Row(2, "Square3x3")] + public void CanGetRowWithResult(int rowIndex, string name) + { + var matrix = TestMatrices[name]; + var row = CreateVector(matrix.ColumnCount); + matrix.Row(rowIndex, row); + + Assert.AreEqual(matrix.ColumnCount, row.Count); + for (var j = 0; j < matrix.ColumnCount; j++) + { + Assert.AreEqual(matrix[rowIndex, j], row[j]); + } + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void GetRowWithResultFailsWhenResultIsNull() + { + var matrix = TestMatrices["Singular3x3"]; + matrix.Row(0, null); + } + + [Test] + [ExpectedException(typeof(ArgumentOutOfRangeException))] + public void GetRowWithResultThrowsArgumentOutOfRangeWithNegativeIndex() + { + var matrix = TestMatrices["Singular3x3"]; + var row = CreateVector(matrix.ColumnCount); + matrix.Row(-1, row); + } + + [Test] + [ExpectedException(typeof(ArgumentOutOfRangeException))] + public void GetRowWithResultThrowsArgumentOutOfRangeWithOverflowingRowIndex() + { + var matrix = TestMatrices["Singular3x3"]; + var row = CreateVector(matrix.ColumnCount); + matrix.Row(matrix.RowCount, row); + } + + [Test] + [Row(0, 0, 1, "Singular3x3")] + [Row(1, 1, 2, "Singular3x3")] + [Row(2, 0, 3, "Singular3x3")] + [Row(2, 0, 3, "Square3x3")] + public void CanGetRowWithRange(int rowIndex, int start, int length, string name) + { + var matrix = TestMatrices[name]; + var row = matrix.Row(rowIndex, start, length); + + Assert.AreEqual(length, row.Count); + for (var j = start; j < start + length; j++) + { + Assert.AreEqual(matrix[rowIndex, j], row[j - start]); + } + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void GetRowWithRangeResultArgumentExeptionWhenLengthIsZero() + { + var matrix = TestMatrices["Singular3x3"]; + var result = CreateVector(matrix.ColumnCount); + matrix.Row(0, 0, 0, result); + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void GetRowWithRangeFailsWithTooSmallResultVector() + { + var matrix = TestMatrices["Singular3x3"]; + var result = CreateVector(matrix.ColumnCount - 1); + matrix.Row(0, 0, 0, result); + } + + [Test] + [Row(0, "Singular3x3")] + [Row(1, "Singular3x3")] + [Row(2, "Singular3x3")] + [Row(2, "Square3x3")] + public void CanGetColumn(int colIndex, string name) + { + var matrix = TestMatrices[name]; + var col = matrix.Column(colIndex); + + Assert.AreEqual(matrix.RowCount, col.Count); + for (var j = 0; j < matrix.RowCount; j++) + { + Assert.AreEqual(matrix[j, colIndex], col[j]); + } + } + + [Test] + [ExpectedException(typeof(ArgumentOutOfRangeException))] + public void GetColumnThrowsArgumentOutOfRangeWithNegativeIndex() + { + var matrix = TestMatrices["Singular3x3"]; + matrix.Column(-1); + } + + [Test] + [ExpectedException(typeof(ArgumentOutOfRangeException))] + public void GetColumnThrowsArgumentOutOfRangeWithOverflowingRowIndex() + { + var matrix = TestMatrices["Singular3x3"]; + matrix.Column(matrix.ColumnCount); + } + + [Test] + [Row(0, "Singular3x3")] + [Row(1, "Singular3x3")] + [Row(2, "Singular3x3")] + [Row(2, "Square3x3")] + public void CanGetColumnWithResult(int colIndex, string name) + { + var matrix = TestMatrices[name]; + var col = CreateVector(matrix.RowCount); + matrix.Column(colIndex, col); + + Assert.AreEqual(matrix.RowCount, col.Count); + for (var j = 0; j < matrix.RowCount; j++) + { + Assert.AreEqual(matrix[j, colIndex], col[j]); + } + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void GetColumnFailsWhenResultIsNull() + { + var matrix = TestMatrices["Singular3x3"]; + matrix.Column(0, null); + } + + [Test] + [ExpectedException(typeof(ArgumentOutOfRangeException))] + public void GetColumnWithResultThrowsArgumentOutOfRangeWithNegativeIndex() + { + var matrix = TestMatrices["Singular3x3"]; + var column = CreateVector(matrix.ColumnCount); + matrix.Column(-1, column); + } + + [Test] + [ExpectedException(typeof(ArgumentOutOfRangeException))] + public void GetColumnWithResultThrowsArgumentOutOfRangeWithOverflowingRowIndex() + { + var matrix = TestMatrices["Singular3x3"]; + var column = CreateVector(matrix.RowCount); + matrix.Row(matrix.ColumnCount, column); + } + + [Test] + [Row(0, 0, 1, "Singular3x3")] + [Row(1, 1, 2, "Singular3x3")] + [Row(2, 0, 3, "Singular3x3")] + [Row(2, 0, 3, "Square3x3")] + public void CanGetColumnWithRange(int colIndex, int start, int length, string name) + { + var matrix = TestMatrices[name]; + var col = matrix.Column(colIndex, start, length); + + Assert.AreEqual(length, col.Count); + for (var j = start; j < start + length; j++) + { + Assert.AreEqual(matrix[j, colIndex], col[j - start]); + } + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void GetColumnWithRangeResultArgumentExeptionWhenLengthIsZero() + { + var matrix = TestMatrices["Singular3x3"]; + var col = CreateVector(matrix.RowCount); + matrix.Column(0, 0, 0, col); + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void GetColumnWithRangeFailsWithTooSmallResultVector() + { + var matrix = TestMatrices["Singular3x3"]; + var result = CreateVector(matrix.RowCount - 1); + matrix.Column(0, 0, matrix.RowCount, result); + } + + [Test] + [Row(0, "Singular3x3")] + [Row(1, "Singular3x3")] + [Row(2, "Singular3x3")] + [Row(2, "Square3x3")] + public void CanSetRow(int rowIndex, string name) + { + var matrix = TestMatrices[name].Clone(); + matrix.SetRow(rowIndex, CreateVector(matrix.ColumnCount)); + + for (var i = 0; i < matrix.RowCount; i++) + { + for (var j = 0; j < matrix.ColumnCount; j++) + { + if (i == rowIndex) + { + Assert.AreEqual(0.0, matrix[i, j]); + } + else + { + Assert.AreEqual(TestMatrices[name][i, j], matrix[i, j]); + } + } + } + } + + [Test] + [Row(0, "Singular3x3")] + [Row(1, "Singular3x3")] + [Row(2, "Singular3x3")] + [Row(2, "Square3x3")] + public void CanSetColumn(int colIndex, string name) + { + var matrix = TestMatrices[name].Clone(); + matrix.SetColumn(colIndex, CreateVector(matrix.ColumnCount)); + + for (var i = 0; i < matrix.RowCount; i++) + { + for (var j = 0; j < matrix.ColumnCount; j++) + { + if (j == colIndex) + { + Assert.AreEqual(0.0, matrix[i, j]); + } + else + { + Assert.AreEqual(TestMatrices[name][i, j], matrix[i, j]); + } + } + } + } + + [Test] + [Row("Singular3x3")] + [Row("Square3x3")] + [Row("Square4x4")] + [Row("Tall3x2")] + [Row("Wide2x3")] + [MultipleAsserts] + public void UpperTriangleResult(string name) + { + var data = TestMatrices[name]; + var result = CreateMatrix(data.RowCount, data.ColumnCount); + var lower = data.UpperTriangle(); + for (var i = 0; i < data.RowCount; i++) + { + for (var j = 0; j < data.ColumnCount; j++) + { + if (i <= j) + { + Assert.AreEqual(data[i, j], + lower[i, j]); + } + else + { + Assert.AreEqual(0, lower[i, j]); + } + } + } + } + + [Test] + [ExpectedArgumentNullException] + public void UpperTriangleWithResultNullShouldThrowException() + { + var data = TestMatrices["Square3x3"]; + Matrix result = null; + data.UpperTriangle(result); + } + + [Test] + [ExpectedArgumentException] + public void UpperTriangleWithUnEqualRowsShouldThrowException() + { + var data = TestMatrices["Square3x3"]; + var result = CreateMatrix(data.RowCount + 1, data.ColumnCount); + data.UpperTriangle(result); + } + + [Test] + [ExpectedArgumentException] + public void UpperTriangleWithUnEqualColumnsShouldThrowException() + { + var data = TestMatrices["Square3x3"]; + var result = CreateMatrix(data.RowCount, data.ColumnCount + 1); + data.UpperTriangle(result); + } + + [Test] + public void StrictlyLowerTriangle() + { + foreach (var data in TestMatrices.Values) + { + var lower = data.StrictlyLowerTriangle(); + for (var i = 0; i < data.RowCount; i++) + { + for (var j = 0; j < data.ColumnCount; j++) + { + if (i > j) + { + Assert.AreEqual(data[i, j], lower[i, j]); + } + else + { + Assert.AreEqual(0, lower[i, j]); + } + } + } + } + } + + [Test] + public void StrictlyLowerTriangleResult() + { + foreach (var data in TestMatrices.Values) + { + var lower = CreateMatrix(data.RowCount, data.ColumnCount); + data.StrictlyLowerTriangle(lower); + for (var i = 0; i < data.RowCount; i++) + { + for (var j = 0; j < data.ColumnCount; j++) + { + if (i > j) + { + Assert.AreEqual(data[i, j], lower[i, j]); + } + else + { + Assert.AreEqual(0, lower[i, j]); + } + } + } + } + } + + [Test] + [ExpectedArgumentNullException] + public void StrictlyLowerTriangleWithNullParameterShouldThrowException() + { + var data = TestMatrices["Square3x3"]; + Matrix lower = null; + data.StrictlyLowerTriangle(lower); + } + + [Test] + [ExpectedArgumentException] + public void StrictlyLowerTriangleWithInvalidColumnNumberShouldThrowException() + { + var data = TestMatrices["Square3x3"]; + var lower = CreateMatrix(data.RowCount, data.ColumnCount + 1); + data.StrictlyLowerTriangle(lower); + } + + [Test] + [ExpectedArgumentException] + public void StrictlyLowerTriangleWithInvalidRowNumberShouldThrowException() + { + var data = TestMatrices["Square3x3"]; + var lower = CreateMatrix(data.RowCount + 1, data.ColumnCount); + data.StrictlyLowerTriangle(lower); + } + + + [Test] + public void StrictlyUpperTriangle() + { + foreach (var data in TestMatrices.Values) + { + var lower = data.StrictlyUpperTriangle(); + for (var i = 0; i < data.RowCount; i++) + { + for (var j = 0; j < data.ColumnCount; j++) + { + if (i < j) + { + Assert.AreEqual(data[i, j], lower[i, j]); + } + else + { + Assert.AreEqual(0, lower[i, j]); + } + } + } + } + } + + [Test] + public void StrictlyUpperTriangleResult() + { + foreach (var data in TestMatrices.Values) + { + var lower = CreateMatrix(data.RowCount, data.ColumnCount); + data.StrictlyUpperTriangle(lower); + for (var i = 0; i < data.RowCount; i++) + { + for (var j = 0; j < data.ColumnCount; j++) + { + if (i < j) + { + Assert.AreEqual(data[i, j], lower[i, j]); + } + else + { + Assert.AreEqual(0, lower[i, j]); + } + } + } + } + } + + [Test] + [ExpectedArgumentNullException] + public void StrictlyUpperTriangleWithNullParameterShouldThrowException() + { + var data = TestMatrices["Square3x3"]; + Matrix lower = null; + data.StrictlyUpperTriangle(lower); + } + + [Test] + [ExpectedArgumentException] + public void StrictlyUpperTriangleWithInvalidColumnNumberShouldThrowException() + { + var data = TestMatrices["Square3x3"]; + var lower = CreateMatrix(data.RowCount, data.ColumnCount + 1); + data.StrictlyUpperTriangle(lower); + } + + [Test] + [ExpectedArgumentException] + public void StrictlyUpperTriangleWithInvalidRowNumberShouldThrowException() + { + var data = TestMatrices["Square3x3"]; + var lower = CreateMatrix(data.RowCount + 1, data.ColumnCount); + data.StrictlyUpperTriangle(lower); + } + + [Test] + [Row("Singular3x3")] + [Row("Square3x3")] + [Row("Square4x4")] + [Row("Tall3x2")] + [Row("Wide2x3")] + [MultipleAsserts] + public void CanTransposeMatrix(string name) + { + var matrix = CreateMatrix(TestData2D[name]); + var transpose = matrix.Transpose(); + + Assert.AreNotSame(matrix, transpose); + Assert.AreEqual(matrix.RowCount, transpose.ColumnCount); + Assert.AreEqual(matrix.ColumnCount, transpose.RowCount); + for (var i = 0; i < matrix.RowCount; i++) + { + for (var j = 0; j < matrix.ColumnCount; j++) + { + Assert.AreEqual(matrix[i, j], transpose[j, i]); + } + } + } + + [Test] + [Row("Singular3x3", new float[] { 1, 2, 3 })] + [Row("Square3x3", new float[] { 1, 2, 3 })] + [Row("Tall3x2", new float[] { 1, 2, 3 })] + [Row("Wide2x3", new float[] { 1, 2 })] + [Row("Singular3x3", null, ExpectedException = typeof(ArgumentNullException))] + [Row("Singular3x3", new float[] { 1, 2, 3, 4, 5 }, ExpectedException = typeof(ArgumentException))] + public virtual void SetColumnWithArray(string name, float[] column) + { + var matrix = TestMatrices[name]; + for (var i = 0; i < matrix.ColumnCount; i++) + { + matrix.SetColumn(i, column); + for (var j = 0; j < matrix.RowCount; j++) + { + Assert.AreEqual(matrix[j, i], column[j]); + } + } + } + + [Test] + [ExpectedArgumentOutOfRangeException] + public void SetColumnArrayWithInvalidColumnIndexShouldThrowException() + { + var matrix = TestMatrices["Square3x3"]; + float[] column = { 1, 2, 3 }; + matrix.SetColumn(-1, column); + } + + [Test] + [ExpectedArgumentOutOfRangeException] + public void SetColumnArrayWithInvalidColumnIndexShouldThrowException2() + { + var matrix = TestMatrices["Square3x3"]; + float[] column = { 1, 2, 3 }; + matrix.SetColumn(matrix.ColumnCount + 1, column); + } + + [Test] + [Row("Singular3x3", new float[] { 1, 2, 3 })] + [Row("Square3x3", new float[] { 1, 2, 3 })] + [Row("Tall3x2", new float[] { 1, 2, 3 })] + [Row("Wide2x3", new float[] { 1, 2 })] + [Row("Singular3x3", new float[] { 1, 2, 3, 4, 5 }, ExpectedException = typeof(ArgumentException))] + public virtual void SetColumnWithVector(string name, float[] column) + { + var matrix = TestMatrices[name]; + var columnVector = CreateVector(column); + for (var i = 0; i < matrix.ColumnCount; i++) + { + matrix.SetColumn(i, column); + for (var j = 0; j < matrix.RowCount; j++) + { + Assert.AreEqual(matrix[j, i], columnVector[j]); + } + } + } + + [Test] + [ExpectedArgumentNullException] + public void SetColumnWithNullVectorShouldThrowException() + { + var matrix = TestMatrices["Square3x3"]; + Vector columnVector = null; + matrix.SetColumn(1, columnVector); + } + + [Test] + [ExpectedArgumentOutOfRangeException] + public void SetColumnVectorWithInvalidColumnIndexShouldThrowException() + { + var matrix = TestMatrices["Square3x3"]; + var column = CreateVector(new float[] { 1, 2, 3 }); + matrix.SetColumn(-1, column); + } + + [Test] + [ExpectedArgumentOutOfRangeException] + public void SetColumnVectorWithInvalidColumnIndexShouldThrowException2() + { + var matrix = TestMatrices["Square3x3"]; + var column = CreateVector(new float[] { 1, 2, 3 }); + matrix.SetColumn(matrix.ColumnCount + 1, column); + } + + [Test] + public void InsertColumn() + { + var matrix = CreateMatrix(3, 3); + var column = CreateVector(matrix.RowCount); + for (var i = 0; i < column.Count; i++) + { + column[i] = i; + } + + for (var k = 0; k < matrix.ColumnCount + 1; k++) + { + var result = matrix.InsertColumn(k, column); + Assert.AreEqual(result.ColumnCount, matrix.ColumnCount + 1); + for (var col = 0; col < result.ColumnCount; col++) + { + for (var row = 0; row < result.RowCount; row++) + { + if (col == k) + { + Assert.AreEqual(row, result[row, col]); + } + else + { + Assert.AreEqual(0, result[row, col]); + } + } + } + } + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void InsertNullColumnShouldThrowExecption() + { + var matrix = TestMatrices["Square3x3"]; + matrix.InsertColumn(0, null); + } + + [Test] + [Row(-1, ExpectedException = typeof(ArgumentOutOfRangeException))] + [Row(5, ExpectedException = typeof(ArgumentOutOfRangeException))] + public void InsertColumnWithInvalidColumnIndexShouldThrowExceptiopn(int columnIndex) + { + var matrix = CreateMatrix(3, 3); + var column = CreateVector(matrix.RowCount); + matrix.InsertColumn(columnIndex, column); + } + + public void InsertColumnWithInvalidNumberOfElementsShouldThrowException() + { + var matrix = CreateMatrix(3, 3); + var column = CreateVector(matrix.RowCount + 1); + matrix.InsertColumn(0, column); + } + + [Test] + [Row("Singular3x3", new float[] { 1, 2, 3 })] + [Row("Square3x3", new float[] { 1, 2, 3 })] + [Row("Tall3x2", new float[] { 1, 2 })] + [Row("Wide2x3", new float[] { 1, 2, 3 })] + [Row("Singular3x3", null, ExpectedException = typeof(ArgumentNullException))] + [Row("Singular3x3", new float[] { 1, 2, 3, 4, 5 }, ExpectedException = typeof(ArgumentException))] + public virtual void SetRowWithArray(string name, float[] row) + { + var matrix = TestMatrices[name]; + for (var i = 0; i < matrix.RowCount; i++) + { + matrix.SetRow(i, row); + for (var j = 0; j < matrix.ColumnCount; j++) + { + Assert.AreEqual(matrix[i, j], row[j]); + } + } + } + + [Test] + [ExpectedArgumentOutOfRangeException] + public void SetRowArrayWithInvalidRowIndexShouldThrowException() + { + var matrix = TestMatrices["Square3x3"]; + float[] row = { 1, 2, 3 }; + matrix.SetRow(-1, row); + } + + [Test] + [ExpectedArgumentOutOfRangeException] + public void SetRowArrayWithInvalidRowIndexShouldThrowException2() + { + var matrix = TestMatrices["Square3x3"]; + float[] row = { 1, 2, 3 }; + matrix.SetRow(matrix.RowCount + 1, row); + } + + [Test] + [Row("Singular3x3", new float[] { 1, 2, 3 })] + [Row("Square3x3", new float[] { 1, 2, 3 })] + [Row("Tall3x2", new float[] { 1, 2 })] + [Row("Wide2x3", new float[] { 1, 2, 3 })] + [Row("Singular3x3", new float[] { 1, 2, 3, 4, 5 }, ExpectedException = typeof(ArgumentException))] + public virtual void SetRowWithVector(string name, float[] row) + { + var matrix = TestMatrices[name]; + var rowVector = CreateVector(row); + for (var i = 0; i < matrix.RowCount; i++) + { + matrix.SetRow(i, row); + for (var j = 0; j < matrix.ColumnCount; j++) + { + Assert.AreEqual(matrix[i, j], rowVector[j]); + } + } + } + + [Test] + [ExpectedArgumentNullException] + public void SetRowWithNullVectorShouldThrowException() + { + var matrix = TestMatrices["Square3x3"]; + Vector rowVector = null; + matrix.SetRow(1, rowVector); + } + + [Test] + [ExpectedArgumentOutOfRangeException] + public void SetRowVectorWithInvalidRowIndexShouldThrowException() + { + var matrix = TestMatrices["Square3x3"]; + var row = CreateVector(new float[] { 1, 2, 3 }); + matrix.SetRow(-1, row); + } + + [Test] + [ExpectedArgumentOutOfRangeException] + public void SetRowVectorWithInvalidRowIndexShouldThrowException2() + { + var matrix = TestMatrices["Square3x3"]; + var row = CreateVector(new float[] { 1, 2, 3 }); + matrix.SetRow(matrix.RowCount + 1, row); + } + + [Test] + [Row(0, 2, 0, 2)] + [Row(1, 1, 1, 1)] + [Row(0, 4, 0, 2, ExpectedException = typeof(ArgumentOutOfRangeException))] + [Row(0, 2, 0, 4, ExpectedException = typeof(ArgumentOutOfRangeException))] + [Row(4, 2, 0, 2, ExpectedException = typeof(ArgumentOutOfRangeException))] + [Row(0, 2, 4, 2, ExpectedException = typeof(ArgumentOutOfRangeException))] + [Row(-1, 2, 0, 2, ExpectedException = typeof(ArgumentOutOfRangeException))] + [Row(0, 2, -1, 2, ExpectedException = typeof(ArgumentOutOfRangeException))] + [Row(0, -1, 0, 2, ExpectedException = typeof(ArgumentException))] + [Row(0, 2, 0, -1, ExpectedException = typeof(ArgumentException))] + public virtual void SetSubMatrix(int rowStart, int rowLength, int colStart, int colLength) + { + foreach (var matrix in TestMatrices.Values) + { + var subMatrix = matrix.SubMatrix(0, 2, 0, 2); + subMatrix[0, 0] = 10.0f; + subMatrix[0, 1] = -1.0f; + subMatrix[1, 0] = 3.0f; + subMatrix[1, 1] = 4.0f; + matrix.SetSubMatrix(rowStart, rowLength, colStart, colLength, subMatrix); + + for (int i = rowStart, ii = 0; i < rowLength; i++, ii++) + { + for (int j = colStart, jj = 0; j < colLength; j++, jj++) + { + Assert.AreEqual(matrix[i, j], subMatrix[ii, jj]); + } + } + } + } + + [Test] + [ExpectedArgumentNullException] + public void SetSubMatrixWithNullSubMatrixShouldThrowException() + { + var data = TestMatrices["Square3x3"]; + Matrix subMatrix = null; + data.SetSubMatrix(0, 2, 0, 2, subMatrix); + } + + [Test] + [Row("Square3x3", new float[] { 1, 2, 3 })] + [Row("Wide2x3", new float[] { 1, 2 })] + [Row("Wide2x3", new float[] { 1, 2, 3 }, ExpectedException = typeof(ArgumentException))] + [Row("Tall3x2", new float[] { 1, 2 })] + public void SetDiagonalVector(string name, float[] diagonal) + { + var matrix = TestMatrices[name]; + var vector = CreateVector(diagonal); + matrix.SetDiagonal(vector); + + var min = Math.Min(matrix.ColumnCount, matrix.RowCount); + Assert.AreEqual(diagonal.Length, min); + + for (var i = 0; i < vector.Count; i++) + { + Assert.AreEqual(vector[i], matrix[i, i]); + } + } + + [Test] + [ExpectedArgumentNullException] + public void SetDiagonalWithNullVectorParameterShouldThrowException() + { + var matrix = TestMatrices["Square3x3"]; + Vector vector = null; + matrix.SetDiagonal(vector); + } + + [Test] + [Row("Square3x3", new float[] { 1, 2, 3 })] + [Row("Wide2x3", new float[] { 1, 2 })] + [Row("Wide2x3", new float[] { 1, 2, 3 }, ExpectedException = typeof(ArgumentException))] + [Row("Tall3x2", new float[] { 1, 2 })] + [Row("Square3x3", null, ExpectedException = typeof(ArgumentNullException))] + public void SetDiagonalArray(string name, float[] diagonal) + { + var matrix = TestMatrices[name]; + matrix.SetDiagonal(diagonal); + var min = Math.Min(matrix.ColumnCount, matrix.RowCount); + Assert.AreEqual(diagonal.Length, min); + for (var i = 0; i < diagonal.Length; i++) + { + Assert.AreEqual(diagonal[i], matrix[i, i]); + } + } + + [Test] + public void InsertRow() + { + var matrix = CreateMatrix(3, 3); + var row = CreateVector(matrix.ColumnCount); + for (var i = 0; i < row.Count; i++) + { + row[i] = i; + } + + for (var insertedRowIndex = 0; insertedRowIndex < matrix.RowCount + 1; insertedRowIndex++) + { + var result = matrix.InsertRow(insertedRowIndex, row); + Assert.AreEqual(result.RowCount, matrix.ColumnCount + 1); + for (var i = 0; i < result.RowCount; i++) + { + for (var j = 0; j < result.ColumnCount; j++) + { + if (i == insertedRowIndex) + { + Assert.AreEqual(row[j], result[i, j]); + } + else + { + Assert.AreEqual(0, result[i, j]); + } + } + } + } + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void InsertNullRowShouldThrowExecption() + { + var matrix = TestMatrices["Square3x3"]; + matrix.InsertRow(0, null); + } + + [Test] + [Row(-1, ExpectedException = typeof(ArgumentOutOfRangeException))] + [Row(5, ExpectedException = typeof(ArgumentOutOfRangeException))] + public void InsertRowWithInvalidRowIndexShouldThrowExceptiopn(int rowIndex) + { + var matrix = CreateMatrix(3, 3); + var row = CreateVector(matrix.ColumnCount); + matrix.InsertRow(rowIndex, row); + } + + public void InsertRowWithInvalidNumberOfElementsShouldThrowException() + { + var matrix = CreateMatrix(3, 3); + var row = CreateVector(matrix.ColumnCount + 1); + matrix.InsertRow(0, row); + } + + [Test] + public void ToArray() + { + foreach (var data in TestMatrices.Values) + { + var array = data.ToArray(); + Assert.AreEqual(data.RowCount, array.GetLength(0)); + Assert.AreEqual(data.ColumnCount, array.GetLength(1)); + + for (var i = 0; i < data.RowCount; i++) + { + for (var j = 0; j < data.ColumnCount; j++) + { + Assert.AreEqual(data[i, j], array[i, j]); + } + } + } + } + + [Test] + public void ToColumnWiseArray() + { + foreach (var data in TestMatrices.Values) + { + var array = data.ToColumnWiseArray(); + Assert.AreEqual(data.RowCount * data.ColumnCount, array.Length); + + for (var i = 0; i < data.RowCount; i++) + { + for (var j = 0; j < data.ColumnCount; j++) + { + Assert.AreEqual(data[i, j], array[j * data.RowCount + i]); + } + } + } + } + + [Test] + public void ToRowWiseArray() + { + foreach (var data in TestMatrices.Values) + { + var array = data.ToRowWiseArray(); + Assert.AreEqual(data.RowCount * data.ColumnCount, array.Length); + + for (var i = 0; i < data.RowCount; i++) + { + for (var j = 0; j < data.ColumnCount; j++) + { + Assert.AreEqual(data[i, j], array[i * data.ColumnCount + j]); + } + } + } + } + + + [Test] + [Row("Singular3x3")] + [Row("Square3x3")] + [Row("Tall3x2")] + [MultipleAsserts] + public virtual void CanPermuteMatrixRows(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")] + [Row("Square3x3")] + [Row("Wide2x3")] + [MultipleAsserts] + public virtual void CanPermuteMatrixColumns(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]]); + } + } + } + + [Test] + public void CanAppendMatrices() + { + var left = CreateMatrix(TestData2D["Singular3x3"]); + var right = CreateMatrix(TestData2D["Tall3x2"]); + var result = left.Append(right); + Assert.AreEqual(left.ColumnCount + right.ColumnCount, result.ColumnCount); + Assert.AreEqual(left.RowCount, right.RowCount); + + for (var i = 0; i < result.RowCount; i++) + { + for (var j = 0; j < result.ColumnCount; j++) + { + if (j < left.ColumnCount) + { + Assert.AreEqual(left[i, j], result[i, j]); + } + else + { + Assert.AreEqual(right[i, j - left.ColumnCount], result[i, j]); + } + } + } + } + + [Test] + [ExpectedArgumentNullException] + public void CanAppendWithRightParameterNullShouldThrowException() + { + var left = TestMatrices["Square3x3"]; + Matrix right = null; + left.Append(right); + } + + [Test] + [ExpectedArgumentNullException] + public void CanAppendWithResultParameterNullShouldThrowException() + { + var left = TestMatrices["Square3x3"]; + var right = TestMatrices["Tall3x2"]; + Matrix result = null; + left.Append(right, result); + } + + [Test] + [ExpectedArgumentException] + public void AppendingTwoMatricesWithDifferentRowCountShouldThrowException() + { + var left = TestMatrices["Square3x3"]; + var right = TestMatrices["Wide2x3"]; + var result = left.Append(right); + } + + [Test] + [ExpectedArgumentException] + public void AppendingWithInvalidResultMatrixColumnsShouldThrowException() + { + var left = TestMatrices["Square3x3"]; + var right = TestMatrices["Tall3x2"]; + var result = CreateMatrix(3, 2); + left.Append(right, result); + } + + [Test] + public void CanStackMatrices() + { + var top = TestMatrices["Square3x3"]; + var bottom = TestMatrices["Wide2x3"]; + var result = top.Stack(bottom); + Assert.AreEqual(top.RowCount + bottom.RowCount, result.RowCount); + Assert.AreEqual(top.ColumnCount, result.ColumnCount); + + for (var i = 0; i < result.RowCount; i++) + { + for (var j = 0; j < result.ColumnCount; j++) + { + if (i < top.RowCount) + { + Assert.AreEqual(result[i, j], top[i, j]); + } + else + { + Assert.AreEqual(result[i, j], bottom[i - top.RowCount, j]); + } + } + } + } + + [Test] + [ExpectedArgumentNullException] + public void StackingWithBottomParameterNullShouldThrowException() + { + var top = TestMatrices["Square3x3"]; + Matrix bottom = null; + var result = CreateMatrix(top.RowCount + top.RowCount, top.ColumnCount); + top.Stack(bottom, result); + } + + [Test] + [ExpectedArgumentNullException] + public void StackingWithResultParameterNullShouldThrowException() + { + var top = TestMatrices["Square3x3"]; + var bottom = TestMatrices["Square3x3"]; + Matrix result = null; + top.Stack(bottom, result); + } + + [Test] + [ExpectedArgumentException] + public void StackingTwoMatricesWithDifferentColumnsShouldThrowException() + { + var top = TestMatrices["Square3x3"]; + var lower = TestMatrices["Tall3x2"]; + var result = CreateMatrix(top.RowCount + lower.RowCount, top.ColumnCount); + top.Stack(lower, result); + } + + [Test] + [ExpectedArgumentException] + public void StackingWithInvalidResultMatrixRowsShouldThrowException() + { + var top = TestMatrices["Square3x3"]; + var bottom = TestMatrices["Wide2x3"]; + var result = CreateMatrix(1, 3); + top.Stack(bottom, result); + } + + [Test] + public void CanDiagonallyStackMatrics() + { + var top = TestMatrices["Tall3x2"]; + var bottom = TestMatrices["Wide2x3"]; + var result = top.DiagonalStack(bottom); + 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]); + } + } + } + } + + [Test] + [ExpectedArgumentNullException] + public void DiagonalStackWithLowerNullShouldThrowException() + { + var top = TestMatrices["Square3x3"]; + Matrix lower = null; + top.DiagonalStack(lower); + } + + [Test] + public virtual void CanDiagonallyStackMatricesWithPassingResult() + { + var top = TestMatrices["Tall3x2"]; + var bottom = TestMatrices["Wide2x3"]; + var result = CreateMatrix(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]); + } + } + } + } + + [Test] + [ExpectedArgumentNullException] + public void DiagonalStackWithResultNullShouldThrowException() + { + var top = TestMatrices["Square3x3"]; + var lower = TestMatrices["Wide2x3"]; + Matrix result = null; + top.DiagonalStack(lower, result); + } + + [Test] + [ExpectedArgumentException] + public void DiagonalStackWithInvalidResultMatrixShouldThrowException() + { + var top = TestMatrices["Square3x3"]; + var lower = TestMatrices["Wide2x3"]; + var result = CreateMatrix(top.RowCount + lower.RowCount + 2, top.ColumnCount + lower.ColumnCount); + top.DiagonalStack(lower, result); + } + + [Test] + public virtual void FrobeniusNorm() + { + var matrix = TestMatrices["Square3x3"]; + AssertHelpers.AlmostEqual(10.7777548f, (float)matrix.FrobeniusNorm(), 7); + + matrix = TestMatrices["Wide2x3"]; + AssertHelpers.AlmostEqual(4.7947888f, (float)matrix.FrobeniusNorm(), 7); + + matrix = TestMatrices["Tall3x2"]; + AssertHelpers.AlmostEqual(7.5412200f, (float)matrix.FrobeniusNorm(), 7); + } + + [Test] + public virtual void InfinityNorm() + { + var matrix = TestMatrices["Square3x3"]; + Assert.AreEqual(16.5f, (float)matrix.InfinityNorm()); + + matrix = TestMatrices["Wide2x3"]; + Assert.AreEqual(6.6f, (float)matrix.InfinityNorm()); + + matrix = TestMatrices["Tall3x2"]; + Assert.AreEqual(9.9f, (float)matrix.InfinityNorm()); + } + + [Test] + public virtual void L1Norm() + { + var matrix = TestMatrices["Square3x3"]; + Assert.AreEqual(12.1f, (float)matrix.L1Norm()); + + matrix = TestMatrices["Wide2x3"]; + Assert.AreEqual(5.5f, (float)matrix.L1Norm()); + + matrix = TestMatrices["Tall3x2"]; + Assert.AreEqual(8.8f, (float)matrix.L1Norm()); + } + + [Test] + public virtual void L2Norm() + { + var matrix = TestMatrices["Square3x3"]; + AssertHelpers.AlmostEqual(10.3913473f, (float)matrix.L2Norm(), 7); + + matrix = TestMatrices["Wide2x3"]; + AssertHelpers.AlmostEqual(4.7540849f, (float)matrix.L2Norm(), 7); + + matrix = TestMatrices["Tall3x2"]; + AssertHelpers.AlmostEqual(7.1827270f, (float)matrix.L2Norm(), 7); + } + } +} \ No newline at end of file diff --git a/src/UnitTests/LinearAlgebraTests/Single/Solvers/Iterative/BiCgStabTest.cs b/src/UnitTests/LinearAlgebraTests/Single/Solvers/Iterative/BiCgStabTest.cs new file mode 100644 index 00000000..9649eb4e --- /dev/null +++ b/src/UnitTests/LinearAlgebraTests/Single/Solvers/Iterative/BiCgStabTest.cs @@ -0,0 +1,289 @@ +namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single.Solvers.Iterative +{ + using System; + using LinearAlgebra.Single; + using LinearAlgebra.Single.Solvers; + using LinearAlgebra.Single.Solvers.Iterative; + using LinearAlgebra.Single.Solvers.StopCriterium; + using LinearAlgebra.Generic; + using LinearAlgebra.Generic.Solvers.Status; + using LinearAlgebra.Generic.Solvers.StopCriterium; + using MbUnit.Framework; + + [TestFixture] + public class BiCgStabTest + { + private const float ConvergenceBoundary = 1e-5f; + private const int MaximumIterations = 1000; + + [Test] + [ExpectedArgumentException] + public void SolveWideMatrix() + { + var matrix = new SparseMatrix(2, 3); + Vector input = new DenseVector(2); + + var solver = new BiCgStab(); + solver.Solve(matrix, input); + } + + [Test] + [ExpectedArgumentException] + public void SolveLongMatrix() + { + var matrix = new SparseMatrix(3, 2); + Vector input = new DenseVector(3); + + var solver = new BiCgStab(); + solver.Solve(matrix, input); + } + + [Test] + [MultipleAsserts] + public void SolveUnitMatrixAndBackMultiply() + { + // Create the identity matrix + Matrix matrix = SparseMatrix.Identity(100); + + // Create the y vector + Vector y = new DenseVector(matrix.RowCount, 1); + + // Create an iteration monitor which will keep track of iterative convergence + var monitor = new Iterator(new IIterationStopCriterium[] + { + new IterationCountStopCriterium(MaximumIterations), + new ResidualStopCriterium(ConvergenceBoundary), + new DivergenceStopCriterium(), + new FailureStopCriterium() + }); + var solver = new BiCgStab(monitor); + + // Solve equation Ax = y + var x = solver.Solve(matrix, y); + + // Now compare the results + Assert.IsNotNull(x, "#02"); + Assert.AreEqual(y.Count, x.Count, "#03"); + + // Back multiply the vector + var z = matrix.Multiply(x); + + // Check that the solution converged + Assert.IsTrue(monitor.Status is CalculationConverged, "#04"); + + // Now compare the vectors + for (var i = 0; i < y.Count; i++) + { + Assert.IsTrue((y[i] - z[i]).IsSmaller(ConvergenceBoundary, 1), "#05-" + i); + } + } + + [Test] + [MultipleAsserts] + public void SolveScaledUnitMatrixAndBackMultiply() + { + // Create the identity matrix + Matrix matrix = SparseMatrix.Identity(100); + + // Scale it with a funny number + matrix.Multiply((float)Math.PI); + + // Create the y vector + Vector y = new DenseVector(matrix.RowCount, 1); + + // Create an iteration monitor which will keep track of iterative convergence + var monitor = new Iterator(new IIterationStopCriterium[] + { + new IterationCountStopCriterium(MaximumIterations), + new ResidualStopCriterium(ConvergenceBoundary), + new DivergenceStopCriterium(), + new FailureStopCriterium() + }); + var solver = new BiCgStab(monitor); + + // Solve equation Ax = y + var x = solver.Solve(matrix, y); + + // Now compare the results + Assert.IsNotNull(x, "#02"); + Assert.AreEqual(y.Count, x.Count, "#03"); + + // Back multiply the vector + var z = matrix.Multiply(x); + + // Check that the solution converged + Assert.IsTrue(monitor.Status is CalculationConverged, "#04"); + + // Now compare the vectors + for (var i = 0; i < y.Count; i++) + { + Assert.IsTrue((y[i] - z[i]).IsSmaller(ConvergenceBoundary, 1), "#05-" + i); + } + } + + [Test] + [MultipleAsserts] + public void SolvePoissonMatrixAndBackMultiply() + { + // Create the matrix + var matrix = new SparseMatrix(25); + + // Assemble the matrix. We assume we're solving the Poisson equation + // on a rectangular 5 x 5 grid + const int GridSize = 5; + + // The pattern is: + // 0 .... 0 -1 0 0 0 0 0 0 0 0 -1 4 -1 0 0 0 0 0 0 0 0 -1 0 0 ... 0 + for (var i = 0; i < matrix.RowCount; i++) + { + // Insert the first set of -1's + if (i > (GridSize - 1)) + { + matrix[i, i - GridSize] = -1; + } + + // Insert the second set of -1's + if (i > 0) + { + matrix[i, i - 1] = -1; + } + + // Insert the centerline values + matrix[i, i] = 4; + + // Insert the first trailing set of -1's + if (i < matrix.RowCount - 1) + { + matrix[i, i + 1] = -1; + } + + // Insert the second trailing set of -1's + if (i < matrix.RowCount - GridSize) + { + matrix[i, i + GridSize] = -1; + } + } + + // Create the y vector + Vector y = new DenseVector(matrix.RowCount, 1); + + // Create an iteration monitor which will keep track of iterative convergence + var monitor = new Iterator(new IIterationStopCriterium[] + { + new IterationCountStopCriterium(MaximumIterations), + new ResidualStopCriterium(ConvergenceBoundary), + new DivergenceStopCriterium(), + new FailureStopCriterium() + }); + var solver = new BiCgStab(monitor); + + // Solve equation Ax = y + var x = solver.Solve(matrix, y); + + // Now compare the results + Assert.IsNotNull(x, "#02"); + Assert.AreEqual(y.Count, x.Count, "#03"); + + // Back multiply the vector + var z = matrix.Multiply(x); + + // Check that the solution converged + Assert.IsTrue(monitor.Status is CalculationConverged, "#04"); + + // Now compare the vectors + for (var i = 0; i < y.Count; i++) + { + Assert.IsTrue(Math.Abs(y[i] - z[i]).IsSmaller(ConvergenceBoundary, 1), "#05-" + i); + } + } + + [Test] + [Row(5)] + [MultipleAsserts] + public void CanSolveForRandomVector(int order) + { + // Due to datatype "float" it can happen that solution will not converge for specific random matrix + // That's why we will do 3 tries and downgrade stop criterium each time + for (var iteration = 6; iteration > 3; iteration--) + { + var matrixA = MatrixLoader.GenerateRandomDenseMatrix(order, order); + var vectorb = MatrixLoader.GenerateRandomDenseVector(order); + + var monitor = new Iterator(new IIterationStopCriterium[] + { + new IterationCountStopCriterium(MaximumIterations), + new ResidualStopCriterium((float)Math.Pow(1.0/10.0, iteration)), + }); + var solver = new BiCgStab(monitor); + var resultx = solver.Solve(matrixA, vectorb); + + if (!(monitor.Status is CalculationConverged)) + { + // Solution was not found, try again downgrading convergence boundary + continue; + } + + 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], (float)Math.Pow(1.0 / 10.0, iteration - 3)); + } + + return; + } + + Assert.Fail("Solution was not found in 3 tries"); + } + + [Test] + [Row(5)] + [MultipleAsserts] + public void CanSolveForRandomMatrix(int order) + { + // Due to datatype "float" it can happen that solution will not converge for specific random matrix + // That's why we will do 3 tries and downgrade stop criterium each time + for (var iteration = 6; iteration > 3; iteration--) + { + var matrixA = MatrixLoader.GenerateRandomDenseMatrix(order, order); + var matrixB = MatrixLoader.GenerateRandomDenseMatrix(order, order); + + var monitor = new Iterator(new IIterationStopCriterium[] + { + new IterationCountStopCriterium(MaximumIterations), + new ResidualStopCriterium((float)Math.Pow(1.0/10.0, iteration)) + }); + var solver = new BiCgStab(monitor); + var matrixX = solver.Solve(matrixA, matrixB); + + if (!(monitor.Status is CalculationConverged)) + { + // Solution was not found, try again downgrading convergence boundary + continue; + } + + // 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], (float)Math.Pow(1.0 / 10.0, iteration - 3)); + } + } + + return; + } + + Assert.Fail("Solution was not found in 3 tries"); + } + } +} diff --git a/src/UnitTests/LinearAlgebraTests/Single/Solvers/Iterative/GpBiCgTest.cs b/src/UnitTests/LinearAlgebraTests/Single/Solvers/Iterative/GpBiCgTest.cs new file mode 100644 index 00000000..e49c80d0 --- /dev/null +++ b/src/UnitTests/LinearAlgebraTests/Single/Solvers/Iterative/GpBiCgTest.cs @@ -0,0 +1,291 @@ +namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single.Solvers.Iterative +{ + using System; + using LinearAlgebra.Single; + using LinearAlgebra.Single.Solvers; + using LinearAlgebra.Single.Solvers.Iterative; + using LinearAlgebra.Single.Solvers.StopCriterium; + using LinearAlgebra.Generic; + using LinearAlgebra.Generic.Solvers.Status; + using LinearAlgebra.Generic.Solvers.StopCriterium; + using MbUnit.Framework; + + [TestFixture] + public class GpBiCgTest + { + private const float ConvergenceBoundary = 1e-5f; + private const int MaximumIterations = 1000; + + [Test] + [ExpectedArgumentException] + public void SolveWideMatrix() + { + var matrix = new SparseMatrix(2, 3); + Vector input = new DenseVector(2); + + var solver = new GpBiCg(); + solver.Solve(matrix, input); + } + + [Test] + [ExpectedArgumentException] + public void SolveLongMatrix() + { + var matrix = new SparseMatrix(3, 2); + Vector input = new DenseVector(3); + + var solver = new GpBiCg(); + solver.Solve(matrix, input); + } + + [Test] + [MultipleAsserts] + public void SolveUnitMatrixAndBackMultiply() + { + // Create the identity matrix + Matrix matrix = SparseMatrix.Identity(100); + + // Create the y vector + Vector y = new DenseVector(matrix.RowCount, 1); + + // Create an iteration monitor which will keep track of iterative convergence + var monitor = new Iterator(new IIterationStopCriterium[] + { + new IterationCountStopCriterium(MaximumIterations), + new ResidualStopCriterium(ConvergenceBoundary), + new DivergenceStopCriterium(), + new FailureStopCriterium() + }); + var solver = new GpBiCg(monitor); + + // Solve equation Ax = y + var x = solver.Solve(matrix, y); + + // Now compare the results + Assert.IsNotNull(x, "#02"); + Assert.AreEqual(y.Count, x.Count, "#03"); + + // Back multiply the vector + var z = matrix.Multiply(x); + + // Check that the solution converged + Assert.IsTrue(monitor.Status is CalculationConverged, "#04"); + + // Now compare the vectors + for (var i = 0; i < y.Count; i++) + { + Assert.IsTrue((y[i] - z[i]).IsSmaller(ConvergenceBoundary, 1), "#05-" + i); + } + } + + [Test] + [MultipleAsserts] + public void SolveScaledUnitMatrixAndBackMultiply() + { + // Create the identity matrix + Matrix matrix = SparseMatrix.Identity(100); + + // Scale it with a funny number + matrix.Multiply((float)Math.PI); + + // Create the y vector + Vector y = new DenseVector(matrix.RowCount, 1); + + // Create an iteration monitor which will keep track of iterative convergence + var monitor = new Iterator(new IIterationStopCriterium[] + { + new IterationCountStopCriterium(MaximumIterations), + new ResidualStopCriterium(ConvergenceBoundary), + new DivergenceStopCriterium(), + new FailureStopCriterium() + }); + + var solver = new GpBiCg(monitor); + + // Solve equation Ax = y + var x = solver.Solve(matrix, y); + + // Now compare the results + Assert.IsNotNull(x, "#02"); + Assert.AreEqual(y.Count, x.Count, "#03"); + + // Back multiply the vector + var z = matrix.Multiply(x); + + // Check that the solution converged + Assert.IsTrue(monitor.Status is CalculationConverged, "#04"); + + // Now compare the vectors + for (var i = 0; i < y.Count; i++) + { + Assert.IsTrue((y[i] - z[i]).IsSmaller(ConvergenceBoundary, 1), "#05-" + i); + } + } + + [Test] + [MultipleAsserts] + public void SolvePoissonMatrixAndBackMultiply() + { + // Create the matrix + var matrix = new SparseMatrix(25); + + // Assemble the matrix. We assume we're solving the Poisson equation + // on a rectangular 5 x 5 grid + const int GridSize = 5; + + // The pattern is: + // 0 .... 0 -1 0 0 0 0 0 0 0 0 -1 4 -1 0 0 0 0 0 0 0 0 -1 0 0 ... 0 + for (var i = 0; i < matrix.RowCount; i++) + { + // Insert the first set of -1's + if (i > (GridSize - 1)) + { + matrix[i, i - GridSize] = -1; + } + + // Insert the second set of -1's + if (i > 0) + { + matrix[i, i - 1] = -1; + } + + // Insert the centerline values + matrix[i, i] = 4; + + // Insert the first trailing set of -1's + if (i < matrix.RowCount - 1) + { + matrix[i, i + 1] = -1; + } + + // Insert the second trailing set of -1's + if (i < matrix.RowCount - GridSize) + { + matrix[i, i + GridSize] = -1; + } + } + + // Create the y vector + Vector y = new DenseVector(matrix.RowCount, 1); + + // Create an iteration monitor which will keep track of iterative convergence + var monitor = new Iterator(new IIterationStopCriterium[] + { + new IterationCountStopCriterium(MaximumIterations), + new ResidualStopCriterium(ConvergenceBoundary), + new DivergenceStopCriterium(), + new FailureStopCriterium() + }); + + var solver = new GpBiCg(monitor); + + // Solve equation Ax = y + var x = solver.Solve(matrix, y); + + // Now compare the results + Assert.IsNotNull(x, "#02"); + Assert.AreEqual(y.Count, x.Count, "#03"); + + // Back multiply the vector + var z = matrix.Multiply(x); + + // Check that the solution converged + Assert.IsTrue(monitor.Status is CalculationConverged, "#04"); + + // Now compare the vectors + for (var i = 0; i < y.Count; i++) + { + Assert.IsTrue(Math.Abs(y[i] - z[i]).IsSmaller(ConvergenceBoundary, 1), "#05-" + i); + } + } + + [Test] + [Row(5)] + [MultipleAsserts] + public void CanSolveForRandomVector(int order) + { + // Due to datatype "float" it can happen that solution will not converge for specific random matrix + // That's why we will do 3 tries and downgrade stop criterium each time + for (var iteration = 6; iteration > 3; iteration--) + { + var matrixA = MatrixLoader.GenerateRandomDenseMatrix(order, order); + var vectorb = MatrixLoader.GenerateRandomDenseVector(order); + + var monitor = new Iterator(new IIterationStopCriterium[] + { + new IterationCountStopCriterium(MaximumIterations), + new ResidualStopCriterium((float)Math.Pow(1.0/10.0, iteration)), + }); + var solver = new GpBiCg(monitor); + var resultx = solver.Solve(matrixA, vectorb); + + if (!(monitor.Status is CalculationConverged)) + { + // Solution was not found, try again downgrading convergence boundary + continue; + } + + 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], (float)Math.Pow(1.0 / 10.0, iteration - 3)); + } + + return; + } + + Assert.Fail("Solution was not found in 3 tries"); + } + + [Test] + [Row(5)] + [MultipleAsserts] + public void CanSolveForRandomMatrix(int order) + { + // Due to datatype "float" it can happen that solution will not converge for specific random matrix + // That's why we will do 3 tries and downgrade stop criterium each time + for (var iteration = 6; iteration > 3; iteration--) + { + var matrixA = MatrixLoader.GenerateRandomDenseMatrix(order, order); + var matrixB = MatrixLoader.GenerateRandomDenseMatrix(order, order); + + var monitor = new Iterator(new IIterationStopCriterium[] + { + new IterationCountStopCriterium(MaximumIterations), + new ResidualStopCriterium((float)Math.Pow(1.0/10.0, iteration)) + }); + var solver = new GpBiCg(monitor); + var matrixX = solver.Solve(matrixA, matrixB); + + if (!(monitor.Status is CalculationConverged)) + { + // Solution was not found, try again downgrading convergence boundary + continue; + } + + // 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], (float)Math.Pow(1.0 / 10.0, iteration - 3)); + } + } + + return; + } + + Assert.Fail("Solution was not found in 3 tries"); + } + } +} \ No newline at end of file diff --git a/src/UnitTests/LinearAlgebraTests/Single/Solvers/Iterative/MlkBiCgStabTest.cs b/src/UnitTests/LinearAlgebraTests/Single/Solvers/Iterative/MlkBiCgStabTest.cs new file mode 100644 index 00000000..829ea5de --- /dev/null +++ b/src/UnitTests/LinearAlgebraTests/Single/Solvers/Iterative/MlkBiCgStabTest.cs @@ -0,0 +1,309 @@ +namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single.Solvers.Iterative +{ + using System; + using LinearAlgebra.Single; + using LinearAlgebra.Single.Solvers; + using LinearAlgebra.Single.Solvers.Iterative; + using LinearAlgebra.Single.Solvers.StopCriterium; + using LinearAlgebra.Generic; + using LinearAlgebra.Generic.Solvers.Status; + using LinearAlgebra.Generic.Solvers.StopCriterium; + using MbUnit.Framework; + + [TestFixture] + public class MlkBiCgStabTest + { + private const float ConvergenceBoundary = 1e-5f; + private const int MaximumIterations = 1000; + + [Test] + [ExpectedArgumentException] + public void SolveWideMatrix() + { + var matrix = new SparseMatrix(2, 3); + Vector input = new DenseVector(2); + + var solver = new MlkBiCgStab(); + solver.Solve(matrix, input); + } + + [Test] + [ExpectedArgumentException] + public void SolveLongMatrix() + { + var matrix = new SparseMatrix(3, 2); + Vector input = new DenseVector(3); + + var solver = new MlkBiCgStab(); + solver.Solve(matrix, input); + } + + [Test] + [MultipleAsserts] + public void SolveUnitMatrixAndBackMultiply() + { + // Create the identity matrix + Matrix matrix = SparseMatrix.Identity(100); + + // Create the y vector + Vector y = new DenseVector(matrix.RowCount, 1); + + // Create an iteration monitor which will keep track of iterative convergence + var monitor = new Iterator(new IIterationStopCriterium[] + { + new IterationCountStopCriterium(MaximumIterations), + new ResidualStopCriterium(ConvergenceBoundary), + new DivergenceStopCriterium(), + new FailureStopCriterium() + }); + + var solver = new MlkBiCgStab(monitor); + + // Solve equation Ax = y + var x = solver.Solve(matrix, y); + + // Now compare the results + Assert.IsNotNull(x, "#02"); + Assert.AreEqual(y.Count, x.Count, "#03"); + + // Back multiply the vector + var z = matrix.Multiply(x); + + // Check that the solution converged + Assert.IsTrue(monitor.Status is CalculationConverged, "#04"); + + // Now compare the vectors + for (var i = 0; i < y.Count; i++) + { + Assert.IsTrue((y[i] - z[i]).IsSmaller(ConvergenceBoundary, 1), "#05-" + i); + } + } + + [Test] + [MultipleAsserts] + public void SolveScaledUnitMatrixAndBackMultiply() + { + // Create the identity matrix + Matrix matrix = SparseMatrix.Identity(100); + + // Scale it with a funny number + matrix.Multiply((float)Math.PI); + + // Create the y vector + Vector y = new DenseVector(matrix.RowCount, 1); + + // Create an iteration monitor which will keep track of iterative convergence + var monitor = new Iterator(new IIterationStopCriterium[] + { + new IterationCountStopCriterium(MaximumIterations), + new ResidualStopCriterium(ConvergenceBoundary), + new DivergenceStopCriterium(), + new FailureStopCriterium() + }); + var solver = new MlkBiCgStab(monitor); + + // Solve equation Ax = y + var x = solver.Solve(matrix, y); + + // Now compare the results + Assert.IsNotNull(x, "#02"); + Assert.AreEqual(y.Count, x.Count, "#03"); + + // Back multiply the vector + var z = matrix.Multiply(x); + + // Check that the solution converged + Assert.IsTrue(monitor.Status is CalculationConverged, "#04"); + + // Now compare the vectors + for (var i = 0; i < y.Count; i++) + { + Assert.IsTrue((y[i] - z[i]).IsSmaller(ConvergenceBoundary, 1), "#05-" + i); + } + } + + [Test] + [MultipleAsserts] + public void SolvePoissonMatrixAndBackMultiply() + { + // Create the matrix + var matrix = new SparseMatrix(25); + // Assemble the matrix. We assume we're solving the Poisson equation + // on a rectangular 5 x 5 grid + const int GridSize = 5; + + // The pattern is: + // 0 .... 0 -1 0 0 0 0 0 0 0 0 -1 4 -1 0 0 0 0 0 0 0 0 -1 0 0 ... 0 + for (var i = 0; i < matrix.RowCount; i++) + { + // Insert the first set of -1's + if (i > (GridSize - 1)) + { + matrix[i, i - GridSize] = -1; + } + + // Insert the second set of -1's + if (i > 0) + { + matrix[i, i - 1] = -1; + } + + // Insert the centerline values + matrix[i, i] = 4; + + // Insert the first trailing set of -1's + if (i < matrix.RowCount - 1) + { + matrix[i, i + 1] = -1; + } + + // Insert the second trailing set of -1's + if (i < matrix.RowCount - GridSize) + { + matrix[i, i + GridSize] = -1; + } + } + + // Create the y vector + Vector y = new DenseVector(matrix.RowCount, 1); + + // Due to datatype "float" it can happen that solution will not converge for specific random starting vectors + // That's why we will do 3 tries + for (var iteration = 0; iteration <= 3; iteration++) + { + // Create an iteration monitor which will keep track of iterative convergence + var monitor = new Iterator(new IIterationStopCriterium[] + { + new IterationCountStopCriterium(MaximumIterations), + new ResidualStopCriterium(ConvergenceBoundary), + new DivergenceStopCriterium(), + new FailureStopCriterium() + }); + var solver = new MlkBiCgStab(monitor); + + // Solve equation Ax = y + Vector x; + try + { + x = solver.Solve(matrix, y); + } + catch (Exception) + { + continue; + } + + + if (!(monitor.Status is CalculationConverged)) + { + continue; + } + + // Now compare the results + Assert.IsNotNull(x, "#02"); + Assert.AreEqual(y.Count, x.Count, "#03"); + + // Back multiply the vector + var z = matrix.Multiply(x); + + // Now compare the vectors + for (var i = 0; i < y.Count; i++) + { + Assert.IsTrue(Math.Abs(y[i] - z[i]).IsSmaller(ConvergenceBoundary, 1), "#04-" + i); + } + + return; + } + + Assert.Fail("Solution was not found in 3 tries"); + } + + [Test] + [Row(5)] + [MultipleAsserts] + public void CanSolveForRandomVector(int order) + { + // Due to datatype "float" it can happen that solution will not converge for specific random matrix + // That's why we will do 4 tries and downgrade stop criterium each time + for (var iteration = 6; iteration > 3; iteration--) + { + var matrixA = MatrixLoader.GenerateRandomDenseMatrix(order, order); + var vectorb = MatrixLoader.GenerateRandomDenseVector(order); + + var monitor = new Iterator(new IIterationStopCriterium[] + { + new IterationCountStopCriterium(MaximumIterations), + new ResidualStopCriterium((float)Math.Pow(1.0/10.0, iteration)), + }); + var solver = new MlkBiCgStab(monitor); + var resultx = solver.Solve(matrixA, vectorb); + + if (!(monitor.Status is CalculationConverged)) + { + // Solution was not found, try again downgrading convergence boundary + continue; + } + + 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], (float)Math.Pow(1.0 / 10.0, iteration - 3)); + } + + return; + } + + Assert.Fail("Solution was not found in 3 tries"); + } + + [Test] + [Row(5)] + [MultipleAsserts] + public void CanSolveForRandomMatrix(int order) + { + // Due to datatype "float" it can happen that solution will not converge for specific random matrix + // That's why we will do 4 tries and downgrade stop criterium each time + for (var iteration = 6; iteration > 3; iteration--) + { + var matrixA = MatrixLoader.GenerateRandomDenseMatrix(order, order); + var matrixB = MatrixLoader.GenerateRandomDenseMatrix(order, order); + + var monitor = new Iterator(new IIterationStopCriterium[] + { + new IterationCountStopCriterium(MaximumIterations), + new ResidualStopCriterium((float)Math.Pow(1.0/10.0, iteration)) + }); + var solver = new MlkBiCgStab(monitor); + var matrixX = solver.Solve(matrixA, matrixB); + + if (!(monitor.Status is CalculationConverged)) + { + // Solution was not found, try again downgrading convergence boundary + continue; + } + + // 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], (float)Math.Pow(1.0 / 10.0, iteration - 3)); + } + } + + return; + } + + Assert.Fail("Solution was not found in 3 tries"); + } + } +} diff --git a/src/UnitTests/LinearAlgebraTests/Single/Solvers/Iterative/TFQMRTest.cs b/src/UnitTests/LinearAlgebraTests/Single/Solvers/Iterative/TFQMRTest.cs new file mode 100644 index 00000000..d0f98012 --- /dev/null +++ b/src/UnitTests/LinearAlgebraTests/Single/Solvers/Iterative/TFQMRTest.cs @@ -0,0 +1,289 @@ +namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single.Solvers.Iterative +{ + using System; + using LinearAlgebra.Single; + using LinearAlgebra.Single.Solvers; + using LinearAlgebra.Single.Solvers.Iterative; + using LinearAlgebra.Single.Solvers.StopCriterium; + using LinearAlgebra.Generic; + using LinearAlgebra.Generic.Solvers.Status; + using LinearAlgebra.Generic.Solvers.StopCriterium; + using MbUnit.Framework; + + [TestFixture] + public class TFQMRTest + { + private const float ConvergenceBoundary = 1e-5f; + private const int MaximumIterations = 1000; + + [Test] + [ExpectedArgumentException] + public void SolveWideMatrix() + { + var matrix = new SparseMatrix(2, 3); + Vector input = new DenseVector(2); + + var solver = new TFQMR(); + solver.Solve(matrix, input); + } + + [Test] + [ExpectedArgumentException] + public void SolveLongMatrix() + { + var matrix = new SparseMatrix(3, 2); + Vector input = new DenseVector(3); + + var solver = new TFQMR(); + solver.Solve(matrix, input); + } + + [Test] + [MultipleAsserts] + public void SolveUnitMatrixAndBackMultiply() + { + // Create the identity matrix + Matrix matrix = SparseMatrix.Identity(100); + + // Create the y vector + Vector y = new DenseVector(matrix.RowCount, 1); + + // Create an iteration monitor which will keep track of iterative convergence + var monitor = new Iterator(new IIterationStopCriterium[] + { + new IterationCountStopCriterium(MaximumIterations), + new ResidualStopCriterium(ConvergenceBoundary), + new DivergenceStopCriterium(), + new FailureStopCriterium() + }); + + var solver = new TFQMR(monitor); + + // Solve equation Ax = y + var x = solver.Solve(matrix, y); + + // Now compare the results + Assert.IsNotNull(x, "#02"); + Assert.AreEqual(y.Count, x.Count, "#03"); + + // Back multiply the vector + var z = matrix.Multiply(x); + + // Check that the solution converged + Assert.IsTrue(monitor.Status is CalculationConverged, "#04"); + + // Now compare the vectors + for (var i = 0; i < y.Count; i++) + { + Assert.IsTrue((y[i] - z[i]).IsSmaller(ConvergenceBoundary, 1), "#05-" + i); + } + } + + [Test] + [MultipleAsserts] + public void SolveScaledUnitMatrixAndBackMultiply() + { + // Create the identity matrix + Matrix matrix = SparseMatrix.Identity(100); + + // Scale it with a funny number + matrix.Multiply((float)Math.PI); + + // Create the y vector + Vector y = new DenseVector(matrix.RowCount, 1); + + // Create an iteration monitor which will keep track of iterative convergence + var monitor = new Iterator(new IIterationStopCriterium[] + { + new IterationCountStopCriterium(MaximumIterations), + new ResidualStopCriterium(ConvergenceBoundary), + new DivergenceStopCriterium(), + new FailureStopCriterium() + }); + var solver = new TFQMR(monitor); + + // Solve equation Ax = y + var x = solver.Solve(matrix, y); + + // Now compare the results + Assert.IsNotNull(x, "#02"); + Assert.AreEqual(y.Count, x.Count, "#03"); + + // Back multiply the vector + var z = matrix.Multiply(x); + + // Check that the solution converged + Assert.IsTrue(monitor.Status is CalculationConverged, "#04"); + + // Now compare the vectors + for (var i = 0; i < y.Count; i++) + { + Assert.IsTrue((y[i] - z[i]).IsSmaller(ConvergenceBoundary, 1), "#05-" + i); + } + } + + [Test] + [MultipleAsserts] + public void SolvePoissonMatrixAndBackMultiply() + { + // Create the matrix + var matrix = new SparseMatrix(25); + // Assemble the matrix. We assume we're solving the Poisson equation + // on a rectangular 5 x 5 grid + const int GridSize = 5; + + // The pattern is: + // 0 .... 0 -1 0 0 0 0 0 0 0 0 -1 4 -1 0 0 0 0 0 0 0 0 -1 0 0 ... 0 + for (var i = 0; i < matrix.RowCount; i++) + { + // Insert the first set of -1's + if (i > (GridSize - 1)) + { + matrix[i, i - GridSize] = -1; + } + + // Insert the second set of -1's + if (i > 0) + { + matrix[i, i - 1] = -1; + } + + // Insert the centerline values + matrix[i, i] = 4; + + // Insert the first trailing set of -1's + if (i < matrix.RowCount - 1) + { + matrix[i, i + 1] = -1; + } + + // Insert the second trailing set of -1's + if (i < matrix.RowCount - GridSize) + { + matrix[i, i + GridSize] = -1; + } + } + + // Create the y vector + Vector y = new DenseVector(matrix.RowCount, 1); + + // Create an iteration monitor which will keep track of iterative convergence + var monitor = new Iterator(new IIterationStopCriterium[] + { + new IterationCountStopCriterium(MaximumIterations), + new ResidualStopCriterium(ConvergenceBoundary), + new DivergenceStopCriterium(), + new FailureStopCriterium() + }); + var solver = new TFQMR(monitor); + + // Solve equation Ax = y + var x = solver.Solve(matrix, y); + + // Now compare the results + Assert.IsNotNull(x, "#02"); + Assert.AreEqual(y.Count, x.Count, "#03"); + + // Back multiply the vector + var z = matrix.Multiply(x); + + // Check that the solution converged + Assert.IsTrue(monitor.Status is CalculationConverged, "#04"); + + // Now compare the vectors + for (var i = 0; i < y.Count; i++) + { + Assert.IsTrue(Math.Abs(y[i] - z[i]).IsSmaller(ConvergenceBoundary, 1), "#05-" + i); + } + } + + [Test] + [Row(5)] + [MultipleAsserts] + public void CanSolveForRandomVector(int order) + { + // Due to datatype "float" it can happen that solution will not converge for specific random matrix + // That's why we will do 3 tries and downgrade stop criterium each time + for (var iteration = 6; iteration > 3; iteration--) + { + var matrixA = MatrixLoader.GenerateRandomDenseMatrix(order, order); + var vectorb = MatrixLoader.GenerateRandomDenseVector(order); + + var monitor = new Iterator(new IIterationStopCriterium[] + { + new IterationCountStopCriterium(MaximumIterations), + new ResidualStopCriterium((float)Math.Pow(1.0/10.0, iteration)), + }); + var solver = new TFQMR(monitor); + var resultx = solver.Solve(matrixA, vectorb); + + if (!(monitor.Status is CalculationConverged)) + { + // Solution was not found, try again downgrading convergence boundary + continue; + } + + 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], (float)Math.Pow(1.0 / 10.0, iteration - 3)); + } + + return; + } + + Assert.Fail("Solution was not found in 3 tries"); + } + + [Test] + [Row(5)] + [MultipleAsserts] + public void CanSolveForRandomMatrix(int order) + { + // Due to datatype "float" it can happen that solution will not converge for specific random matrix + // That's why we will do 4 tries and downgrade stop criterium each time + for (var iteration = 6; iteration > 3; iteration--) + { + var matrixA = MatrixLoader.GenerateRandomDenseMatrix(order, order); + var matrixB = MatrixLoader.GenerateRandomDenseMatrix(order, order); + + var monitor = new Iterator(new IIterationStopCriterium[] + { + new IterationCountStopCriterium(MaximumIterations), + new ResidualStopCriterium((float)Math.Pow(1.0/10.0, iteration)) + }); + var solver = new TFQMR(monitor); + var matrixX = solver.Solve(matrixA, matrixB); + + if (!(monitor.Status is CalculationConverged)) + { + // Solution was not found, try again downgrading convergence boundary + continue; + } + + // 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], (float)Math.Pow(1.0 / 10.0, iteration - 3)); + } + } + + return; + } + + Assert.Fail("Solution was not found in 3 tries"); + } + } +} diff --git a/src/UnitTests/LinearAlgebraTests/Single/Solvers/IteratorTest.cs b/src/UnitTests/LinearAlgebraTests/Single/Solvers/IteratorTest.cs new file mode 100644 index 00000000..728fb936 --- /dev/null +++ b/src/UnitTests/LinearAlgebraTests/Single/Solvers/IteratorTest.cs @@ -0,0 +1,354 @@ +namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single.Solvers +{ + using System; + using System.Collections.Generic; + using LinearAlgebra.Single; + using LinearAlgebra.Single.Solvers; + using LinearAlgebra.Single.Solvers.StopCriterium; + using LinearAlgebra.Generic.Solvers.Status; + using LinearAlgebra.Generic.Solvers.StopCriterium; + using MbUnit.Framework; + + [TestFixture] + public class IteratorTest + { + [Test] + [MultipleAsserts] + public void CreateWithNullCollection() + { + var iterator = new Iterator(null); + Assert.IsNotNull(iterator, "Should have an iterator"); + Assert.AreEqual(0, iterator.NumberOfCriteria, "There shouldn't be any criteria"); + } + + [Test] + [MultipleAsserts] + public void CreateWithEmptyCollection() + { + var iterator = new Iterator(new IIterationStopCriterium[] { }); + Assert.IsNotNull(iterator, "Should have an iterator"); + Assert.AreEqual(0, iterator.NumberOfCriteria, "There shouldn't be any criteria"); + } + + [Test] + [MultipleAsserts] + public void CreateWithCollectionWithNulls() + { + var iterator = new Iterator(new IIterationStopCriterium[] { null, null }); + Assert.IsNotNull(iterator, "Should have an iterator"); + Assert.AreEqual(0, iterator.NumberOfCriteria, "There shouldn't be any criteria"); + } + + [Test] + [ExpectedArgumentException] + public void CreateWithDuplicates() + { + new Iterator(new IIterationStopCriterium[] + { + new FailureStopCriterium(), + new FailureStopCriterium() + }); + } + + [Test] + [MultipleAsserts] + public void CreateWithCollection() + { + var criteria = new List> + { + new FailureStopCriterium(), + new DivergenceStopCriterium(), + new IterationCountStopCriterium(), + new ResidualStopCriterium() + }; + var iterator = new Iterator(criteria); + Assert.IsNotNull(iterator, "Should have an iterator"); + + // Check that we have all the criteria + Assert.AreEqual(criteria.Count, iterator.NumberOfCriteria, "Incorrect criterium count"); + var enumerator = iterator.StoredStopCriteria; + while (enumerator.MoveNext()) + { + var criterium = enumerator.Current; + Assert.IsTrue(criteria.Exists( c => ReferenceEquals(c, criterium)), "Criterium missing"); + } + } + + [Test] + [ExpectedArgumentNullException] + public void AddWithNullStopCriterium() + { + var iterator = new Iterator(); + iterator.Add(null); + } + + [Test] + [ExpectedArgumentException] + public void AddWithExistingStopCriterium() + { + var iterator = new Iterator(); + iterator.Add(new FailureStopCriterium()); + Assert.AreEqual(1, iterator.NumberOfCriteria, "Incorrect criterium count"); + + iterator.Add(new FailureStopCriterium()); + } + + [Test] + [MultipleAsserts] + public void Add() + { + var criteria = new List> + { + new FailureStopCriterium(), + new DivergenceStopCriterium(), + new IterationCountStopCriterium(), + new ResidualStopCriterium() + }; + var iterator = new Iterator(); + Assert.AreEqual(0, iterator.NumberOfCriteria, "Incorrect criterium count"); + + foreach (var criterium in criteria) + { + iterator.Add(criterium); + Assert.IsTrue(iterator.Contains(criterium), "Missing criterium"); + } + + // Check that we have all the criteria + Assert.AreEqual(criteria.Count, iterator.NumberOfCriteria, "Incorrect criterium count"); + var enumerator = iterator.StoredStopCriteria; + while (enumerator.MoveNext()) + { + var criterium = enumerator.Current; + Assert.IsTrue(criteria.Exists( c => ReferenceEquals(c, criterium)), "Criterium missing"); + } + } + + [Test] + [ExpectedArgumentNullException] + public void RemoveWithNullStopCriterium() + { + var criteria = new List> + { + new FailureStopCriterium(), + new DivergenceStopCriterium(), + new IterationCountStopCriterium(), + new ResidualStopCriterium() + }; + var iterator = new Iterator(criteria); + Assert.AreEqual(criteria.Count, iterator.NumberOfCriteria, "Incorrect criterium count"); + + iterator.Remove(null); + } + + [Test] + [MultipleAsserts] + public void RemoveWithNonExistingStopCriterium() + { + var criteria = new List> + { + new FailureStopCriterium(), + new DivergenceStopCriterium(), + new IterationCountStopCriterium(), + }; + var iterator = new Iterator(criteria); + Assert.AreEqual(criteria.Count, iterator.NumberOfCriteria, "Incorrect criterium count"); + + iterator.Remove(new ResidualStopCriterium()); + Assert.AreEqual(criteria.Count, iterator.NumberOfCriteria, "Incorrect criterium count"); + } + + [Test] + [MultipleAsserts] + public void Remove() + { + var criteria = new List> + { + new FailureStopCriterium(), + new DivergenceStopCriterium(), + new IterationCountStopCriterium(), + new ResidualStopCriterium() + }; + var iterator = new Iterator(criteria); + Assert.AreEqual(criteria.Count, iterator.NumberOfCriteria, "Incorrect criterium count"); + + foreach (var criterium in criteria) + { + iterator.Remove(criterium); + Assert.IsFalse(iterator.Contains(criterium), "Did not remove the criterium"); + } + } + + [Test] + [ExpectedArgumentException] + public void DetermineStatusWithoutStopCriteria() + { + var iterator = new Iterator(); + iterator.DetermineStatus(0, + new DenseVector(3, 4), + new DenseVector(3, 5), + new DenseVector(3, 6)); + } + + [Test] + [ExpectedException(typeof(ArgumentOutOfRangeException))] + public void DetermineStatusWithNegativeIterationNumber() + { + var criteria = new List> + { + new FailureStopCriterium(), + new DivergenceStopCriterium(), + new IterationCountStopCriterium(), + new ResidualStopCriterium() + }; + var iterator = new Iterator(criteria); + + iterator.DetermineStatus(-1, + new DenseVector(3, 4), + new DenseVector(3, 5), + new DenseVector(3, 6)); + } + + [Test] + [ExpectedArgumentNullException] + public void DetermineStatusWithNullSolutionVector() + { + var criteria = new List> + { + new FailureStopCriterium(), + new DivergenceStopCriterium(), + new IterationCountStopCriterium(), + new ResidualStopCriterium() + }; + var iterator = new Iterator(criteria); + + iterator.DetermineStatus(1, + null, + new DenseVector(3, 5), + new DenseVector(3, 6)); + } + + [Test] + [ExpectedArgumentNullException] + public void DetermineStatusWithNullSourceVector() + { + var criteria = new List> + { + new FailureStopCriterium(), + new DivergenceStopCriterium(), + new IterationCountStopCriterium(), + new ResidualStopCriterium() + }; + var iterator = new Iterator(criteria); + + iterator.DetermineStatus(1, + new DenseVector(3, 5), + null, + new DenseVector(3, 6)); + } + + [Test] + [ExpectedArgumentNullException] + public void DetermineStatusWithNullResidualVector() + { + var criteria = new List> + { + new FailureStopCriterium(), + new DivergenceStopCriterium(), + new IterationCountStopCriterium(), + new ResidualStopCriterium() + }; + var iterator = new Iterator(criteria); + + iterator.DetermineStatus(1, + new DenseVector(3, 4), + new DenseVector(3, 5), + null); + } + + [Test] + [MultipleAsserts] + public void DetermineStatus() + { + var criteria = new List> + { + new FailureStopCriterium(), + new DivergenceStopCriterium(), + new IterationCountStopCriterium(1) + }; + + var iterator = new Iterator(criteria); + + // First step, nothing should happen. + iterator.DetermineStatus(0, + new DenseVector(3, 4), + new DenseVector(3, 4), + new DenseVector(3, 4)); + Assert.IsInstanceOfType(typeof(CalculationRunning), iterator.Status, "Incorrect status"); + + // Second step, should run out of iterations. + iterator.DetermineStatus(1, + new DenseVector(3, 4), + new DenseVector(3, 4), + new DenseVector(3, 4)); + Assert.IsInstanceOfType(typeof(CalculationStoppedWithoutConvergence), iterator.Status, "Incorrect status"); + } + + [Test] + [MultipleAsserts] + public void ResetToPrecalculationState() + { + var criteria = new List> + { + new FailureStopCriterium(), + new DivergenceStopCriterium(), + new IterationCountStopCriterium(1) + }; + + var iterator = new Iterator(criteria); + + // First step, nothing should happen. + iterator.DetermineStatus(0, + new DenseVector(3, 4), + new DenseVector(3, 4), + new DenseVector(3, 4)); + Assert.IsInstanceOfType(typeof(CalculationRunning), iterator.Status, "Incorrect status"); + + iterator.ResetToPrecalculationState(); + Assert.IsInstanceOfType(typeof(CalculationIndetermined), iterator.Status, "Incorrect status"); + Assert.IsInstanceOfType(typeof(CalculationIndetermined), criteria[0].Status, "Incorrect status"); + Assert.IsInstanceOfType(typeof(CalculationIndetermined), criteria[1].Status, "Incorrect status"); + Assert.IsInstanceOfType(typeof(CalculationIndetermined), criteria[2].Status, "Incorrect status"); + } + + [Test] + [MultipleAsserts] + public void Clone() + { + var criteria = new List> + { + new FailureStopCriterium(), + new DivergenceStopCriterium(), + new IterationCountStopCriterium(), + new ResidualStopCriterium() + }; + + var iterator = new Iterator(criteria); + + var clonedIterator = iterator.Clone(); + Assert.IsInstanceOfType(typeof(Iterator), clonedIterator, "Incorrect type"); + + var clone = clonedIterator as Iterator; + Assert.IsNotNull(clone); + // ReSharper disable PossibleNullReferenceException + Assert.AreEqual(iterator.NumberOfCriteria, clone.NumberOfCriteria, "Incorrect criterium count"); + // ReSharper restore PossibleNullReferenceException + + var enumerator = clone.StoredStopCriteria; + while (enumerator.MoveNext()) + { + var criterium = enumerator.Current; + Assert.IsTrue(criteria.Exists(c => c.GetType().Equals(criterium.GetType())), "Criterium missing"); + } + } + } +} diff --git a/src/UnitTests/LinearAlgebraTests/Single/Solvers/Preconditioners/DiagonalTest.cs b/src/UnitTests/LinearAlgebraTests/Single/Solvers/Preconditioners/DiagonalTest.cs new file mode 100644 index 00000000..d1dc1f4c --- /dev/null +++ b/src/UnitTests/LinearAlgebraTests/Single/Solvers/Preconditioners/DiagonalTest.cs @@ -0,0 +1,32 @@ +namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single.Solvers.Preconditioners +{ + using LinearAlgebra.Single; + using LinearAlgebra.Single.Solvers.Preconditioners; + using LinearAlgebra.Generic; + using LinearAlgebra.Generic.Solvers.Preconditioners; + using MbUnit.Framework; + + [TestFixture] + public sealed class DiagonalTest : PreconditionerTest + { + internal override IPreConditioner CreatePreconditioner() + { + return new Diagonal(); + } + + protected override void CheckResult(IPreConditioner preconditioner, SparseMatrix matrix, Vector vector, Vector result) + { + Assert.AreEqual(typeof(Diagonal), preconditioner.GetType(), "#01"); + + // Compute M * result = product + // compare vector and product. Should be equal + Vector product = new DenseVector(result.Count); + matrix.Multiply(result, product); + + for (var i = 0; i < product.Count; i++) + { + Assert.IsTrue(((double)vector[i]).AlmostEqual(product[i], -Epsilon.Magnitude()), "#02-" + i); + } + } + } +} diff --git a/src/UnitTests/LinearAlgebraTests/Single/Solvers/Preconditioners/IluptElementSorterTest.cs b/src/UnitTests/LinearAlgebraTests/Single/Solvers/Preconditioners/IluptElementSorterTest.cs new file mode 100644 index 00000000..5ed7d006 --- /dev/null +++ b/src/UnitTests/LinearAlgebraTests/Single/Solvers/Preconditioners/IluptElementSorterTest.cs @@ -0,0 +1,503 @@ +// NOTE: This class file Build Action is not set to Compile by default. Because IlutpElementSorter class is internal. If you want +// NOTE: to test IlutpElementSorter you should make it public, set Build Action=Compile of this file (in properties) and run tets. +// NOTE: After all tests passed please do all actions vice versa. IlutpElementSorter class is only for internal usage. + +namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single.Solvers.Preconditioners +{ + using LinearAlgebra.Generic; + using LinearAlgebra.Single; + using LinearAlgebra.Single.Solvers.Preconditioners; + using MbUnit.Framework; + + [TestFixture] + public sealed class IluptElementSorterTest + { + [Test] + [MultipleAsserts] + public void HeapSortWithIncreasingIntergerArray() + { + var sortedIndices = new [] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + IlutpElementSorter.SortIntegersDecreasing(sortedIndices); + for (var i = 0; i < sortedIndices.Length; i++) + { + Assert.AreEqual(sortedIndices.Length - 1 - i, sortedIndices[i], "#01-" + i); + } + } + + [Test] + [MultipleAsserts] + public void HeapSortWithDecreasingIntegerArray() + { + var sortedIndices = new [] { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }; + IlutpElementSorter.SortIntegersDecreasing(sortedIndices); + for (var i = 0; i < sortedIndices.Length; i++) + { + Assert.AreEqual(sortedIndices.Length - 1 - i, sortedIndices[i], "#01-" + i); + } + } + + [Test] + [MultipleAsserts] + public void HeapSortWithRandomIntegerArray() + { + var sortedIndices = new []{ 5, 2, 8, 6, 0, 4, 1, 7, 3, 9 }; + IlutpElementSorter.SortIntegersDecreasing(sortedIndices); + for (var i = 0; i < sortedIndices.Length; i++) + { + Assert.AreEqual(sortedIndices.Length - 1 - i, sortedIndices[i], "#01-" + i); + } + } + + [Test] + [MultipleAsserts] + public void HeapSortWithDuplicateEntries() + { + var sortedIndices = new []{ 1, 1, 1, 1, 2, 2, 2, 2, 3, 4 }; + IlutpElementSorter.SortIntegersDecreasing(sortedIndices); + for (var i = 0; i < sortedIndices.Length; i++) + { + if (i == 0) + { + Assert.AreEqual(4, sortedIndices[i], "#01-" + i); + } + else + { + if (i == 1) + { + Assert.AreEqual(3, sortedIndices[i], "#01-" + i); + } + else + { + if (i < 6) + { + if (sortedIndices[i] != 2) + { + Assert.Fail("#01-" + i); + } + } + else + { + if (sortedIndices[i] != 1) + { + Assert.Fail("#01-" + i); + } + } + } + } + } + } + + [Test] + [MultipleAsserts] + public void HeapSortWithSpecialConstructedIntegerArray() + { + var sortedIndices = new []{ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0 }; + IlutpElementSorter.SortIntegersDecreasing(sortedIndices); + for (var i = 0; i < sortedIndices.Length; i++) + { + if (i == 0) + { + Assert.AreEqual(1, sortedIndices[i], "#01-" + i); + break; + } + } + + sortedIndices = new []{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + IlutpElementSorter.SortIntegersDecreasing(sortedIndices); + for (var i = 0; i < sortedIndices.Length; i++) + { + if (i == 0) + { + Assert.AreEqual(1, sortedIndices[i], "#02-" + i); + break; + } + } + + sortedIndices = new []{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }; + IlutpElementSorter.SortIntegersDecreasing(sortedIndices); + for (var i = 0; i < sortedIndices.Length; i++) + { + if (i == 0) + { + Assert.AreEqual(1, sortedIndices[i], "#03-" + i); + break; + } + } + + sortedIndices = new []{ 1, 1, 1, 0, 1, 1, 1, 1, 1, 1 }; + IlutpElementSorter.SortIntegersDecreasing(sortedIndices); + for (var i = 0; i < sortedIndices.Length; i++) + { + if (i == 9) + { + Assert.AreEqual(0, sortedIndices[i], "#04-" + i); + break; + } + } + } + + [Test] + [MultipleAsserts] + public void HeapSortWithIncreasingDoubleArray() + { + var sortedIndices = new int[10]; + Vector values = new DenseVector(10); + values[0] = 0; + values[1] = 1; + values[2] = 2; + values[3] = 3; + values[4] = 4; + values[5] = 5; + values[6] = 6; + values[7] = 7; + values[8] = 8; + values[9] = 9; + for (var i = 0; i < sortedIndices.Length; i++) + { + sortedIndices[i] = i; + } + + IlutpElementSorter.SortDoubleIndicesDecreasing(0, sortedIndices.Length - 1, sortedIndices, values); + for (var i = 0; i < sortedIndices.Length; i++) + { + Assert.AreEqual(sortedIndices.Length - 1 - i, sortedIndices[i], "#01-" + i); + } + } + + [Test] + [MultipleAsserts] + public void HeapSortWithDecreasingDoubleArray() + { + var sortedIndices = new int[10]; + Vector values = new DenseVector(10); + values[0] = 9; + values[1] = 8; + values[2] = 7; + values[3] = 6; + values[4] = 5; + values[5] = 4; + values[6] = 3; + values[7] = 2; + values[8] = 1; + values[9] = 0; + for (var i = 0; i < sortedIndices.Length; i++) + { + sortedIndices[i] = i; + } + + IlutpElementSorter.SortDoubleIndicesDecreasing(0, sortedIndices.Length - 1, sortedIndices, values); + for (var i = 0; i < sortedIndices.Length; i++) + { + Assert.AreEqual(i, sortedIndices[i], "#01-" + i); + } + } + + [Test] + [MultipleAsserts] + public void HeapSortWithRandomDoubleArray() + { + var sortedIndices = new int[10]; + Vector values = new DenseVector(10); + values[0] = 5; + values[1] = 2; + values[2] = 8; + values[3] = 6; + values[4] = 0; + values[5] = 4; + values[6] = 1; + values[7] = 7; + values[8] = 3; + values[9] = 9; + for (var i = 0; i < sortedIndices.Length; i++) + { + sortedIndices[i] = i; + } + + IlutpElementSorter.SortDoubleIndicesDecreasing(0, sortedIndices.Length - 1, sortedIndices, values); + for (var i = 0; i < sortedIndices.Length; i++) + { + switch (i) + { + case 0: + Assert.AreEqual(9, sortedIndices[i], "#01-" + i); + break; + case 1: + Assert.AreEqual(2, sortedIndices[i], "#01-" + i); + break; + case 2: + Assert.AreEqual(7, sortedIndices[i], "#01-" + i); + break; + case 3: + Assert.AreEqual(3, sortedIndices[i], "#01-" + i); + break; + case 4: + Assert.AreEqual(0, sortedIndices[i], "#01-" + i); + break; + case 5: + Assert.AreEqual(5, sortedIndices[i], "#01-" + i); + break; + case 6: + Assert.AreEqual(8, sortedIndices[i], "#01-" + i); + break; + case 7: + Assert.AreEqual(1, sortedIndices[i], "#01-" + i); + break; + case 8: + Assert.AreEqual(6, sortedIndices[i], "#01-" + i); + break; + case 9: + Assert.AreEqual(4, sortedIndices[i], "#01-" + i); + break; + } + } + } + + [Test] + [MultipleAsserts] + public void HeapSortWithDuplicateDoubleEntries() + { + var sortedIndices = new int[10]; + Vector values = new DenseVector(10); + values[0] = 1; + values[1] = 1; + values[2] = 1; + values[3] = 1; + values[4] = 2; + values[5] = 2; + values[6] = 2; + values[7] = 2; + values[8] = 3; + values[9] = 4; + + for (var i = 0; i < sortedIndices.Length; i++) + { + sortedIndices[i] = i; + } + IlutpElementSorter.SortDoubleIndicesDecreasing(0, sortedIndices.Length - 1, sortedIndices, values); + for (var i = 0; i < sortedIndices.Length; i++) + { + if (i == 0) + { + Assert.AreEqual(9, sortedIndices[i], "#01-" + i); + } + else + { + if (i == 1) + { + Assert.AreEqual(8, sortedIndices[i], "#01-" + i); + } + else + { + if (i < 6) + { + if ((sortedIndices[i] != 4) && + (sortedIndices[i] != 5) && + (sortedIndices[i] != 6) && + (sortedIndices[i] != 7)) + { + Assert.Fail("#01-" + i); + } + } + else + { + if ((sortedIndices[i] != 0) && + (sortedIndices[i] != 1) && + (sortedIndices[i] != 2) && + (sortedIndices[i] != 3)) + { + Assert.Fail("#01-" + i); + } + } + } + } + } + } + + [Test] + [MultipleAsserts] + public void HeapSortWithSpecialConstructedDoubleArray() + { + var sortedIndices = new int[10]; + Vector values = new DenseVector(10); + values[0] = 0; + values[1] = 0; + values[2] = 0; + values[3] = 0; + values[4] = 0; + values[5] = 1; + values[6] = 0; + values[7] = 0; + values[8] = 0; + values[9] = 0; + for (var i = 0; i < sortedIndices.Length; i++) + { + sortedIndices[i] = i; + } + IlutpElementSorter.SortDoubleIndicesDecreasing(0, sortedIndices.Length - 1, sortedIndices, values); + for (var i = 0; i < sortedIndices.Length; i++) + { + if (i == 0) + { + Assert.AreEqual(5, sortedIndices[i], "#01-" + i); + break; + } + } + + values[0] = 1; + values[1] = 0; + values[2] = 0; + values[3] = 0; + values[4] = 0; + values[5] = 0; + values[6] = 0; + values[7] = 0; + values[8] = 0; + values[9] = 0; + for (var i = 0; i < sortedIndices.Length; i++) + { + sortedIndices[i] = i; + } + IlutpElementSorter.SortDoubleIndicesDecreasing(0, sortedIndices.Length - 1, sortedIndices, values); + for (var i = 0; i < sortedIndices.Length; i++) + { + if (i == 0) + { + Assert.AreEqual(0, sortedIndices[i], "#02-" + i); + break; + } + } + + values[0] = 0; + values[1] = 0; + values[2] = 0; + values[3] = 0; + values[4] = 0; + values[5] = 0; + values[6] = 0; + values[7] = 0; + values[8] = 0; + values[9] = 1; + for (var i = 0; i < sortedIndices.Length; i++) + { + sortedIndices[i] = i; + } + IlutpElementSorter.SortDoubleIndicesDecreasing(0, sortedIndices.Length - 1, sortedIndices, values); + for (var i = 0; i < sortedIndices.Length; i++) + { + if (i == 0) + { + Assert.AreEqual(9, sortedIndices[i], "#03-" + i); + break; + } + } + + values[0] = 1; + values[1] = 1; + values[2] = 1; + values[3] = 0; + values[4] = 1; + values[5] = 1; + values[6] = 1; + values[7] = 1; + values[8] = 1; + values[9] = 1; + for (var i = 0; i < sortedIndices.Length; i++) + { + sortedIndices[i] = i; + } + IlutpElementSorter.SortDoubleIndicesDecreasing(0, sortedIndices.Length - 1, sortedIndices, values); + for (var i = 0; i < sortedIndices.Length; i++) + { + if (i == 9) + { + Assert.AreEqual(3, sortedIndices[i], "#04-" + i); + break; + } + } + } + + [Test] + [MultipleAsserts] + public void HeapSortWithIncreasingDoubleArrayWithLowerBound() + { + var sortedIndices = new int[10]; + Vector values = new DenseVector(10); + values[0] = 0; + values[1] = 1; + values[2] = 2; + values[3] = 3; + values[4] = 4; + values[5] = 5; + values[6] = 6; + values[7] = 7; + values[8] = 8; + values[9] = 9; + for (var i = 0; i < sortedIndices.Length; i++) + { + sortedIndices[i] = i; + } + + IlutpElementSorter.SortDoubleIndicesDecreasing(4, sortedIndices.Length - 1, sortedIndices, values); + for (var i = 0; i < sortedIndices.Length - 4; i++) + { + Assert.AreEqual(sortedIndices.Length - 1 - i, sortedIndices[i], "#01-" + i); + } + } + + [Test] + [MultipleAsserts] + public void HeapSortWithIncreasingDoubleArrayWithUpperBound() + { + var sortedIndices = new int[10]; + Vector values = new DenseVector(10); + values[0] = 0; + values[1] = 1; + values[2] = 2; + values[3] = 3; + values[4] = 4; + values[5] = 5; + values[6] = 6; + values[7] = 7; + values[8] = 8; + values[9] = 9; + for (var i = 0; i < sortedIndices.Length; i++) + { + sortedIndices[i] = i; + } + + IlutpElementSorter.SortDoubleIndicesDecreasing(0, sortedIndices.Length - 5, sortedIndices, values); + for (var i = 0; i < sortedIndices.Length - 5; i++) + { + Assert.AreEqual(sortedIndices.Length - 5 - i, sortedIndices[i], "#01-" + i); + } + } + + [Test] + [MultipleAsserts] + public void HeapSortWithIncreasingDoubleArrayWithLowerAndUpperBound() + { + var sortedIndices = new int[10]; + Vector values = new DenseVector(10); + values[0] = 0; + values[1] = 1; + values[2] = 2; + values[3] = 3; + values[4] = 4; + values[5] = 5; + values[6] = 6; + values[7] = 7; + values[8] = 8; + values[9] = 9; + for (var i = 0; i < sortedIndices.Length; i++) + { + sortedIndices[i] = i; + } + + IlutpElementSorter.SortDoubleIndicesDecreasing(2, sortedIndices.Length - 3, sortedIndices, values); + for (var i = 0; i < sortedIndices.Length - 4; i++) + { + Assert.AreEqual(sortedIndices.Length - 3 - i, sortedIndices[i], "#01-" + i); + } + } + } +} diff --git a/src/UnitTests/LinearAlgebraTests/Single/Solvers/Preconditioners/IlutpTest.cs b/src/UnitTests/LinearAlgebraTests/Single/Solvers/Preconditioners/IlutpTest.cs new file mode 100644 index 00000000..7957a67e --- /dev/null +++ b/src/UnitTests/LinearAlgebraTests/Single/Solvers/Preconditioners/IlutpTest.cs @@ -0,0 +1,250 @@ +namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single.Solvers.Preconditioners +{ + using System; + using System.Reflection; + using LinearAlgebra.Single; + using LinearAlgebra.Single.Solvers.Preconditioners; + using LinearAlgebra.Generic; + using LinearAlgebra.Generic.Solvers.Preconditioners; + using MbUnit.Framework; + + [TestFixture] + public sealed class IlutpPreconditionerTest : PreconditionerTest + { + private double _dropTolerance = 0.1; + private double _fillLevel = 1.0; + private double _pivotTolerance = 1.0; + + [SetUp] + public void Setup() + { + _dropTolerance = 0.1; + _fillLevel = 1.0; + _pivotTolerance = 1.0; + } + + private static T GetMethod(Ilutp ilutp, string methodName) + { + var type = ilutp.GetType(); + var methodInfo = type.GetMethod(methodName, + BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static, + null, + CallingConventions.Standard, + new Type[0], + null); + var obj = methodInfo.Invoke(ilutp, null); + return (T)obj; + } + + private static SparseMatrix GetUpperTriangle(Ilutp ilutp) + { + return GetMethod(ilutp, "UpperTriangle"); + } + + private static SparseMatrix GetLowerTriangle(Ilutp ilutp) + { + return GetMethod(ilutp, "LowerTriangle"); + } + + private static int[] GetPivots(Ilutp ilutp) + { + return GetMethod(ilutp, "Pivots"); + } + + private static SparseMatrix CreateReverseUnitMatrix(int size) + { + var matrix = new SparseMatrix(size); + for (var i = 0; i < size; i++) + { + matrix[i, size - 1 - i] = 2; + } + + return matrix; + } + + private Ilutp InternalCreatePreconditioner() + { + var result = new Ilutp + { + DropTolerance = _dropTolerance, + FillLevel = _fillLevel, + PivotTolerance = _pivotTolerance + }; + return result; + } + + internal override IPreConditioner CreatePreconditioner() + { + _pivotTolerance = 0; + _dropTolerance = 0.0; + _fillLevel = 100; + return InternalCreatePreconditioner(); + } + + protected override void CheckResult(IPreConditioner preconditioner, SparseMatrix matrix, Vector vector, Vector result) + { + Assert.AreEqual(typeof(Ilutp), preconditioner.GetType(), "#01"); + + // Compute M * result = product + // compare vector and product. Should be equal + Vector product = new DenseVector(result.Count); + matrix.Multiply(result, product); + for (var i = 0; i < product.Count; i++) + { + Assert.IsTrue(((double)vector[i]).AlmostEqual(product[i], -Epsilon.Magnitude()), "#02-" + i); + } + } + + [Test] + [MultipleAsserts] + public void SolveReturningOldVectorWithoutPivoting() + { + const int Size = 10; + + var newMatrix = CreateUnitMatrix(Size); + var vector = CreateStandardBcVector(Size); + + // set the pivot tolerance to zero so we don't pivot + _pivotTolerance = 0.0; + _dropTolerance = 0.0; + _fillLevel = 100; + var preconditioner = CreatePreconditioner(); + preconditioner.Initialize(newMatrix); + Vector result = new DenseVector(vector.Count); + preconditioner.Approximate(vector, result); + CheckResult(preconditioner, newMatrix, vector, result); + } + + [Test] + [MultipleAsserts] + public void SolveReturningOldVectorWithPivoting() + { + const int Size = 10; + var newMatrix = CreateUnitMatrix(Size); + var vector = CreateStandardBcVector(Size); + + // Set the pivot tolerance to 1 so we always pivot (if necessary) + _pivotTolerance = 1.0; + _dropTolerance = 0.0; + _fillLevel = 100; + var preconditioner = CreatePreconditioner(); + preconditioner.Initialize(newMatrix); + Vector result = new DenseVector(vector.Count); + preconditioner.Approximate(vector, result); + CheckResult(preconditioner, newMatrix, vector, result); + } + + [Test] + [MultipleAsserts] + public void CompareWithOriginalDenseMatrixWithoutPivoting() + { + var sparseMatrix = new SparseMatrix(3); + sparseMatrix[0, 0] = -1; + sparseMatrix[0, 1] = 5; + sparseMatrix[0, 2] = 6; + sparseMatrix[1, 0] = 3; + sparseMatrix[1, 1] = -6; + sparseMatrix[1, 2] = 1; + sparseMatrix[2, 0] = 6; + sparseMatrix[2, 1] = 8; + sparseMatrix[2, 2] = 9; + var ilu = new Ilutp + { + PivotTolerance = 0.0, + DropTolerance = 0, + FillLevel = 10 + }; + ilu.Initialize(sparseMatrix); + var l = GetLowerTriangle(ilu); + + // Assert l is lower triagonal + for (var i = 0; i < l.RowCount; i++) + { + for (var j = i + 1; j < l.RowCount; j++) + { + Assert.IsTrue(0.0.AlmostEqual(l[i,j], -Epsilon.Magnitude()), "#01-" + i + "-" + j); + } + } + + var u = GetUpperTriangle(ilu); + + // Assert u is upper triagonal + for (var i = 0; i < u.RowCount; i++) + { + for (var j = 0; j < i; j++) + { + Assert.IsTrue(0.0.AlmostEqual(u[i,j], -Epsilon.Magnitude()), "#02-" + i + "-" + j); + } + } + + var original = l.Multiply(u); + for (var i = 0; i < sparseMatrix.RowCount; i++) + { + for (var j = 0; j < sparseMatrix.ColumnCount; j++) + { + Assert.IsTrue(((double)sparseMatrix[i,j]).AlmostEqual(original[i, j], -Epsilon.Magnitude()), "#03-" + i + "-" + j); + } + } + } + + [Test] + [MultipleAsserts] + public void CompareWithOriginalDenseMatrixWithPivoting() + { + var sparseMatrix = new SparseMatrix(3); + sparseMatrix[0, 0] = -1; + sparseMatrix[0, 1] = 5; + sparseMatrix[0, 2] = 6; + sparseMatrix[1, 0] = 3; + sparseMatrix[1, 1] = -6; + sparseMatrix[1, 2] = 1; + sparseMatrix[2, 0] = 6; + sparseMatrix[2, 1] = 8; + sparseMatrix[2, 2] = 9; + var ilu = new Ilutp + { + PivotTolerance = 1.0, + DropTolerance = 0, + FillLevel = 10 + }; + ilu.Initialize(sparseMatrix); + var l = GetLowerTriangle(ilu); + var u = GetUpperTriangle(ilu); + var pivots = GetPivots(ilu); + var p = new SparseMatrix(l.RowCount); + for (var i = 0; i < p.RowCount; i++) + { + p[i, pivots[i]] = 1.0f; + } + + var temp = l.Multiply(u); + var original = temp.Multiply(p); + for (var i = 0; i < sparseMatrix.RowCount; i++) + { + for (var j = 0; j < sparseMatrix.ColumnCount; j++) + { + Assert.IsTrue(((double)sparseMatrix[i, j]).AlmostEqual(original[i, j], -Epsilon.Magnitude()), "#01-" + i + "-" + j); + } + } + } + + [Test] + [MultipleAsserts] + public void SolveWithPivoting() + { + const int Size = 10; + var newMatrix = CreateReverseUnitMatrix(Size); + var vector = CreateStandardBcVector(Size); + var preconditioner = new Ilutp + { + PivotTolerance = 1.0, + DropTolerance = 0, + FillLevel = 10 + }; + preconditioner.Initialize(newMatrix); + Vector result = new DenseVector(vector.Count); + preconditioner.Approximate(vector, result); + CheckResult(preconditioner, newMatrix, vector, result); + } + } +} diff --git a/src/UnitTests/LinearAlgebraTests/Single/Solvers/Preconditioners/IncompleteLUTest.cs b/src/UnitTests/LinearAlgebraTests/Single/Solvers/Preconditioners/IncompleteLUTest.cs new file mode 100644 index 00000000..adc799aa --- /dev/null +++ b/src/UnitTests/LinearAlgebraTests/Single/Solvers/Preconditioners/IncompleteLUTest.cs @@ -0,0 +1,84 @@ +namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single.Solvers.Preconditioners +{ + using System; + using System.Reflection; + using LinearAlgebra.Single; + using LinearAlgebra.Single.Solvers.Preconditioners; + using LinearAlgebra.Generic; + using LinearAlgebra.Generic.Solvers.Preconditioners; + using MbUnit.Framework; + + [TestFixture] + public sealed class IncompleteLUFactorizationTest : PreconditionerTest + { + private static T GetMethod(IncompleteLU ilu, string methodName) + { + var type = ilu.GetType(); + var methodInfo = type.GetMethod(methodName, + BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static, + null, + CallingConventions.Standard, + new Type[0], + null); + var obj = methodInfo.Invoke(ilu, null); + return (T)obj; + } + + private static Matrix GetUpperTriangle(IncompleteLU ilu) + { + return GetMethod>(ilu, "UpperTriangle"); + } + + private static Matrix GetLowerTriangle(IncompleteLU ilu) + { + return GetMethod>(ilu, "LowerTriangle"); + } + + internal override IPreConditioner CreatePreconditioner() + { + return new IncompleteLU(); + } + + protected override void CheckResult(IPreConditioner preconditioner, SparseMatrix matrix, Vector vector, Vector result) + { + Assert.AreEqual(typeof(IncompleteLU), preconditioner.GetType(), "#01"); + + // Compute M * result = product + // compare vector and product. Should be equal + Vector product = new DenseVector(result.Count); + matrix.Multiply(result, product); + + for (var i = 0; i < product.Count; i++) + { + Assert.IsTrue(((double)vector[i]).AlmostEqual(product[i], -Epsilon.Magnitude()), "#02-" + i); + } + } + + [Test] + [MultipleAsserts] + public void CompareWithOriginalDenseMatrix() + { + var sparseMatrix = new SparseMatrix(3); + sparseMatrix[0, 0] = -1; + sparseMatrix[0, 1] = 5; + sparseMatrix[0, 2] = 6; + sparseMatrix[1, 0] = 3; + sparseMatrix[1, 1] = -6; + sparseMatrix[1, 2] = 1; + sparseMatrix[2, 0] = 6; + sparseMatrix[2, 1] = 8; + sparseMatrix[2, 2] = 9; + var ilu = new IncompleteLU(); + ilu.Initialize(sparseMatrix); + var original = GetLowerTriangle(ilu).Multiply(GetUpperTriangle(ilu)); + + for (var i = 0; i < sparseMatrix.RowCount; i++) + { + for (var j = 0; j < sparseMatrix.ColumnCount; j++) + { + Assert.IsTrue((sparseMatrix[i, j]).AlmostEqual(original[i, j]), "#01-" + i + "-" + j); + } + } + } + } +} diff --git a/src/UnitTests/LinearAlgebraTests/Single/Solvers/Preconditioners/PreConditionerTest.cs b/src/UnitTests/LinearAlgebraTests/Single/Solvers/Preconditioners/PreConditionerTest.cs new file mode 100644 index 00000000..3bfebc43 --- /dev/null +++ b/src/UnitTests/LinearAlgebraTests/Single/Solvers/Preconditioners/PreConditionerTest.cs @@ -0,0 +1,127 @@ +namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single.Solvers.Preconditioners +{ + using LinearAlgebra.Single; + using LinearAlgebra.Generic; + using LinearAlgebra.Generic.Solvers.Preconditioners; + using MbUnit.Framework; + + public abstract class PreconditionerTest + { + protected const double Epsilon = 1e-10; + + internal SparseMatrix CreateUnitMatrix(int size) + { + var matrix = new SparseMatrix(size); + for (var i = 0; i < size; i++) + { + matrix[i, i] = 2; + } + + return matrix; + } + + protected Vector CreateStandardBcVector(int size) + { + Vector vector = new DenseVector(size); + for (var i = 0; i < size; i++) + { + vector[i] = i + 1; + } + + return vector; + } + + internal abstract IPreConditioner CreatePreconditioner(); + + protected abstract void CheckResult(IPreConditioner preconditioner, SparseMatrix matrix, Vector vector, Vector result); + + [Test] + [MultipleAsserts] + public void ApproximateWithUnitMatrixReturningNewVector() + { + const int Size = 10; + + var newMatrix = CreateUnitMatrix(Size); + var vector = CreateStandardBcVector(Size); + + var preconditioner = CreatePreconditioner(); + preconditioner.Initialize(newMatrix); + + var result = preconditioner.Approximate(vector); + + CheckResult(preconditioner, newMatrix, vector, result); + } + + [Test] + [MultipleAsserts] + public void ApproximateReturningOldVector() + { + const int Size = 10; + var newMatrix = CreateUnitMatrix(Size); + var vector = CreateStandardBcVector(Size); + + var preconditioner = CreatePreconditioner(); + preconditioner.Initialize(newMatrix); + + Vector result = new DenseVector(vector.Count); + preconditioner.Approximate(vector, result); + + CheckResult(preconditioner, newMatrix, vector, result); + } + + [Test] + [ExpectedArgumentException] + public void ApproximateWithVectorWithIncorrectLength() + { + const int Size = 10; + var newMatrix = CreateUnitMatrix(Size); + var vector = CreateStandardBcVector(Size); + + var preconditioner = CreatePreconditioner(); + preconditioner.Initialize(newMatrix); + + Vector result = new DenseVector(vector.Count + 10); + preconditioner.Approximate(vector, result); + } + + [Test] + [ExpectedArgumentNullException] + public void ApproximateWithNullVector() + { + const int Size = 10; + var newMatrix = CreateUnitMatrix(Size); + var vector = CreateStandardBcVector(Size); + + var preconditioner = CreatePreconditioner(); + preconditioner.Initialize(newMatrix); + + Vector result = new DenseVector(vector.Count + 10); + preconditioner.Approximate(null, result); + } + + [Test] + [ExpectedArgumentNullException] + public void ApproximateWithNullResultVector() + { + const int Size = 10; + var newMatrix = CreateUnitMatrix(Size); + var vector = CreateStandardBcVector(Size); + + var preconditioner = CreatePreconditioner(); + preconditioner.Initialize(newMatrix); + + Vector result = null; + preconditioner.Approximate(vector, result); + } + + [Test] + [ExpectedArgumentException] + public void ApproximateWithNonInitializedPreconditioner() + { + const int Size = 10; + var vector = CreateStandardBcVector(Size); + var preconditioner = CreatePreconditioner(); + preconditioner.Approximate(vector); + } + } +} \ No newline at end of file diff --git a/src/UnitTests/LinearAlgebraTests/Single/Solvers/Preconditioners/UnitPreconditionerTest.cs b/src/UnitTests/LinearAlgebraTests/Single/Solvers/Preconditioners/UnitPreconditionerTest.cs new file mode 100644 index 00000000..560c5862 --- /dev/null +++ b/src/UnitTests/LinearAlgebraTests/Single/Solvers/Preconditioners/UnitPreconditionerTest.cs @@ -0,0 +1,29 @@ +namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single.Solvers.Preconditioners +{ + using LinearAlgebra.Single; + using LinearAlgebra.Single.Solvers.Preconditioners; + using LinearAlgebra.Generic; + using LinearAlgebra.Generic.Solvers.Preconditioners; + using MbUnit.Framework; + + [TestFixture] + public sealed class UnitPreconditionerTest : PreconditionerTest + { + internal override IPreConditioner CreatePreconditioner() + { + return new UnitPreconditioner(); + } + + protected override void CheckResult(IPreConditioner preconditioner, SparseMatrix matrix, Vector vector, Vector result) + { + Assert.AreEqual(typeof(UnitPreconditioner), preconditioner.GetType(), "#01"); + + // Unit preconditioner is doing nothing. Vector and result should be equal + + for (var i = 0; i < vector.Count; i++) + { + Assert.IsTrue(vector[i] == result[i], "#02-" + i); + } + } + } +} diff --git a/src/UnitTests/LinearAlgebraTests/Single/Solvers/StopCriterium/DivergenceStopCriteriumTest.cs b/src/UnitTests/LinearAlgebraTests/Single/Solvers/StopCriterium/DivergenceStopCriteriumTest.cs new file mode 100644 index 00000000..1961038e --- /dev/null +++ b/src/UnitTests/LinearAlgebraTests/Single/Solvers/StopCriterium/DivergenceStopCriteriumTest.cs @@ -0,0 +1,233 @@ +namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single.Solvers.StopCriterium +{ + using LinearAlgebra.Single; + using LinearAlgebra.Single.Solvers.StopCriterium; + using LinearAlgebra.Generic.Solvers.Status; + using MbUnit.Framework; + + [TestFixture] + public sealed class DivergenceStopCriteriumTest + { + [Test] + [ExpectedArgumentOutOfRangeException] + public void CreateWithNegativeMaximumIncrease() + { + new DivergenceStopCriterium(-0.1); + } + + [Test] + [ExpectedArgumentOutOfRangeException] + public void CreateWithIllegalMinimumIterations() + { + new DivergenceStopCriterium(2); + } + + [Test] + [MultipleAsserts] + public void Create() + { + var criterium = new DivergenceStopCriterium(0.1, 3); + Assert.IsNotNull(criterium, "There should be a criterium"); + + Assert.AreEqual(0.1, criterium.MaximumRelativeIncrease, "Incorrect maximum"); + Assert.AreEqual(3, criterium.MinimumNumberOfIterations, "Incorrect iteration count"); + } + + [Test] + [MultipleAsserts] + public void ResetMaximumIncrease() + { + var criterium = new DivergenceStopCriterium(0.5, 3); + Assert.IsNotNull(criterium, "There should be a criterium"); + + Assert.AreEqual(0.5, criterium.MaximumRelativeIncrease, "Incorrect maximum"); + + criterium.ResetMaximumRelativeIncreaseToDefault(); + Assert.AreEqual(DivergenceStopCriterium.DefaultMaximumRelativeIncrease, criterium.MaximumRelativeIncrease, "Incorrect value"); + } + + [Test] + [MultipleAsserts] + public void ResetMinimumIterationsBelowMaximum() + { + var criterium = new DivergenceStopCriterium(0.5, 15); + Assert.IsNotNull(criterium, "There should be a criterium"); + + Assert.AreEqual(15, criterium.MinimumNumberOfIterations, "Incorrect iteration count"); + + criterium.ResetNumberOfIterationsToDefault(); + Assert.AreEqual(DivergenceStopCriterium.DefaultMinimumNumberOfIterations, criterium.MinimumNumberOfIterations, "Incorrect value"); + } + + [Test] + [ExpectedArgumentOutOfRangeException] + public void DetermineStatusWithIllegalIterationNumber() + { + var criterium = new DivergenceStopCriterium(0.5, 15); + criterium.DetermineStatus(-1, + new DenseVector(3, 4), + new DenseVector(3, 5), + new DenseVector(3, 6)); + } + + [Test] + [ExpectedArgumentNullException] + public void DetermineStatusWithNullResidualVector() + { + var criterium = new DivergenceStopCriterium(0.5, 15); + criterium.DetermineStatus(1, + new DenseVector(3, 4), + new DenseVector(3, 5), + null); + } + + [Test] + [MultipleAsserts] + public void DetermineStatusWithTooFewIterations() + { + const float Increase = 0.5f; + const int Iterations = 10; + + var criterium = new DivergenceStopCriterium(Increase, Iterations); + + // Add residuals. We should not diverge because we'll have to few iterations + for (var i = 0; i < Iterations - 1; i++) + { + criterium.DetermineStatus(i, + new DenseVector(new [] { 1.0f }), + new DenseVector(new [] { 1.0f }), + new DenseVector(new [] { (i + 1) * (Increase + 0.1f) })); + + Assert.IsInstanceOfType(typeof(CalculationRunning), criterium.Status, "Status check fail."); + } + } + + [Test] + [MultipleAsserts] + public void DetermineStatusWithNoDivergence() + { + const float Increase = 0.5f; + const int Iterations = 10; + + var criterium = new DivergenceStopCriterium(Increase, Iterations); + + // Add residuals. We should not diverge because we won't have enough increase + for (var i = 0; i < Iterations * 2; i++) + { + criterium.DetermineStatus(i, + new DenseVector(new [] { 1.0f }), + new DenseVector(new [] { 1.0f }), + new DenseVector(new [] { (i + 1) * (Increase - 0.01f) })); + + Assert.IsInstanceOfType(typeof(CalculationRunning), criterium.Status, "Status check fail."); + } + } + + [Test] + [MultipleAsserts] + public void DetermineStatusWithDivergenceThroughNaN() + { + const float Increase = 0.5f; + const int Iterations = 10; + + var criterium = new DivergenceStopCriterium(Increase, Iterations); + + // Add residuals. We should not diverge because we'll have to few iterations + for (var i = 0; i < Iterations - 5; i++) + { + criterium.DetermineStatus(i, + new DenseVector(new [] { 1.0f }), + new DenseVector(new [] { 1.0f }), + new DenseVector(new [] { (i + 1) * (Increase - 0.01f) })); + + Assert.IsInstanceOfType(typeof(CalculationRunning), criterium.Status, "Status check fail."); + } + + // Now make it fail by throwing in a NaN + criterium.DetermineStatus(Iterations, + new DenseVector(new [] { 1.0f }), + new DenseVector(new [] { 1.0f }), + new DenseVector(new [] { float.NaN })); + + Assert.IsInstanceOfType(typeof(CalculationDiverged), criterium.Status, "Status check fail."); + } + + [Test] + [MultipleAsserts] + public void DetermineStatusWithDivergence() + { + const float Increase = 0.5f; + const int Iterations = 10; + + var criterium = new DivergenceStopCriterium(Increase, Iterations); + + // Add residuals. We should not diverge because we'll have one to few iterations + float previous = 1; + for (var i = 0; i < Iterations - 1; i++) + { + previous *= (1 + Increase + 0.01f); + criterium.DetermineStatus(i, + new DenseVector(new[] { 1.0f }), + new DenseVector(new[] { 1.0f }), + new DenseVector(new[] { previous })); + + Assert.IsInstanceOfType(typeof(CalculationRunning), criterium.Status, "Status check fail."); + } + + // Add the final residual. Now we should have divergence + previous *= (1 + Increase + 0.01f); + criterium.DetermineStatus(Iterations - 1, + new DenseVector(new[] { 1.0f }), + new DenseVector(new[] { 1.0f }), + new DenseVector(new[] { previous })); + + Assert.IsInstanceOfType(typeof(CalculationDiverged), criterium.Status, "Status check fail."); + } + + [Test] + [MultipleAsserts] + public void ResetCalculationState() + { + const double Increase = 0.5; + const int Iterations = 10; + + var criterium = new DivergenceStopCriterium(Increase, Iterations); + + // Add residuals. Blow it up instantly + criterium.DetermineStatus(1, + new DenseVector(new [] { 1.0f }), + new DenseVector(new [] { 1.0f }), + new DenseVector(new [] { float.NaN })); + + Assert.IsInstanceOfType(typeof(CalculationDiverged), criterium.Status, "Status check fail."); + + // Reset the state + criterium.ResetToPrecalculationState(); + + Assert.AreEqual(Increase, criterium.MaximumRelativeIncrease, "Incorrect maximum"); + Assert.AreEqual(Iterations, criterium.MinimumNumberOfIterations, "Incorrect iteration count"); + Assert.IsInstanceOfType(typeof(CalculationIndetermined), criterium.Status, "Status check fail."); + } + + [Test] + [MultipleAsserts] + public void Clone() + { + const double Increase = 0.5; + const int Iterations = 10; + + var criterium = new DivergenceStopCriterium(Increase, Iterations); + Assert.IsNotNull(criterium, "There should be a criterium"); + + var clone = criterium.Clone(); + Assert.IsInstanceOfType(typeof(DivergenceStopCriterium), clone, "Wrong criterium type"); + + var clonedCriterium = clone as DivergenceStopCriterium; + Assert.IsNotNull(clonedCriterium); + // ReSharper disable PossibleNullReferenceException + Assert.AreEqual(criterium.MaximumRelativeIncrease, clonedCriterium.MaximumRelativeIncrease, "Incorrect maximum"); + Assert.AreEqual(criterium.MinimumNumberOfIterations, clonedCriterium.MinimumNumberOfIterations, "Incorrect iteration count"); + // ReSharper restore PossibleNullReferenceException + } + } +} diff --git a/src/UnitTests/LinearAlgebraTests/Single/Solvers/StopCriterium/FailureStopCriteriumTest.cs b/src/UnitTests/LinearAlgebraTests/Single/Solvers/StopCriterium/FailureStopCriteriumTest.cs new file mode 100644 index 00000000..a411fe2f --- /dev/null +++ b/src/UnitTests/LinearAlgebraTests/Single/Solvers/StopCriterium/FailureStopCriteriumTest.cs @@ -0,0 +1,133 @@ +namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single.Solvers.StopCriterium +{ + using LinearAlgebra.Single; + using LinearAlgebra.Single.Solvers.StopCriterium; + using LinearAlgebra.Generic.Solvers.Status; + using MbUnit.Framework; + + [TestFixture] + public sealed class FailureStopCriteriumTest + { + [Test] + [MultipleAsserts] + public void Create() + { + var criterium = new FailureStopCriterium(); + Assert.IsNotNull(criterium, "Should have a criterium now"); + } + + [Test] + [ExpectedArgumentOutOfRangeException] + public void DetermineStatusWithIllegalIterationNumber() + { + var criterium = new FailureStopCriterium(); + Assert.IsNotNull(criterium, "There should be a criterium"); + + criterium.DetermineStatus(-1, new DenseVector(3, 4), new DenseVector(3, 5), new DenseVector(3, 6)); + } + + [Test] + [ExpectedArgumentNullException] + public void DetermineStatusWithNullSolutionVector() + { + var criterium = new FailureStopCriterium(); + Assert.IsNotNull(criterium, "There should be a criterium"); + + criterium.DetermineStatus(1, null, new DenseVector(3, 6), new DenseVector(4, 4)); + } + + [Test] + [ExpectedArgumentNullException] + public void DetermineStatusWithNullResidualVector() + { + var criterium = new FailureStopCriterium(); + Assert.IsNotNull(criterium, "There should be a criterium"); + + criterium.DetermineStatus(1, new DenseVector(3, 4), new DenseVector(3, 6), null); + } + + [Test] + [ExpectedArgumentException] + public void DetermineStatusWithNonMatchingVectors() + { + var criterium = new FailureStopCriterium(); + Assert.IsNotNull(criterium, "There should be a criterium"); + + criterium.DetermineStatus(1, new DenseVector(3, 4), new DenseVector(3, 6), new DenseVector(4, 4)); + } + + [Test] + [MultipleAsserts] + public void DetermineStatusWithResidualNaN() + { + var criterium = new FailureStopCriterium(); + Assert.IsNotNull(criterium, "There should be a criterium"); + + var solution = new DenseVector(new [] { 1.0f, 1.0f, 2.0f }); + var source = new DenseVector(new [] { 1001.0f, 0, 2003.0f }); + var residual = new DenseVector(new [] { 1000, float.NaN, 2001 }); + + criterium.DetermineStatus(5, solution, source, residual); + Assert.IsInstanceOfType(typeof(CalculationFailure), criterium.Status, "Should be failed"); + } + + [Test] + [MultipleAsserts] + public void DetermineStatusWithSolutionNaN() + { + var criterium = new FailureStopCriterium(); + Assert.IsNotNull(criterium, "There should be a criterium"); + + var solution = new DenseVector(new[] { 1, 1, float.NaN }); + var source = new DenseVector(new[] { 1001.0f, 0.0f, 2003.0f }); + var residual = new DenseVector(new[] { 1000.0f, 1000.0f, 2001.0f }); + + criterium.DetermineStatus(5, solution, source, residual); + Assert.IsInstanceOfType(typeof(CalculationFailure), criterium.Status, "Should be failed"); + } + + [Test] + [MultipleAsserts] + public void DetermineStatus() + { + var criterium = new FailureStopCriterium(); + Assert.IsNotNull(criterium, "There should be a criterium"); + + var solution = new DenseVector(new[] { 3.0f, 2.0f, 1.0f }); + var source = new DenseVector(new[] { 1001.0f, 0.0f, 2003.0f }); + var residual = new DenseVector(new[] { 1.0f, 2.0f, 3.0f }); + + criterium.DetermineStatus(5, solution, source, residual); + Assert.IsInstanceOfType(typeof(CalculationRunning), criterium.Status, "Should be running"); + } + + [Test] + [MultipleAsserts] + public void ResetCalculationState() + { + var criterium = new FailureStopCriterium(); + Assert.IsNotNull(criterium, "There should be a criterium"); + + var solution = new DenseVector(new[] { 1.0f, 1.0f, 2.0f }); + var source = new DenseVector(new[] { 1001.0f, 0.0f, 2003.0f }); + var residual = new DenseVector(new[] { 1000.0f, 1000.0f, 2001.0f }); + + criterium.DetermineStatus(5, solution, source, residual); + Assert.IsInstanceOfType(typeof(CalculationRunning), criterium.Status, "Should be running"); + + criterium.ResetToPrecalculationState(); + Assert.IsInstanceOfType(typeof(CalculationIndetermined), criterium.Status, "Should not have started"); + } + + [Test] + [MultipleAsserts] + public void Clone() + { + var criterium = new FailureStopCriterium(); + Assert.IsNotNull(criterium, "There should be a criterium"); + + var clone = criterium.Clone(); + Assert.IsInstanceOfType(typeof(FailureStopCriterium), clone, "Wrong criterium type"); + } + } +} diff --git a/src/UnitTests/LinearAlgebraTests/Single/Solvers/StopCriterium/IterationCountStopCriteriumTest.cs b/src/UnitTests/LinearAlgebraTests/Single/Solvers/StopCriterium/IterationCountStopCriteriumTest.cs new file mode 100644 index 00000000..397a3c18 --- /dev/null +++ b/src/UnitTests/LinearAlgebraTests/Single/Solvers/StopCriterium/IterationCountStopCriteriumTest.cs @@ -0,0 +1,96 @@ +namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single.Solvers.StopCriterium +{ + using LinearAlgebra.Single; + using LinearAlgebra.Single.Solvers.StopCriterium; + using LinearAlgebra.Generic.Solvers.Status; + using MbUnit.Framework; + + [TestFixture] + public sealed class IterationCountStopCriteriumTest + { + [Test] + [ExpectedArgumentOutOfRangeException] + public void CreateWithIllegalMinimumIterations() + { + new IterationCountStopCriterium(-1); + } + + [Test] + [MultipleAsserts] + public void Create() + { + var criterium = new IterationCountStopCriterium(10); + Assert.IsNotNull(criterium, "A criterium should have been created"); + } + + [Test] + [MultipleAsserts] + public void ResetMaximumIterations() + { + var criterium = new IterationCountStopCriterium(10); + Assert.IsNotNull(criterium, "A criterium should have been created"); + Assert.AreEqual(10, criterium.MaximumNumberOfIterations, "Incorrect maximum number of iterations"); + + criterium.ResetMaximumNumberOfIterationsToDefault(); + Assert.AreNotEqual(10, criterium.MaximumNumberOfIterations, "Should have reset"); + Assert.AreEqual(IterationCountStopCriterium.DefaultMaximumNumberOfIterations, criterium.MaximumNumberOfIterations, "Reset to the wrong value"); + } + + [Test] + [ExpectedArgumentOutOfRangeException] + public void DetermineStatusWithIllegalIterationNumber() + { + var criterium = new IterationCountStopCriterium(10); + Assert.IsNotNull(criterium, "A criterium should have been created"); + + criterium.DetermineStatus(-1, new DenseVector(3, 1), new DenseVector(3, 2), new DenseVector(3,3)); + Assert.Fail(); + } + + [Test] + [MultipleAsserts] + public void DetermineStatus() + { + var criterium = new IterationCountStopCriterium(10); + Assert.IsNotNull(criterium, "A criterium should have been created"); + + criterium.DetermineStatus(5, new DenseVector(3, 1), new DenseVector(3, 2), new DenseVector(3, 3)); + Assert.IsInstanceOfType(typeof(CalculationRunning), criterium.Status, "Should be running"); + + criterium.DetermineStatus(10, new DenseVector(3, 1), new DenseVector(3, 2), new DenseVector(3, 3)); + Assert.IsInstanceOfType(typeof(CalculationStoppedWithoutConvergence), criterium.Status, "Should be finished"); + } + + [Test] + [MultipleAsserts] + public void ResetCalculationState() + { + var criterium = new IterationCountStopCriterium(10); + Assert.IsNotNull(criterium, "A criterium should have been created"); + + criterium.DetermineStatus(5, new DenseVector(3, 1), new DenseVector(3, 2), new DenseVector(3, 3)); + Assert.IsInstanceOfType(typeof(CalculationRunning), criterium.Status, "Should be running"); + + criterium.ResetToPrecalculationState(); + Assert.IsInstanceOfType(typeof(CalculationIndetermined), criterium.Status, "Should not have started"); + } + + [Test] + [MultipleAsserts] + public void Clone() + { + var criterium = new IterationCountStopCriterium(10); + Assert.IsNotNull(criterium, "A criterium should have been created"); + Assert.AreEqual(10, criterium.MaximumNumberOfIterations, "Incorrect maximum"); + + var clone = criterium.Clone(); + Assert.IsInstanceOfType(typeof(IterationCountStopCriterium), clone, "Wrong criterium type"); + + var clonedCriterium = clone as IterationCountStopCriterium; + Assert.IsNotNull(clonedCriterium); + // ReSharper disable PossibleNullReferenceException + Assert.AreEqual(criterium.MaximumNumberOfIterations, clonedCriterium.MaximumNumberOfIterations, "Clone failed"); + // ReSharper restore PossibleNullReferenceException + } + } +} diff --git a/src/UnitTests/LinearAlgebraTests/Single/Solvers/StopCriterium/ResidualStopCriteriumTest.cs b/src/UnitTests/LinearAlgebraTests/Single/Solvers/StopCriterium/ResidualStopCriteriumTest.cs new file mode 100644 index 00000000..bacdda85 --- /dev/null +++ b/src/UnitTests/LinearAlgebraTests/Single/Solvers/StopCriterium/ResidualStopCriteriumTest.cs @@ -0,0 +1,261 @@ +namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single.Solvers.StopCriterium +{ + using LinearAlgebra.Single; + using LinearAlgebra.Single.Solvers.StopCriterium; + using LinearAlgebra.Generic.Solvers.Status; + using MbUnit.Framework; + + [TestFixture] + public sealed class ResidualStopCriteriumTest + { + [Test] + [ExpectedArgumentOutOfRangeException] + public void CreateWithNegativeMaximum() + { + new ResidualStopCriterium(-0.1); + Assert.Fail(); + } + + [Test] + [ExpectedArgumentOutOfRangeException] + public void CreateWithIllegalMinimumIterations() + { + new ResidualStopCriterium(-1); + Assert.Fail(); + } + + [Test] + [MultipleAsserts] + public void Create() + { + var criterium = new ResidualStopCriterium(1e-8, 50); + Assert.IsNotNull(criterium, "There should be a criterium"); + + Assert.AreEqual(1e-8, criterium.Maximum, "Incorrect maximum"); + Assert.AreEqual(50, criterium.MinimumIterationsBelowMaximum, "Incorrect iteration count"); + } + + [Test] + [MultipleAsserts] + public void ResetMaximum() + { + var criterium = new ResidualStopCriterium(1e-8, 50); + Assert.IsNotNull(criterium, "There should be a criterium"); + + criterium.ResetMaximumResidualToDefault(); + Assert.AreEqual(ResidualStopCriterium.DefaultMaximumResidual, criterium.Maximum, "Incorrect maximum"); + } + + [Test] + [MultipleAsserts] + public void ResetMinimumIterationsBelowMaximum() + { + var criterium = new ResidualStopCriterium(1e-8, 50); + Assert.IsNotNull(criterium, "There should be a criterium"); + + criterium.ResetMinimumIterationsBelowMaximumToDefault(); + Assert.AreEqual(ResidualStopCriterium.DefaultMinimumIterationsBelowMaximum, criterium.MinimumIterationsBelowMaximum, "Incorrect iteration count"); + } + + [Test] + [ExpectedArgumentOutOfRangeException] + public void DetermineStatusWithIllegalIterationNumber() + { + var criterium = new ResidualStopCriterium(1e-8, 50); + Assert.IsNotNull(criterium, "There should be a criterium"); + + criterium.DetermineStatus(-1, + new DenseVector(3, 4), + new DenseVector(3, 5), + new DenseVector(3, 6)); + Assert.Fail(); + } + + [Test] + [ExpectedArgumentNullException] + public void DetermineStatusWithNullSolutionVector() + { + var criterium = new ResidualStopCriterium(1e-8, 50); + Assert.IsNotNull(criterium, "There should be a criterium"); + + criterium.DetermineStatus(1, + null, + new DenseVector(3, 5), + new DenseVector(3, 6)); + Assert.Fail(); + } + + [Test] + [ExpectedArgumentNullException] + public void DetermineStatusWithNullSourceVector() + { + var criterium = new ResidualStopCriterium(1e-8, 50); + Assert.IsNotNull(criterium, "There should be a criterium"); + + criterium.DetermineStatus(1, + new DenseVector(3, 4), + null, + new DenseVector(3, 6)); + Assert.Fail(); + } + + [Test] + [ExpectedArgumentNullException] + public void DetermineStatusWithNullResidualVector() + { + var criterium = new ResidualStopCriterium(1e-8, 50); + Assert.IsNotNull(criterium, "There should be a criterium"); + + criterium.DetermineStatus(1, + new DenseVector(3, 4), + new DenseVector(3, 5), + null); + } + + [Test] + [ExpectedArgumentException] + public void DetermineStatusWithNonMatchingSolutionVector() + { + var criterium = new ResidualStopCriterium(1e-8, 50); + Assert.IsNotNull(criterium, "There should be a criterium"); + + criterium.DetermineStatus(1, + new DenseVector(4, 4), + new DenseVector(3, 4), + new DenseVector(3, 4)); + } + + [Test] + [ExpectedArgumentException] + public void DetermineStatusWithNonMatchingSourceVector() + { + var criterium = new ResidualStopCriterium(1e-8, 50); + Assert.IsNotNull(criterium, "There should be a criterium"); + + criterium.DetermineStatus(1, + new DenseVector(3, 4), + new DenseVector(4, 4), + new DenseVector(3, 4)); + } + + [Test] + [ExpectedArgumentException] + public void DetermineStatusWithNonMatchingResidualVector() + { + var criterium = new ResidualStopCriterium(1e-8, 50); + Assert.IsNotNull(criterium, "There should be a criterium"); + + criterium.DetermineStatus(1, + new DenseVector(3, 4), + new DenseVector(3, 4), + new DenseVector(4, 4)); + } + + [Test] + [MultipleAsserts] + public void DetermineStatusWithSourceNaN() + { + var criterium = new ResidualStopCriterium(1e-3, 10); + Assert.IsNotNull(criterium, "There should be a criterium"); + + var solution = new DenseVector(new[] { 1.0f, 1.0f, 2.0f }); + var source = new DenseVector(new[] { 1.0f, 1.0f, float.NaN }); + var residual = new DenseVector(new[] { 1000.0f, 1000.0f, 2001.0f }); + + criterium.DetermineStatus(5, solution, source, residual); + Assert.IsInstanceOfType(typeof(CalculationDiverged), criterium.Status, "Should be diverged"); + } + + [Test] + [MultipleAsserts] + public void DetermineStatusWithResidualNaN() + { + var criterium = new ResidualStopCriterium(1e-3, 10); + Assert.IsNotNull(criterium, "There should be a criterium"); + + var solution = new DenseVector(new[] { 1.0f, 1.0f, 2.0f }); + var source = new DenseVector(new[] { 1.0f, 1.0f, 2.0f }); + var residual = new DenseVector(new[] { 1000.0f, float.NaN, 2001.0f }); + + criterium.DetermineStatus(5, solution, source, residual); + Assert.IsInstanceOfType(typeof(CalculationDiverged), criterium.Status, "Should be diverged"); + } + + // Bugfix: The unit tests for the BiCgStab solver run with a super simple matrix equation + // which converges at the first iteration. The default settings for the + // residual stop criterium should be able to handle this. + [Test] + [MultipleAsserts] + public void DetermineStatusWithConvergenceAtFirstIteration() + { + var criterium = new ResidualStopCriterium(); + Assert.IsNotNull(criterium, "There should be a criterium"); + + var solution = new DenseVector(new[] { 1.0f, 1.0f, 1.0f }); + var source = new DenseVector(new[] { 1.0f, 1.0f, 1.0f }); + var residual = new DenseVector(new[] { 0.0f, 0.0f, 0.0f }); + + criterium.DetermineStatus(0, solution, source, residual); + Assert.IsInstanceOfType(typeof(CalculationConverged), criterium.Status, "Should be done"); + } + + [Test] + [MultipleAsserts] + public void DetermineStatus() + { + var criterium = new ResidualStopCriterium(1e-3, 10); + Assert.IsNotNull(criterium, "There should be a criterium"); + + // Note that the solution vector isn't actually being used so ... + var solution = new DenseVector(new[] { float.NaN, float.NaN, float.NaN }); + + // Set the source values + var source = new DenseVector(new[] { 1.000f, 1.000f, 2.001f }); + + // Set the residual values + var residual = new DenseVector(new[] { 0.001f, 0.001f, 0.002f }); + + criterium.DetermineStatus(5, solution, source, residual); + Assert.IsInstanceOfType(typeof(CalculationRunning), criterium.Status, "Should still be running"); + + criterium.DetermineStatus(16, solution, source, residual); + Assert.IsInstanceOfType(typeof(CalculationConverged), criterium.Status, "Should be done"); + } + + [Test] + [MultipleAsserts] + public void ResetCalculationState() + { + var criterium = new ResidualStopCriterium(1e-3, 10); + Assert.IsNotNull(criterium, "There should be a criterium"); + + var solution = new DenseVector(new[] { 0.001f, 0.001f, 0.002f }); + var source = new DenseVector(new[] { 0.001f, 0.001f, 0.002f }); + var residual = new DenseVector(new[] { 1.000f, 1.000f, 2.001f }); + + criterium.DetermineStatus(5, solution, source, residual); + Assert.IsInstanceOfType(typeof(CalculationRunning), criterium.Status, "Should be running"); + + criterium.ResetToPrecalculationState(); + Assert.IsInstanceOfType(typeof(CalculationIndetermined), criterium.Status, "Should not have started"); + } + + [Test] + [MultipleAsserts] + public void Clone() + { + var criterium = new ResidualStopCriterium(1e-3, 10); + Assert.IsNotNull(criterium, "There should be a criterium"); + + var clone = criterium.Clone(); + Assert.IsInstanceOfType(typeof(ResidualStopCriterium), clone, "Wrong criterium type"); + + var clonedCriterium = clone as ResidualStopCriterium; + Assert.IsNotNull(clonedCriterium); + // ReSharper disable PossibleNullReferenceException + Assert.AreEqual(criterium.Maximum, clonedCriterium.Maximum, "Clone failed"); + Assert.AreEqual(criterium.MinimumIterationsBelowMaximum, clonedCriterium.MinimumIterationsBelowMaximum, "Clone failed"); + // ReSharper restore PossibleNullReferenceException + } + } +} diff --git a/src/UnitTests/LinearAlgebraTests/Single/SparseMatrixTests.cs b/src/UnitTests/LinearAlgebraTests/Single/SparseMatrixTests.cs new file mode 100644 index 00000000..8dde4c4b --- /dev/null +++ b/src/UnitTests/LinearAlgebraTests/Single/SparseMatrixTests.cs @@ -0,0 +1,173 @@ +// +// 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. +// + +namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single +{ + using System.Collections.Generic; + using LinearAlgebra.Generic; + using MbUnit.Framework; + using LinearAlgebra.Single; + + public class SparseMatrixTests : MatrixTests + { + protected override Matrix CreateMatrix(int rows, int columns) + { + return new SparseMatrix(rows, columns); + } + + protected override Matrix CreateMatrix(float[,] data) + { + return new SparseMatrix(data); + } + + protected override Vector CreateVector(int size) + { + return new SparseVector(size); + } + + protected override Vector CreateVector(float[] data) + { + return new SparseVector(data); + } + + [Test] + public void CanCreateMatrixFrom1DArray() + { + var testData = new Dictionary> + { + { "Singular3x3", new SparseMatrix(3, 3, new float[] { 1, 1, 1, 1, 1, 1, 2, 2, 2 }) }, + { "Square3x3", new SparseMatrix(3, 3, new[] { -1.1f, 0.0f, -4.4f, -2.2f, 1.1f, 5.5f, -3.3f, 2.2f, 6.6f }) }, + { "Square4x4", new SparseMatrix(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 SparseMatrix(3, 2, new[] { -1.1f, 0.0f, -4.4f, -2.2f, 1.1f, 5.5f }) }, + { "Wide2x3", new SparseMatrix(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, Ignore] + public void MatrixFrom1DArrayIsReference() + { + // Sparse Matrix copies values from float[], but no remember reference. + var data = new float[] { 1, 1, 1, 1, 1, 1, 2, 2, 2 }; + var matrix = new SparseMatrix(3, 3, data); + matrix[0, 0] = 10.0f; + Assert.AreEqual(10.0, data[0]); + } + + [Test] + public void MatrixFrom2DArrayIsCopy() + { + var matrix = new SparseMatrix(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 SparseMatrix(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 SparseMatrix(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 = SparseMatrix.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) + { + SparseMatrix.Identity(order); + } + + [Test] + public void CanCreateHeavySparseMatrix() + { + var matrix = new SparseMatrix(500, 1000); + var nonzero = 0; + var rnd = new System.Random(); + + for (int i = 0; i < matrix.RowCount; i++) + { + for (int j = 0; j < matrix.ColumnCount; j++ ) + { + var value = rnd.Next(10) * rnd.Next(10) * rnd.Next(10) * rnd.Next(10) * rnd.Next(10); + if (value != 0) + { + nonzero++; + } + matrix[i, j] = value; + } + } + + Assert.AreEqual(matrix.NonZerosCount, nonzero); + } + } +} diff --git a/src/UnitTests/LinearAlgebraTests/Single/SparseVectorTest.TextHandling.cs b/src/UnitTests/LinearAlgebraTests/Single/SparseVectorTest.TextHandling.cs new file mode 100644 index 00000000..58ba3d6a --- /dev/null +++ b/src/UnitTests/LinearAlgebraTests/Single/SparseVectorTest.TextHandling.cs @@ -0,0 +1,132 @@ +// +// 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. +// + +namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single +{ + using System; + using System.Globalization; + using LinearAlgebra.Single; + using MbUnit.Framework; + + public class SparseVectorTextHandlingTest + { + [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 CanParseDoubleSparseVectorsWithInvariant(string stringToParse, string expectedToString) + { + var formatProvider = CultureInfo.InvariantCulture; + SparseVector vector = SparseVector.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 CanParseDoubleSparseVectorsWithCulture(string stringToParse, string expectedToString, string culture) + { + var formatProvider = CultureInfo.GetCultureInfo(culture); + SparseVector vector = SparseVector.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 CanParseDoubleSparseVectors(string vectorAsString) + { + var mappedString = String.Format( + vectorAsString, + CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator, + CultureInfo.CurrentCulture.TextInfo.ListSeparator); + + SparseVector vector = SparseVector.Parse(mappedString); + + Assert.AreEqual(mappedString, vector.ToString()); + } + + [Test] + [MultipleAsserts] + public void ParseThrowsFormatExceptionIfMissingClosingParen() + { + Assert.Throws(() => SparseVector.Parse("(1")); + Assert.Throws(() => SparseVector.Parse("[1")); + } + + [Test] + public void CanTryParseDoubleSparseVector() + { + var data = new[] { 1.2f, 3.4f, 5.6e-10f }; + var text = String.Format( + "{1}{0}{2}{0}{3}", + CultureInfo.CurrentCulture.TextInfo.ListSeparator, + data[0], + data[1], + data[2]); + + SparseVector vector; + var ret = SparseVector.TryParse(text, out vector); + Assert.IsTrue(ret); + AssertHelpers.AlmostEqualList(data, vector.ToArray(), 1e-15); + + ret = SparseVector.TryParse(text, CultureInfo.CurrentCulture, out vector); + Assert.IsTrue(ret); + AssertHelpers.AlmostEqualList(data, vector.ToArray(), 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) + { + SparseVector vector; + var ret = SparseVector.TryParse(str, CultureInfo.InvariantCulture, out vector); + Assert.IsFalse(ret); + Assert.IsNull(vector); + } + } +} diff --git a/src/UnitTests/LinearAlgebraTests/Single/SparseVectorTest.cs b/src/UnitTests/LinearAlgebraTests/Single/SparseVectorTest.cs new file mode 100644 index 00000000..e9bcf74d --- /dev/null +++ b/src/UnitTests/LinearAlgebraTests/Single/SparseVectorTest.cs @@ -0,0 +1,414 @@ +// +// 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. +// + +namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single +{ + using System; + using System.Collections.Generic; + using LinearAlgebra.Generic; + using MbUnit.Framework; + using LinearAlgebra.Single; + + public class SparseVectorTest : VectorTests + { + protected override Vector CreateVector(int size) + { + return new SparseVector(size); + } + + protected override Vector CreateVector(IList data) + { + var vector = new SparseVector(data.Count); + for (var index = 0; index < data.Count; index++) + { + vector[index] = data[index]; + } + return vector; + } + + #region Test similar to DenseVector + [Test] + [MultipleAsserts] + public void CanCreateSparseVectorFromArray() + { + var data = new float[Data.Length]; + Array.Copy(Data, data, Data.Length); + var vector = new SparseVector(data); + + for (var i = 0; i < data.Length; i++) + { + Assert.AreEqual(data[i], vector[i]); + } + } + + [Test] + [MultipleAsserts] + public void CanCreateSparseVectorFromAnotherSparseVector() + { + var vector = new SparseVector(Data); + var other = new SparseVector(vector); + + Assert.AreNotSame(vector, other); + for (var i = 0; i < Data.Length; i++) + { + Assert.AreEqual(vector[i], other[i]); + } + } + + [Test] + [MultipleAsserts] + public void CanCreateSparseVectorFromAnotherVector() + { + var vector = (Vector)new SparseVector(Data); + var other = new SparseVector(vector); + + Assert.AreNotSame(vector, other); + for (var i = 0; i < Data.Length; i++) + { + Assert.AreEqual(vector[i], other[i]); + } + } + + [Test] + [MultipleAsserts] + public void CanCreateSparseVectorFromUserDefinedVector() + { + var vector = new UserDefinedVector(Data); + var other = new SparseVector(vector); + + for (var i = 0; i < Data.Length; i++) + { + Assert.AreEqual(vector[i], other[i]); + } + } + + [Test] + public void CanCreateSparseVectorWithConstantValues() + { + var vector = new SparseVector(5, 5); + Assert.ForAll(vector, value => value == 5); + } + + [Test] + [MultipleAsserts] + public void CanCreateSparseMatrix() + { + var vector = new SparseVector(3); + var matrix = vector.CreateMatrix(2, 3); + Assert.AreEqual(2, matrix.RowCount); + Assert.AreEqual(3, matrix.ColumnCount); + } + + + [Test] + [MultipleAsserts] + public void CanConvertSparseVectorToArray() + { + var vector = new SparseVector(Data); + var array = vector.ToArray(); + Assert.IsInstanceOfType(typeof(float[]), array); + Assert.AreElementsEqual(vector, array); + } + + [Test] + [MultipleAsserts] + public void CanConvertArrayToSparseVector() + { + var array = new[] { 0.0f, 1.0f, 2.0f, 3.0f, 4.0f }; + var vector = new SparseVector(array); + Assert.IsInstanceOfType(typeof(SparseVector), vector); + Assert.AreElementsEqual(array, array); + } + + [Test] + public void CanCallUnaryPlusOperatorOnSparseVector() + { + var vector = new SparseVector(Data); + var other = +vector; + for (var i = 0; i < Data.Length; i++) + { + Assert.AreEqual(vector[i], other[i]); + } + } + + [Test] + [MultipleAsserts] + public void CanAddTwoSparseVectorsUsingOperator() + { + var vector = new SparseVector(Data); + var other = new SparseVector(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 CanCallUnaryNegationOperatorOnSparseVector() + { + var vector = new SparseVector(Data); + var other = -vector; + for (var i = 0; i < Data.Length; i++) + { + Assert.AreEqual(-Data[i], other[i]); + } + } + + [Test] + [MultipleAsserts] + public void CanSubtractTwoSparseVectorsUsingOperator() + { + var vector = new SparseVector(Data); + var other = new SparseVector(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 CanMultiplySparseVectorByScalarUsingOperators() + { + var vector = new SparseVector(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 SparseVector(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 CanDivideSparseVectorByScalarUsingOperators() + { + var vector = new SparseVector(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 CanCalculateOuterProductForSparseVector() + { + var vector1 = CreateVector(Data); + var vector2 = CreateVector(Data); + Matrix m = Vector.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 OuterProducForSparseVectortWithFirstParameterNullShouldThrowException() + { + SparseVector vector1 = null; + var vector2 = CreateVector(Data); + Vector.OuterProduct(vector1, vector2); + } + + [Test] + [ExpectedArgumentNullException] + public void OuterProductForSparseVectorWithSecondParameterNullShouldThrowException() + { + var vector1 = CreateVector(Data); + SparseVector vector2 = null; + Vector.OuterProduct(vector1, vector2); + } + #endregion + + [Test] + [MultipleAsserts] + public void CanCreateSparseVectorFromDenseVector() + { + var vector = (Vector)new DenseVector(Data); + var other = new SparseVector(vector); + + Assert.AreNotSame(vector, other); + for (var i = 0; i < Data.Length; i++) + { + Assert.AreEqual(vector[i], other[i]); + } + } + + [Test] + [MultipleAsserts] + public void CheckSparseMechanismBySettingValues() + { + var vector = new SparseVector(10000); + + //Add non-zero elements + vector[200] = 1.5f; + Assert.AreEqual(1.5, vector[200]); + Assert.AreEqual(1, vector.NonZerosCount); + + vector[500] = 3.5f; + Assert.AreEqual(3.5, vector[500]); + Assert.AreEqual(2, vector.NonZerosCount); + + vector[800] = 5.5f; + Assert.AreEqual(5.5, vector[800]); + Assert.AreEqual(3, vector.NonZerosCount); + + vector[0] = 7.5f; + Assert.AreEqual(7.5, vector[0]); + Assert.AreEqual(4, vector.NonZerosCount); + + //Remove non-zero elements + vector[200] = 0; + Assert.AreEqual(0, vector[200]); + Assert.AreEqual(3, vector.NonZerosCount); + + vector[500] = 0; + Assert.AreEqual(0, vector[500]); + Assert.AreEqual(2, vector.NonZerosCount); + + vector[800] = 0; + Assert.AreEqual(0, vector[800]); + Assert.AreEqual(1, vector.NonZerosCount); + + vector[0] = 0; + Assert.AreEqual(0, vector[0]); + Assert.AreEqual(0, vector.NonZerosCount); + } + + [Test] + [MultipleAsserts] + public void CheckSparseMechanismByZeroMultiply() + { + var vector = new SparseVector(10000); + + //Add non-zero elements + vector[200] = 1.5f; + vector[500] = 3.5f; + vector[800] = 5.5f; + vector[0] = 7.5f; + + //Multiply by 0 + vector *= 0; + Assert.AreEqual(0, vector[200]); + Assert.AreEqual(0, vector[500]); + Assert.AreEqual(0, vector[800]); + Assert.AreEqual(0, vector[0]); + Assert.AreEqual(0, vector.NonZerosCount); + } + + + [Test] + public void CanDotProductOfTwoSparseVectors() + { + var vectorA = new SparseVector(10000); + vectorA[200] = 1; + vectorA[500] = 3; + vectorA[800] = 5; + vectorA[100] = 7; + vectorA[900] = 9; + + var vectorB = new SparseVector(10000); + vectorB[300] = 3; + vectorB[500] = 5; + vectorB[800] = 7; + + + Assert.AreEqual(50.0, vectorA.DotProduct(vectorB)); + } + + [Test] + public void CreateHugeSparseVector() + { + var data = new float[1000000]; + var rnd = new Random(); + for (int i = 0; i < 1000000; i++) + data[i] = rnd.Next(); + + var vector = new SparseVector(data); + } + + [Test] + public void PointwiseMultiplySparseVector() + { + var zeroArray = new[] { 0.0f, 1.0f, 0.0f, 1.0f, 0.0f }; + var vector1 = new SparseVector(Data); + var vector2 = new SparseVector(zeroArray); + var result = new SparseVector(vector1.Count); + + vector1.PointwiseMultiply(vector2, result); + + for (var i = 0; i < vector1.Count; i++) + { + Assert.AreEqual(Data[i] * zeroArray[i], result[i]); + } + Assert.AreEqual(2, result.NonZerosCount); + } + } +} diff --git a/src/UnitTests/LinearAlgebraTests/Single/UserDefinedMatrixTests.cs b/src/UnitTests/LinearAlgebraTests/Single/UserDefinedMatrixTests.cs new file mode 100644 index 00000000..b98b9828 --- /dev/null +++ b/src/UnitTests/LinearAlgebraTests/Single/UserDefinedMatrixTests.cs @@ -0,0 +1,196 @@ +// +// 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. +// + +namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single +{ + using System; + using Distributions; + using LinearAlgebra.Generic; + using Properties; + using Threading; + + internal class UserDefinedMatrix : Matrix + { + private readonly float[,] _data; + + public UserDefinedMatrix(int order): base(order, order) + { + _data = new float[order, order]; + } + + public UserDefinedMatrix(int rows, int columns) : base(rows, columns) + { + _data = new float[rows, columns]; + } + + public UserDefinedMatrix(float[,] data) : base(data.GetLength(0), data.GetLength(1)) + { + _data = (float[,])data.Clone(); + } + + public override float At(int row, int column) + { + return _data[row, column]; + } + + public override void At(int row, int column, float value) + { + _data[row, column] = value; + } + + public override Matrix CreateMatrix(int numberOfRows, int numberOfColumns) + { + return new UserDefinedMatrix(numberOfRows, numberOfColumns); + } + + public override Vector CreateVector(int size) + { + return new UserDefinedVector(size); + } + + public static UserDefinedMatrix Identity(int order) + { + var m = new UserDefinedMatrix(order, order); + for (var i = 0; i < order; i++) + { + m[i, i] = 1.0f; + } + + return m; + } + + public override void Negate() + { + Multiply(-1); + } + + public override Matrix 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; + } + + public override Matrix 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; + } + + protected sealed override float AddT(float val1, float val2) + { + return val1 + val2; + } + + protected sealed override float SubtractT(float val1, float val2) + { + return val1 - val2; + } + + protected sealed override float MultiplyT(float val1, float val2) + { + return val1 * val2; + } + + protected sealed override float DivideT(float val1, float val2) + { + return val1 / val2; + } + + protected sealed override double AbsoluteT(float val1) + { + return Math.Abs(val1); + } + } + + public class UserDefinedMatrixTests : MatrixTests + { + protected override Matrix CreateMatrix(int rows, int columns) + { + return new UserDefinedMatrix(rows, columns); + } + + protected override Matrix CreateMatrix(float[,] data) + { + return new UserDefinedMatrix(data); + } + + protected override Vector CreateVector(int size) + { + return new UserDefinedVector(size); + } + + protected override Vector CreateVector(float[] data) + { + return new UserDefinedVector(data); + } + } +} diff --git a/src/UnitTests/LinearAlgebraTests/Single/UserDefinedVectorTests.cs b/src/UnitTests/LinearAlgebraTests/Single/UserDefinedVectorTests.cs new file mode 100644 index 00000000..6e8b316e --- /dev/null +++ b/src/UnitTests/LinearAlgebraTests/Single/UserDefinedVectorTests.cs @@ -0,0 +1,255 @@ +// +// 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. +// + +namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single +{ + using System; + using System.Collections.Generic; + using System.Linq; + using Distributions; + using LinearAlgebra.Generic; + using Properties; + using Threading; + + internal class UserDefinedVector : Vector + { + private readonly float[] _data; + + public UserDefinedVector(int size) + : base(size) + { + _data = new float[size]; + } + + public UserDefinedVector(float[] data) + : base(data.Length) + { + _data = (float[])data.Clone(); + } + + public override float this[int index] + { + get + { + return _data[index]; + } + + set + { + _data[index] = value; + } + } + + public override Matrix CreateMatrix(int rows, int columns) + { + return new UserDefinedMatrix(rows, columns); + } + + public override Vector CreateVector(int size) + { + return new UserDefinedVector(size); + } + + public override Vector Negate() + { + var result = new UserDefinedVector(Count); + CommonParallel.For( + 0, + _data.Length, + index => result[index] = -_data[index]); + + return result; + } + + public override Vector Random(int length, IContinuousDistribution randomDistribution) + { + if (length < 1) + { + throw new ArgumentException(Resources.ArgumentMustBePositive, "length"); + } + + var v = (UserDefinedVector)CreateVector(length); + for (var index = 0; index < v._data.Length; index++) + { + v._data[index] = (float)randomDistribution.Sample(); + } + + return v; + } + + public override Vector Random(int length, IDiscreteDistribution randomDistribution) + { + if (length < 1) + { + throw new ArgumentException(Resources.ArgumentMustBePositive, "length"); + } + + var v = (UserDefinedVector)CreateVector(length); + for (var index = 0; index < v._data.Length; index++) + { + v._data[index] = randomDistribution.Sample(); + } + + return v; + } + + public override int MinimumIndex() + { + var index = 0; + var min = _data[0]; + for (var i = 1; i < Count; i++) + { + if (min > _data[i]) + { + index = i; + min = _data[i]; + } + } + + return index; + } + + public override int MaximumIndex() + { + var index = 0; + var max = _data[0]; + for (var i = 1; i < Count; i++) + { + if (max < _data[i]) + { + index = i; + max = _data[i]; + } + } + + return index; + } + + public override double Norm(double p) + { + if (p < 0.0) + { + throw new ArgumentOutOfRangeException("p"); + } + + if (1.0 == p) + { + return CommonParallel.Aggregate( + 0, + Count, + index => Math.Abs(this[index])); + } + + if (2.0 == p) + { + return _data.Aggregate(0.0f, SpecialFunctions.Hypotenuse); + } + + if (Double.IsPositiveInfinity(p)) + { + return CommonParallel.Select( + 0, + Count, + (index, localData) => Math.Max(localData, Math.Abs(_data[index])), + Math.Max); + } + + var sum = CommonParallel.Aggregate( + 0, + Count, + index => Math.Pow(Math.Abs(_data[index]), p)); + + return Math.Pow(sum, 1.0 / p); + } + + public override Vector Normalize(double p) + { + if (p < 0.0) + { + throw new ArgumentOutOfRangeException("p"); + } + + var norm = Norm(p); + var clone = Clone(); + if (norm == 0.0) + { + return clone; + } + + clone.Multiply(1.0f / (float)norm, clone); + + return clone; + } + + protected sealed override float AddT(float val1, float val2) + { + return val1 + val2; + } + + protected sealed override float SubtractT(float val1, float val2) + { + return val1 - val2; + } + + protected sealed override float MultiplyT(float val1, float val2) + { + return val1 * val2; + } + + protected sealed override float DivideT(float val1, float val2) + { + return val1 / val2; + } + + protected sealed override double AbsoluteT(float val1) + { + return Math.Abs(val1); + } + } + + public class UserDefinedVectorTests : VectorTests + { + protected override Vector CreateVector(int size) + { + return new UserDefinedVector(size); + } + + protected override Vector CreateVector(IList data) + { + var vector = new UserDefinedVector(data.Count); + for (var index = 0; index < data.Count; index++) + { + vector[index] = data[index]; + } + + return vector; + } + } +} \ No newline at end of file diff --git a/src/UnitTests/LinearAlgebraTests/Single/VectorTests.Arithmetic.cs b/src/UnitTests/LinearAlgebraTests/Single/VectorTests.Arithmetic.cs new file mode 100644 index 00000000..4030841c --- /dev/null +++ b/src/UnitTests/LinearAlgebraTests/Single/VectorTests.Arithmetic.cs @@ -0,0 +1,901 @@ +// +// 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. +// + +namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single +{ + using System; + using LinearAlgebra.Generic; + using MbUnit.Framework; + + public abstract partial class VectorTests + { + [Test] + public void CanCallPlus() + { + var vector = CreateVector(Data); + var other = vector.Plus(); + + for (var i = 0; i < Data.Length; i++) + { + Assert.AreEqual(vector[i], other[i]); + } + } + + [Test] + public void OperatorPlusThrowsArgumentNullExceptionWhenCallOnNullVector() + { + Vector vector = null; + Vector other = null; + Assert.Throws(() => other = +vector); + } + + [Test] + public void CanCallUnaryPlusOperator() + { + var vector = CreateVector(Data); + var other = +vector; + + for (var i = 0; i < Data.Length; i++) + { + Assert.AreEqual(vector[i], other[i]); + } + } + + [Test] + [MultipleAsserts] + public void CanAddScalarToVector() + { + var copy = CreateVector(Data); + var vector = copy.Add(2.0f); + + for (var i = 0; i < Data.Length; i++) + { + Assert.AreEqual(Data[i] + 2.0, vector[i]); + } + + vector.Add(0.0f); + for (var i = 0; i < Data.Length; i++) + { + Assert.AreEqual(Data[i] + 2.0, vector[i]); + } + } + + [Test] + [MultipleAsserts] + public void CanAddScalarToVectorUsingResultVector() + { + var vector = CreateVector(Data); + var result = CreateVector(Data.Length); + vector.Add(2.0f, result); + + 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] + 2.0, result[i]); + } + + vector.Add(0.0f, result); + for (var i = 0; i < Data.Length; i++) + { + Assert.AreEqual(Data[i], result[i]); + } + } + + [Test] + public void ThrowsArgumentNullExceptionWhenAddingScalarWithNullResultVector() + { + var vector = CreateVector(Data.Length); + Assert.Throws(() => vector.Add(0.0f, null)); + } + + [Test] + public void ThrowsArgumentExceptionWhenAddingScalarWithWrongSizeResultVector() + { + var vector = CreateVector(Data.Length); + var result = CreateVector(Data.Length + 1); + Assert.Throws(() => vector.Add(0.0f, result)); + } + + [Test] + public void ThrowsArgumentNullExceptionWhenAddingTwoVectorsAndOneIsNull() + { + var vector = CreateVector(Data); + Assert.Throws(() => vector.Add(null)); + } + + [Test] + public void ThrowsArgumentExceptionWhenAddingTwoVectorsOfDifferingSize() + { + var vector = CreateVector(Data.Length); + var other = CreateVector(Data.Length + 1); + Assert.Throws(() => vector.Add(other)); + } + + [Test] + public void ThrowsArgumentNullExceptionWhenAddingTwoVectorsAndResultIsNull() + { + var vector = CreateVector(Data.Length); + var other = CreateVector(Data.Length + 1); + Assert.Throws(() => vector.Add(other, null)); + } + + [Test] + public void ThrowsArgumentExceptionWhenAddingTwoVectorsAndResultIsDifferentSize() + { + var vector = CreateVector(Data.Length); + var other = CreateVector(Data.Length); + var result = CreateVector(Data.Length + 1); + Assert.Throws(() => vector.Add(other, result)); + } + + [Test] + public void AdditionOperatorThrowsArgumentNullExpectionIfAVectorIsNull() + { + Vector a = null; + var b = CreateVector(Data.Length); + Assert.Throws(() => a += b); + + a = b; + b = null; + Assert.Throws(() => a += b); + } + + [Test] + public void AdditionOperatorThrowsArgumentExpectionIfVectorsAreDifferentSize() + { + var a = CreateVector(Data.Length); + var b = CreateVector(Data.Length + 1); + Assert.Throws(() => a += b); + } + + [Test] + public void CanAddTwoVectors() + { + var copy = CreateVector(Data); + var other = CreateVector(Data); + var vector = copy.Add(other); + + for (var i = 0; i < Data.Length; i++) + { + Assert.AreEqual(Data[i] * 2.0, vector[i]); + } + } + + [Test] + [MultipleAsserts] + public void CanAddTwoVectorsUsingResultVector() + { + var vector = CreateVector(Data); + var other = CreateVector(Data); + var result = CreateVector(Data.Length); + vector.Add(other, result); + + 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] + [MultipleAsserts] + public void CanAddTwoVectorsUsingOperator() + { + var vector = CreateVector(Data); + var other = CreateVector(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 CanAddVectorToItself() + { + var copy = CreateVector(Data); + var vector = copy.Add(copy); + + for (var i = 0; i < Data.Length; i++) + { + Assert.AreEqual(Data[i] * 2.0, vector[i]); + } + } + + [Test] + [MultipleAsserts] + public void CanAddVectorToItselfUsingResultVector() + { + var vector = CreateVector(Data); + var result = CreateVector(Data.Length); + vector.Add(vector, result); + + 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] * 2.0, result[i]); + } + } + + [Test] + [MultipleAsserts] + public void CanAddTwoVectorsUsingItselfAsResultVector() + { + var vector = CreateVector(Data); + var other = CreateVector(Data); + vector.Add(other, vector); + + for (var i = 0; i < Data.Length; i++) + { + Assert.AreEqual(Data[i], other[i], "Making sure the original vector wasn't modified."); + Assert.AreEqual(Data[i] * 2.0, vector[i]); + } + } + + [Test] + public void CanCallNegate() + { + var vector = CreateVector(Data); + var other = vector.Negate(); + for (var i = 0; i < Data.Length; i++) + { + Assert.AreEqual(-Data[i], other[i]); + } + } + + [Test] + public void OperatorNegateThrowsArgumentNullExceptionWhenCallOnNullVector() + { + Vector vector = null; + Vector other = null; + Assert.Throws(() => other = -vector); + } + + [Test] + public void CanCallUnaryNegationOperator() + { + var vector = CreateVector(Data); + var other = -vector; + for (var i = 0; i < Data.Length; i++) + { + Assert.AreEqual(-Data[i], other[i]); + } + } + + [Test] + [MultipleAsserts] + public void CanSubtractScalarFromVector() + { + var copy = CreateVector(Data); + var vector = copy.Subtract(2.0f); + + for (var i = 0; i < Data.Length; i++) + { + Assert.AreEqual(Data[i] - 2.0, vector[i]); + } + + vector.Subtract(0.0f); + for (var i = 0; i < Data.Length; i++) + { + Assert.AreEqual(Data[i] - 2.0, vector[i]); + } + } + + [Test] + [MultipleAsserts] + public void CanSubtractScalarFromVectorUsingResultVector() + { + var vector = CreateVector(Data); + var result = CreateVector(Data.Length); + vector.Subtract(2.0f, result); + + 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] - 2.0, result[i]); + } + + vector.Subtract(0.0f, result); + for (var i = 0; i < Data.Length; i++) + { + Assert.AreEqual(Data[i], result[i]); + } + } + + [Test] + public void ThrowsArgumentNullExceptionWhenSubtractingScalarWithNullResultVector() + { + var vector = CreateVector(Data.Length); + Assert.Throws(() => vector.Subtract(0.0f, null)); + } + + [Test] + public void ThrowsArgumentExceptionWhenSubtractingScalarWithWrongSizeResultVector() + { + var vector = CreateVector(Data.Length); + var result = CreateVector(Data.Length + 1); + Assert.Throws(() => vector.Subtract(0.0f, result)); + } + + [Test] + public void ThrowsArgumentNullExceptionWhenSubtractingTwoVectorsAndOneIsNull() + { + var vector = CreateVector(Data); + Assert.Throws(() => vector.Subtract(null)); + } + + [Test] + public void ThrowsArgumentExceptionWhenSubtractingTwoVectorsOfDifferingSize() + { + var vector = CreateVector(Data.Length); + var other = CreateVector(Data.Length + 1); + Assert.Throws(() => vector.Subtract(other)); + } + + [Test] + public void ThrowsArgumentNullExceptionWhenSubtractingTwoVectorsAndResultIsNull() + { + var vector = CreateVector(Data.Length); + var other = CreateVector(Data.Length + 1); + Assert.Throws(() => vector.Subtract(other, null)); + } + + [Test] + public void ThrowsArgumentExceptionWhenSubtractingTwoVectorsAndResultIsDifferentSize() + { + var vector = CreateVector(Data.Length); + var other = CreateVector(Data.Length); + var result = CreateVector(Data.Length + 1); + Assert.Throws(() => vector.Subtract(other, result)); + } + + [Test] + public void SubtractionOperatorThrowsArgumentNullExpectionIfAVectorIsNull() + { + Vector a = null; + var b = CreateVector(Data.Length); + Assert.Throws(() => a -= b); + + a = b; + b = null; + Assert.Throws(() => a -= b); + } + + [Test] + public void SubtractionOperatorThrowsArgumentExpectionIfVectorsAreDifferentSize() + { + var a = CreateVector(Data.Length); + var b = CreateVector(Data.Length + 1); + Assert.Throws(() => a -= b); + } + + [Test] + public void CanSubtractTwoVectors() + { + var copy = CreateVector(Data); + var other = CreateVector(Data); + var vector = copy.Subtract(other); + + for (var i = 0; i < Data.Length; i++) + { + Assert.AreEqual(0.0, vector[i]); + } + } + + [Test] + [MultipleAsserts] + public void CanSubtractTwoVectorsUsingResultVector() + { + var vector = CreateVector(Data); + var other = CreateVector(Data); + var result = CreateVector(Data.Length); + vector.Subtract(other, result); + + 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 CanSubtractTwoVectorsUsingOperator() + { + var vector = CreateVector(Data); + var other = CreateVector(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] + public void CanSubtractVectorFromItself() + { + var copy = CreateVector(Data); + var vector = copy.Subtract(copy); + + for (var i = 0; i < Data.Length; i++) + { + Assert.AreEqual(0.0, vector[i]); + } + } + + [Test] + [MultipleAsserts] + public void CanSubtractVectorFromItselfUsingResultVector() + { + var vector = CreateVector(Data); + var result = CreateVector(Data.Length); + vector.Subtract(vector, result); + + for (var i = 0; i < Data.Length; i++) + { + Assert.AreEqual(Data[i], vector[i], "Making sure the original vector wasn't modified."); + Assert.AreEqual(0.0, result[i]); + } + } + + [Test] + [MultipleAsserts] + public void CanSubtractTwoVectorsUsingItselfAsResultVector() + { + var vector = CreateVector(Data); + var other = CreateVector(Data); + vector.Subtract(other, vector); + + for (var i = 0; i < Data.Length; i++) + { + Assert.AreEqual(Data[i], other[i], "Making sure the original vector wasn't modified."); + Assert.AreEqual(0.0, vector[i]); + } + } + + [Test] + [MultipleAsserts] + public void CanDivideVectorByScalar() + { + var copy = CreateVector(Data); + var vector = copy.Divide(2.0f); + + for (var i = 0; i < Data.Length; i++) + { + Assert.AreEqual(Data[i] / 2.0, vector[i]); + } + + vector.Divide(1.0f); + for (var i = 0; i < Data.Length; i++) + { + Assert.AreEqual(Data[i] / 2.0, vector[i]); + } + } + + [Test] + [MultipleAsserts] + public void CanDivideVectorByScalarUsingResultVector() + { + var vector = CreateVector(Data); + var result = CreateVector(Data.Length); + vector.Divide(2.0f, result); + + 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] / 2.0, result[i]); + } + + vector.Divide(1.0f, result); + for (var i = 0; i < Data.Length; i++) + { + Assert.AreEqual(Data[i], result[i]); + } + } + + [Test] + [MultipleAsserts] + public void CanMultiplyVectorByScalar() + { + var copy = CreateVector(Data); + var vector = copy.Multiply(2.0f); + + for (var i = 0; i < Data.Length; i++) + { + Assert.AreEqual(Data[i] * 2.0, vector[i]); + } + + vector.Multiply(1.0f); + for (var i = 0; i < Data.Length; i++) + { + Assert.AreEqual(Data[i] * 2.0, vector[i]); + } + } + + [Test] + [MultipleAsserts] + public void CanMultiplyVectorByScalarUsingResultVector() + { + var vector = CreateVector(Data); + var result = CreateVector(Data.Length); + vector.Multiply(2.0f, result); + + 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] * 2.0, result[i]); + } + + vector.Multiply(1.0f, result); + for (var i = 0; i < Data.Length; i++) + { + Assert.AreEqual(Data[i], result[i]); + } + } + + [Test] + public void ThrowsArgumentNullExceptionWhenMultiplyingScalarWithNullResultVector() + { + var vector = CreateVector(Data.Length); + Assert.Throws(() => vector.Multiply(1.0f, null)); + } + + [Test] + public void ThrowsArgumentNullExceptionWhenDividingScalarWithNullResultVector() + { + var vector = CreateVector(Data.Length); + Assert.Throws(() => vector.Divide(1.0f, null)); + } + + [Test] + public void ThrowsArgumentExceptionWhenMultiplyingScalarWithWrongSizeResultVector() + { + var vector = CreateVector(Data.Length); + var result = CreateVector(Data.Length + 1); + Assert.Throws(() => vector.Multiply(0.0f, result)); + } + + [Test] + public void ThrowsArgumentExceptionWhenDividingScalarWithWrongSizeResultVector() + { + var vector = CreateVector(Data.Length); + var result = CreateVector(Data.Length + 1); + Assert.Throws(() => vector.Divide(0.0f, result)); + } + + [Test] + [MultipleAsserts] + public void CanMultiplyVectorByScalarUsingOperators() + { + var vector = CreateVector(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 = CreateVector(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 CanDivideVectorByScalarUsingOperators() + { + var vector = CreateVector(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] + [MultipleAsserts] + public void OperatorMultiplyThrowsArgumentNullExceptionWhenVectorIsNull() + { + Vector vector = null; + Vector result = null; + Assert.Throws(() => result = vector * 2.0f); + Assert.Throws(() => result = 2.0f * vector); + } + + [Test] + public void OperatorDivideThrowsArgumentNullExceptionWhenVectorIsNull() + { + Vector vector = null; + Assert.Throws(() => vector = vector / 2.0f); + } + + [Test] + public void CanDotProduct() + { + var dataA = CreateVector(Data); + var dataB = CreateVector(Data); + + Assert.AreEqual(55.0, dataA.DotProduct(dataB)); + } + + [Test] + [ExpectedArgumentNullException] + public void DotProductThrowsExceptionWhenArgumentIsNull() + { + var dataA = CreateVector(Data); + Vector dataB = null; + + dataA.DotProduct(dataB); + } + + [Test] + [ExpectedArgumentException] + public void DotProductThrowsExceptionWhenArgumentHasDifferentSize() + { + var dataA = CreateVector(Data); + var dataB = CreateVector(new float[] { 1, 2, 3, 4, 5, 6 }); + + dataA.DotProduct(dataB); + } + + [Test] + public void CanDotProductUsingOperator() + { + var dataA = CreateVector(Data); + var dataB = CreateVector(Data); + + Assert.AreEqual(55.0, dataA * dataB); + } + + [Test] + [ExpectedArgumentNullException] + public void OperatorDotProductThrowsExceptionWhenLeftArgumentIsNull() + { + var dataA = CreateVector(Data); + Vector dataB = null; + + var d = dataA * dataB; + } + + [Test] + [ExpectedArgumentNullException] + public void OperatorDotProductThrowsExceptionWhenRightArgumentIsNull() + { + Vector dataA = null; + var dataB = CreateVector(Data); + + var d = dataA * dataB; + } + + [Test] + [ExpectedArgumentException] + public void OperatorDotProductThrowsExceptionWhenArgumentHasDifferentSize() + { + var dataA = CreateVector(Data); + var dataB = CreateVector(new float[] { 1, 2, 3, 4, 5, 6 }); + + var d = dataA * dataB; + } + + [Test] + public void PointwiseMultiply() + { + var vector1 = CreateVector(Data); + var vector2 = vector1.Clone(); + var result = vector1.PointwiseMultiply(vector2); + for (var i = 0; i < vector1.Count; i++) + { + Assert.AreEqual(Data[i] * Data[i], result[i]); + } + } + + [Test] + public void PointwiseMultiplyUsingResultVector() + { + var vector1 = CreateVector(Data); + var vector2 = vector1.Clone(); + var result = CreateVector(vector1.Count); + vector1.PointwiseMultiply(vector2, result); + for (var i = 0; i < vector1.Count; i++) + { + Assert.AreEqual(Data[i] * Data[i], result[i]); + } + } + + [Test] + [ExpectedArgumentNullException] + public void PointwiseMultiplyWithOtherNullShouldThrowException() + { + var vector1 = CreateVector(Data); + Vector vector2 = null; + var result = CreateVector(vector1.Count); + vector1.PointwiseMultiply(vector2, result); + } + + [Test] + [ExpectedArgumentNullException] + public void PointwiseMultiplyWithResultNullShouldThrowException() + { + var vector1 = CreateVector(Data); + var vector2 = vector1.Clone(); + Vector result = null; + vector1.PointwiseMultiply(vector2, result); + } + + [Test] + [ExpectedArgumentException] + public void PointwiseMultiplyWithInvalidResultLengthShouldThrowException() + { + var vector1 = CreateVector(Data); + var vector2 = vector1.Clone(); + var result = CreateVector(vector1.Count + 1); + vector1.PointwiseMultiply(vector2, result); + } + + [Test] + public void PointWiseDivide() + { + var vector1 = CreateVector(Data); + var vector2 = vector1.Clone(); + var result = vector1.PointwiseDivide(vector2); + for (var i = 0; i < vector1.Count; i++) + { + Assert.AreEqual(Data[i] / Data[i], result[i]); + } + } + + [Test] + public void PointWiseDivideUsingResultVector() + { + var vector1 = CreateVector(Data); + var vector2 = vector1.Clone(); + var result = CreateVector(vector1.Count); + vector1.PointwiseDivide(vector2, result); + for (var i = 0; i < vector1.Count; i++) + { + Assert.AreEqual(Data[i] / Data[i], result[i]); + } + } + + [Test] + [ExpectedArgumentNullException] + public void PointwiseDivideWithOtherNullShouldThrowException() + { + var vector1 = CreateVector(Data); + Vector vector2 = null; + var result = CreateVector(vector1.Count); + vector1.PointwiseDivide(vector2, result); + } + + [Test] + [ExpectedArgumentNullException] + public void PointwiseDivideWithResultNullShouldThrowException() + { + var vector1 = CreateVector(Data); + var vector2 = vector1.Clone(); + Vector result = null; + vector1.PointwiseDivide(vector2, result); + } + + [Test] + [ExpectedArgumentException] + public void PointwiseDivideWithInvalidResultLengthShouldThrowException() + { + var vector1 = CreateVector(Data); + var vector2 = vector1.Clone(); + var result = CreateVector(vector1.Count + 1); + vector1.PointwiseDivide(vector2, result); + } + + [Test] + public void CanCalculateOuterProduct() + { + var vector1 = CreateVector(Data); + var vector2 = CreateVector(Data); + Matrix m = Vector.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 OuterProductWithFirstParameterNullShouldThrowException() + { + Vector vector1 = null; + var vector2 = CreateVector(Data); + Vector.OuterProduct(vector1, vector2); + } + + [Test] + [ExpectedArgumentNullException] + public void OutercProductWithSecondParameterNullShouldThrowException() + { + var vector1 = CreateVector(Data); + Vector vector2 = null; + Vector.OuterProduct(vector1, vector2); + } + + [Test] + public void CanCalculateTensorMultiply() + { + var vector1 = CreateVector(Data); + var vector2 = CreateVector(Data); + var m = vector1.TensorMultiply(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 TensorMultiplyWithNullParameterNullShouldThrowException() + { + var vector1 = CreateVector(Data); + Vector vector2 = null; + vector1.TensorMultiply(vector2); + } + } +} \ No newline at end of file diff --git a/src/UnitTests/LinearAlgebraTests/Single/VectorTests.Norm.cs b/src/UnitTests/LinearAlgebraTests/Single/VectorTests.Norm.cs new file mode 100644 index 00000000..52cb31f8 --- /dev/null +++ b/src/UnitTests/LinearAlgebraTests/Single/VectorTests.Norm.cs @@ -0,0 +1,90 @@ +// +// 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. +// + +namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single +{ + using System; + using MbUnit.Framework; + + public abstract partial class VectorTests + { + [Test] + public void CanComputeNorm() + { + var vector = CreateVector(Data); + AssertHelpers.AlmostEqual(7.416198487095663f, (float)vector.Norm(2), 7); + } + + [Test] + public void CanComputeNorm1() + { + var vector = CreateVector(Data); + AssertHelpers.AlmostEqual(15.0f, (float)vector.Norm(1), 7); + } + + [Test] + public void CanComputeSquareNorm() + { + var vector = CreateVector(Data); + AssertHelpers.AlmostEqual(55.0f, (float)vector.Norm(2) * (float)vector.Norm(2), 7); + } + + [Test] + [Row(1, 15.0)] + [Row(2, 7.416198487095663)] + [Row(3, 6.0822019955734001)] + [Row(10, 5.0540557845353753)] + public void CanComputeNormP(int p, float expected) + { + var vector = CreateVector(Data); + AssertHelpers.AlmostEqual(expected, (float)vector.Norm(p), 7); + } + + [Test] + public void CanComputeNormInfinity() + { + var vector = CreateVector(Data); + AssertHelpers.AlmostEqual(5.0f, (float)vector.Norm(Double.PositiveInfinity), 7); + } + + [Test] + [MultipleAsserts] + public void CanNormalizeVector() + { + var vector = CreateVector(Data); + var result = vector.Normalize(2); + AssertHelpers.AlmostEqual(0.134839967f, result[0], 7); + AssertHelpers.AlmostEqual(0.269679934f, result[1], 7); + AssertHelpers.AlmostEqual(0.404519916f, result[2], 7); + AssertHelpers.AlmostEqual(0.539359868f, result[3], 7); + AssertHelpers.AlmostEqual(0.6741998f, result[4], 7); + } + } +} \ No newline at end of file diff --git a/src/UnitTests/LinearAlgebraTests/Single/VectorTests.cs b/src/UnitTests/LinearAlgebraTests/Single/VectorTests.cs new file mode 100644 index 00000000..a79bd509 --- /dev/null +++ b/src/UnitTests/LinearAlgebraTests/Single/VectorTests.cs @@ -0,0 +1,439 @@ +// +// 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. +// + +namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single +{ + using System; + using System.Collections; + using System.Collections.Generic; + using System.Globalization; + using Distributions; + using LinearAlgebra.Generic; + using MbUnit.Framework; + + [TestFixture] + public abstract partial class VectorTests + { + protected readonly float[] Data = { 1, 2, 3, 4, 5 }; + + [Test] + [MultipleAsserts] + public void CanCloneVector() + { + var vector = CreateVector(Data); + var clone = vector.Clone(); + + Assert.AreNotSame(vector, clone); + Assert.AreEqual(vector.Count, clone.Count); + for (var index = 0; index < Data.Length; index++) + { + Assert.AreEqual(vector[index], clone[index]); + } + } + + [Test] + [MultipleAsserts] + public void CanCloneVectorUsingICloneable() + { + var vector = CreateVector(Data); + var clone = (Vector)((ICloneable)vector).Clone(); + + Assert.AreNotSame(vector, clone); + Assert.AreEqual(vector.Count, clone.Count); + for (var index = 0; index < Data.Length; index++) + { + Assert.AreEqual(vector[index], clone[index]); + } + } + + [Test] + public void CanConvertVectorToString() + { + var vector = CreateVector(Data); + var str = vector.ToString(); + var sep = CultureInfo.CurrentCulture.TextInfo.ListSeparator; + Assert.AreEqual(string.Format("1{0}2{0}3{0}4{0}5", sep), str); + } + + [Test] + [MultipleAsserts] + public void CanCopyPartialVectorToAnother() + { + var vector = CreateVector(Data); + var other = CreateVector(Data.Length); + + vector.CopyTo(other, 2, 2, 2); + + Assert.AreEqual(0.0, other[0]); + Assert.AreEqual(0.0, other[1]); + Assert.AreEqual(3.0, other[2]); + Assert.AreEqual(4.0, other[3]); + Assert.AreEqual(0.0, other[4]); + } + + [Test] + [MultipleAsserts] + public void CanCopyPartialVectorToSelf() + { + var vector = CreateVector(Data); + vector.CopyTo(vector, 0, 2, 2); + + Assert.AreEqual(1.0, vector[0]); + Assert.AreEqual(2.0, vector[1]); + Assert.AreEqual(1.0, vector[2]); + Assert.AreEqual(2.0, vector[3]); + Assert.AreEqual(5.0, vector[4]); + } + + [Test] + [MultipleAsserts] + public void CanCopyVectorToAnother() + { + var vector = CreateVector(Data); + var other = CreateVector(Data.Length); + + vector.CopyTo(other); + + for (var index = 0; index < Data.Length; index++) + { + Assert.AreEqual(vector[index], other[index]); + } + } + + [Test] + [Ignore] + public void CanCreateMatrix() + { + } + + [Test] + public void CanCreateVector() + { + var expected = CreateVector(5); + var actual = expected.CreateVector(5); + Assert.AreEqual(expected.GetType(), actual.GetType(), "vectors are same type."); + } + + [Test] + public void CanEnumerateOverVector() + { + var vector = CreateVector(Data); + Assert.AreElementsEqual(Data, vector); + } + + [Test] + [MultipleAsserts] + public void CanEnumerateOverVectorUsingIEnumerable() + { + var enumerable = (IEnumerable)CreateVector(Data); + var index = 0; + foreach (var element in enumerable) + { + Assert.AreEqual(Data[index++], (float)element); + } + } + + [Test] + [MultipleAsserts] + public void CanEquateVectors() + { + var vector1 = CreateVector(Data); + var vector2 = CreateVector(Data); + var vector3 = CreateVector(4); + Assert.IsTrue(vector1.Equals(vector1)); + Assert.IsTrue(vector1.Equals(vector2)); + Assert.IsFalse(vector1.Equals(vector3)); + Assert.IsFalse(vector1.Equals(null)); + } + + [Test] + [MultipleAsserts] + public void ThrowsArgumentExceptionIfSizeIsNotPositive() + { + Assert.Throws(() => CreateVector(-1)); + Assert.Throws(() => CreateVector(0)); + } + + [Test] + public void TestingForEqualityWithNonVectorReturnsFalse() + { + var vector = CreateVector(Data); + Assert.IsFalse(vector.Equals(2)); + } + + [Test] + public void CanTestForEqualityUsingObjectEquals() + { + var vector1 = CreateVector(Data); + var vector2 = CreateVector(Data); + Assert.IsTrue(vector1.Equals((object)vector2)); + } + + [Test] + public void VectorGetHashCode() + { + var vector = CreateVector(new float[] { 1, 2, 3, 4 }); + Assert.AreEqual(2093056, vector.GetHashCode()); + } + + [Test] + public void GetIndexedEnumerator() + { + var vector = CreateVector(Data); + foreach (var pair in vector.GetIndexedEnumerator()) + { + Assert.AreEqual(Data[pair.Key], pair.Value); + } + } + + [Test] + public void CanConvertVectorToArray() + { + var vector = CreateVector(Data); + var array = vector.ToArray(); + Assert.AreElementsEqual(vector, array); + } + + [Test] + [MultipleAsserts] + public void CanConvertVectorToColumnMatrix() + { + var vector = CreateVector(Data); + var matrix = vector.ToColumnMatrix(); + + Assert.AreEqual(vector.Count, matrix.RowCount); + Assert.AreEqual(1, matrix.ColumnCount); + + for (var i = 0; i < vector.Count; i++) + { + Assert.AreEqual(vector[i], matrix[i, 0]); + } + } + + [Test] + [MultipleAsserts] + public void CanConvertVectorToRowMatrix() + { + var vector = CreateVector(Data); + var matrix = vector.ToRowMatrix(); + + Assert.AreEqual(vector.Count, matrix.ColumnCount); + Assert.AreEqual(1, matrix.RowCount); + + for (var i = 0; i < vector.Count; i++) + { + Assert.AreEqual(vector[i], matrix[0, i]); + } + } + + [Test] + public void CanSetValues() + { + var vector = CreateVector(Data); + vector.SetValues(Data); + for (var i = 0; i < Data.Length; i++) + { + Assert.AreEqual(vector[i], Data[i]); + } + } + + [Test] + [Row(0, 5)] + [Row(2, 2)] + [Row(1, 4)] + [Row(6, 10, ExpectedException = typeof(ArgumentOutOfRangeException))] + [Row(1, 10, ExpectedException = typeof(ArgumentOutOfRangeException))] + [Row(1, -10, ExpectedException = typeof(ArgumentOutOfRangeException))] + public void CanCalculateSubVector(int index, int length) + { + var vector = CreateVector(Data); + var sub = vector.SubVector(index, length); + Assert.AreEqual(length, sub.Count); + for (var i = 0; i < length; i++) + { + Assert.AreEqual(vector[i + index], sub[i]); + } + } + + [Test] + public void CanFindAbsoluteMinimumIndex() + { + var source = CreateVector(Data); + const int Expected = 0; + var actual = source.AbsoluteMinimumIndex(); + Assert.AreEqual(Expected, actual); + } + + [Test] + public void CanFindAbsoluteMinimum() + { + var source = CreateVector(Data); + const float Expected = 1; + var actual = source.AbsoluteMinimum(); + Assert.AreEqual(Expected, actual); + } + + [Test] + public void CanFindAbsoluteMaximumIndex() + { + var source = CreateVector(Data); + const int Expected = 4; + var actual = source.AbsoluteMaximumIndex(); + Assert.AreEqual(Expected, actual); + } + + [Test] + public void CanFindAbsoluteMaximum() + { + var source = CreateVector(Data); + const float Expected = 5; + var actual = source.AbsoluteMaximum(); + Assert.AreEqual(Expected, actual); + } + + [Test] + public void CanFindMaximumIndex() + { + var vector = CreateVector(Data); + + const int Expected = 4; + var actual = vector.MaximumIndex(); + + Assert.AreEqual(Expected, actual); + } + + [Test] + public void CanFindMaximum() + { + var vector = CreateVector(Data); + + const float Expected = 5; + var actual = vector.Maximum(); + + Assert.AreEqual(Expected, actual); + } + + [Test] + public void CanFindMinimumIndex() + { + var vector = CreateVector(Data); + + const int Expected = 0; + var actual = vector.MinimumIndex(); + + Assert.AreEqual(Expected, actual); + } + + [Test] + public void CanFindMinimum() + { + var vector = CreateVector(Data); + + const float Expected = 1; + var actual = vector.Minimum(); + + Assert.AreEqual(Expected, actual); + } + + [Test] + [Row(0, 5)] + [Row(2, 2)] + [Row(1, 4)] + [Row(6, 10, ExpectedException = typeof(ArgumentOutOfRangeException))] + [Row(1, 10, ExpectedException = typeof(ArgumentOutOfRangeException))] + [Row(1, -10, ExpectedException = typeof(ArgumentOutOfRangeException))] + public void CanGetSubVector(int index, int length) + { + var vector = CreateVector(Data); + var sub = vector.SubVector(index, length); + Assert.AreEqual(length, sub.Count); + for (var i = 0; i < length; i++) + { + Assert.AreEqual(vector[i + index], sub[i]); + } + } + + [Test] + public void CanSum() + { + float[] testData = { -20, -10, 10, 20, 30, }; + var vector = CreateVector(testData); + var actual = vector.Sum(); + const float Expected = 30; + Assert.AreEqual(Expected, actual); + } + + [Test] + public void CanSumMagnitudes() + { + float[] testData = { -20, -10, 10, 20, 30, }; + var vector = CreateVector(testData); + var actual = vector.SumMagnitudes(); + const float Expected = 90; + Assert.AreEqual(Expected, actual); + } + + [Test] + [ExpectedArgumentNullException] + public void CanSetValuesWithNullParameterShouldThrowException() + { + var vector = CreateVector(Data); + vector.SetValues(null); + } + + [Test] + [ExpectedArgumentException] + public void CanSetValuesWithNonEqualDataLengthShouldThrowException() + { + var vector = CreateVector(Data.Length + 2); + vector.SetValues(Data); + } + + + [Test] + [ExpectedArgumentException] + public void RandomWithNumberOfElementsLessThanZeroShouldThrowException() + { + var vector = CreateVector(4); + vector.Random(-2, new ContinuousUniform()); + } + + [Test] + public void CanClearVector() + { + float[] testData = { -20, -10, 10, 20, 30, }; + var vector = CreateVector(testData); + vector.Clear(); + foreach (var element in vector) + { + Assert.AreEqual(0.0, element); + } + } + + protected abstract Vector CreateVector(int size); + protected abstract Vector CreateVector(IList data); + } +} \ No newline at end of file diff --git a/src/UnitTests/UnitTests.csproj b/src/UnitTests/UnitTests.csproj index dfb5c828..de749239 100644 --- a/src/UnitTests/UnitTests.csproj +++ b/src/UnitTests/UnitTests.csproj @@ -112,7 +112,7 @@ - + @@ -146,7 +146,7 @@ - + @@ -156,6 +156,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +