From fe9e158777c8946a19c96445c4126ca79c09ed8e Mon Sep 17 00:00:00 2001 From: Marcus Cuda Date: Tue, 13 Nov 2012 15:58:27 +0200 Subject: [PATCH] added thin qr --- src/NativeWrappers/MKL/lapack.cpp | 282 +++++++++++---- src/NativeWrappers/MKL/lapack.h | 2 +- .../ACMLWrapper.vcxproj} | 4 + .../ACMLWrapper/ACMKWrapper.vcxproj.filters | 33 -- .../ACMLWrapperTests/ACMLWrapperTests.csproj | 2 +- .../Windows/MKL/MKLWrapper.vcxproj | 74 ++-- .../Windows/MKL/MKLWrapper.vcxproj.filters | 22 +- .../MKLWrapperTests/MKLWrapperTests.csproj | 5 +- src/NativeWrappers/Windows/NativeWrappers.sln | 63 +--- .../Acml/AcmlLinearAlgebraProvider.Complex.cs | 2 + .../AcmlLinearAlgebraProvider.Complex32.cs | 2 + .../Acml/AcmlLinearAlgebraProvider.double.cs | 2 + .../Acml/AcmlLinearAlgebraProvider.float.cs | 2 + .../GotoBlasLinearAlgebraProvider.Complex.cs | 10 +- ...GotoBlasLinearAlgebraProvider.Complex32.cs | 10 +- .../GotoBlasLinearAlgebraProvider.double.cs | 10 +- .../GotoBlasLinearAlgebraProvider.float.cs | 10 +- .../ILinearAlgebraProviderOfT.cs | 70 +++- .../ManagedLinearAlgebraProvider.Complex.cs | 296 +++++++++++---- .../ManagedLinearAlgebraProvider.Complex32.cs | 289 +++++++++++---- .../ManagedLinearAlgebraProvider.Double.cs | 291 +++++++++++---- .../ManagedLinearAlgebraProvider.Single.cs | 292 +++++++++++---- .../Mkl/MklLinearAlgebraProvider.Complex.cs | 154 +++----- .../Mkl/MklLinearAlgebraProvider.Complex32.cs | 154 +++----- .../Mkl/MklLinearAlgebraProvider.double.cs | 232 +++++++----- .../Mkl/MklLinearAlgebraProvider.float.cs | 154 +++----- .../LinearAlgebra/Mkl/SafeNativeMethods.cs | 24 ++ .../LinearAlgebra/Complex/ExtensionMethods.cs | 5 +- .../Complex/Factorization/DenseQR.cs | 23 +- .../LinearAlgebra/Complex/Factorization/QR.cs | 7 +- .../Complex/Factorization/UserQR.cs | 65 +++- .../Complex32/ExtensionMethods.cs | 5 +- .../Complex32/Factorization/DenseQR.cs | 23 +- .../Complex32/Factorization/QR.cs | 7 +- .../Complex32/Factorization/UserQR.cs | 65 +++- .../LinearAlgebra/Double/ExtensionMethods.cs | 5 +- .../Double/Factorization/DenseQR.cs | 24 +- .../LinearAlgebra/Double/Factorization/QR.cs | 7 +- .../Double/Factorization/UserQR.cs | 66 +++- .../Generic/Factorization/GramSchmidt.cs | 2 +- .../LinearAlgebra/Generic/Factorization/QR.cs | 42 ++- .../LinearAlgebra/Single/ExtensionMethods.cs | 5 +- .../Single/Factorization/DenseQR.cs | 23 +- .../LinearAlgebra/Single/Factorization/QR.cs | 7 +- .../Single/Factorization/UserQR.cs | 65 +++- src/Numerics/Numerics.csproj | 16 - .../Complex/LinearAlgebraProviderTests.cs | 337 ++++++++++++++++- .../Complex32/LinearAlgebraProviderTests.cs | 337 +++++++++++++++++ .../Double/LinearAlgebraProviderTests.cs | 342 +++++++++++++++++- .../Single/LinearAlgebraProviderTests.cs | 337 +++++++++++++++++ .../Complex/Factorization/QRTests.cs | 281 +++++++++++++- .../Complex/Factorization/UserQRTests.cs | 272 ++++++++++++++ .../Complex32/Factorization/QRTests.cs | 91 +++++ .../Complex32/Factorization/UserQRTests.cs | 277 ++++++++++++++ .../Double/Factorization/QRTests.cs | 81 +++++ .../Double/Factorization/UserQRTests.cs | 272 ++++++++++++++ .../Single/Factorization/QRTests.cs | 82 +++++ .../Single/Factorization/UserQRTests.cs | 272 ++++++++++++++ src/UnitTests/Setup.cs | 8 - 59 files changed, 4862 insertions(+), 1080 deletions(-) rename src/NativeWrappers/Windows/{ACMLWrapper/ACMKWrapper.vcxproj => ACML/ACMLWrapper.vcxproj} (97%) delete mode 100644 src/NativeWrappers/Windows/ACMLWrapper/ACMKWrapper.vcxproj.filters diff --git a/src/NativeWrappers/MKL/lapack.cpp b/src/NativeWrappers/MKL/lapack.cpp index 0024ac31..92f7d827 100644 --- a/src/NativeWrappers/MKL/lapack.cpp +++ b/src/NativeWrappers/MKL/lapack.cpp @@ -538,6 +538,27 @@ extern "C"{ return info; } + DLLEXPORT MKL_INT s_qr_thin_factor(MKL_INT m, MKL_INT n, float q[], float tau[], float r[], float work[], MKL_INT len) + { + MKL_INT info = 0; + sgeqrf_(&m, &n, q, &m, tau, work, &len, &info); + + for (MKL_INT i = 0; i < n; ++i) + { + for (MKL_INT j = 0; j < n; ++j) + { + if( i <= j) { + r[j * n + i] = q[j * m + i]; + } + } + } + + sorgqr_(&m, &n, &n, q, &m, tau, work, &len, &info); + + return info; + } + + DLLEXPORT MKL_INT d_qr_factor(MKL_INT m, MKL_INT n, double r[], double tau[], double q[], double work[], MKL_INT len) { MKL_INT info = 0; @@ -567,6 +588,26 @@ extern "C"{ return info; } + DLLEXPORT MKL_INT d_qr_thin_factor(MKL_INT m, MKL_INT n, double q[], double tau[], double r[], double work[], MKL_INT len) + { + MKL_INT info = 0; + dgeqrf_(&m, &n, q, &m, tau, work, &len, &info); + + for (MKL_INT i = 0; i < n; ++i) + { + for (MKL_INT j = 0; j < n; ++j) + { + if( i <= j) { + r[j * n + i] = q[j * m + i]; + } + } + } + + dorgqr_(&m, &n, &n, q, &m, tau, work, &len, &info); + + return info; + } + DLLEXPORT MKL_INT c_qr_factor(MKL_INT m, MKL_INT n, MKL_Complex8 r[], MKL_Complex8 tau[], MKL_Complex8 q[], MKL_Complex8 work[], MKL_INT len) { MKL_INT info = 0; @@ -596,6 +637,26 @@ extern "C"{ return info; } + DLLEXPORT MKL_INT c_qr_thin_factor(MKL_INT m, MKL_INT n, MKL_Complex8 q[], MKL_Complex8 tau[], MKL_Complex8 r[], MKL_Complex8 work[], MKL_INT len) + { + MKL_INT info = 0; + cgeqrf_(&m, &n, q, &m, tau, work, &len, &info); + + for (MKL_INT i = 0; i < n; ++i) + { + for (MKL_INT j = 0; j < n; ++j) + { + if( i <= j) { + r[j * n + i] = q[j * m + i]; + } + } + } + + cungqr_(&m, &n, &n, q, &m, tau, work, &len, &info); + + return info; + } + DLLEXPORT MKL_INT z_qr_factor(MKL_INT m, MKL_INT n, MKL_Complex16 r[], MKL_Complex16 tau[], MKL_Complex16 q[], MKL_Complex16 work[], MKL_INT len) { MKL_INT info = 0; @@ -625,29 +686,41 @@ extern "C"{ return info; } - DLLEXPORT MKL_INT s_qr_solve(MKL_INT m, MKL_INT n, MKL_INT bn, float r[], float b[], float x[], float work[], MKL_INT len) + DLLEXPORT MKL_INT z_qr_thin_factor(MKL_INT m, MKL_INT n, MKL_Complex16 q[], MKL_Complex16 tau[], MKL_Complex16 r[], MKL_Complex16 work[], MKL_INT len) { MKL_INT info = 0; - float* clone_r = new float[m*n]; - std::memcpy(clone_r, r, m*n*sizeof(float)); - - float* tau = new float[std::max(1, std::min(m,n))]; - sgeqrf_(&m, &n, clone_r, &m, tau, work, &len, &info); + zgeqrf_(&m, &n, q, &m, tau, work, &len, &info); - if (info != 0) + for (MKL_INT i = 0; i < n; ++i) { - delete[] clone_r; - delete[] tau; - return info; + for (MKL_INT j = 0; j < n; ++j) + { + if( i <= j) { + r[j * n + i] = q[j * m + i]; + } + } } + zungqr_(&m, &n, &n, q, &m, tau, work, &len, &info); + + return info; + } + + DLLEXPORT MKL_INT s_qr_solve(MKL_INT m, MKL_INT n, MKL_INT bn, float a[], float b[], float x[], float work[], MKL_INT len) + { + MKL_INT info = 0; + MKL_INT* jpvt = new MKL_INT[n]; + MKL_INT rank = 0; + float cond = -1.0; + + float* clone_a = new float[m*n]; + std::memcpy(clone_a, a, m*n*sizeof(float)); + float* clone_b = new float[m*bn]; std::memcpy(clone_b, b, m*bn*sizeof(float)); - char side ='L'; - char tran = 'T'; - sormqr_(&side, &tran, &m, &bn, &n, clone_r, &m, tau, clone_b, &m, work, &len, &info); - cblas_strsm(CblasColMajor, CblasLeft, CblasUpper, CblasNoTrans, CblasNonUnit, n, bn, 1.0, clone_r, m, clone_b, m); + sgelsy_(&m, &n, &bn, clone_a, &m, clone_b, &m, jpvt, &cond, &rank, work, &len, &info); + for (MKL_INT i = 0; i < n; ++i) { for (MKL_INT j = 0; j < bn; ++j) @@ -656,36 +729,57 @@ extern "C"{ } } - delete[] clone_r; - delete[] tau; + delete[] jpvt; + delete[] clone_a; delete[] clone_b; return info; } - DLLEXPORT MKL_INT d_qr_solve(MKL_INT m, MKL_INT n, MKL_INT bn, double r[], double b[], double x[], double work[], MKL_INT len) + DLLEXPORT MKL_INT d_qr_solve(MKL_INT m, MKL_INT n, MKL_INT bn, double a[], double b[], double x[], double work[], MKL_INT len) { MKL_INT info = 0; - double* clone_r = new double[m*n]; - std::memcpy(clone_r, r, m*n*sizeof(double)); + MKL_INT* jpvt = new MKL_INT[n]; + MKL_INT rank = 0; + double cond = -1.0; - double* tau = new double[std::max(1, std::min(m,n))]; - dgeqrf_(&m, &n, clone_r, &m, tau, work, &len, &info); + double* clone_a = new double[m*n]; + std::memcpy(clone_a, a, m*n*sizeof(double)); - if (info != 0) + double* clone_b = new double[m*bn]; + std::memcpy(clone_b, b, m*bn*sizeof(double)); + + dgelsy_(&m, &n, &bn, clone_a, &m, clone_b, &m, jpvt, &cond, &rank, work, &len, &info); + + for (MKL_INT i = 0; i < n; ++i) { - delete[] clone_r; - delete[] tau; - return info; + for (MKL_INT j = 0; j < bn; ++j) + { + x[j * n + i] = clone_b[j * m + i]; + } } - double* clone_b = new double[m*bn]; - std::memcpy(clone_b, b, m*bn*sizeof(double)); + delete[] jpvt; + delete[] clone_a; + delete[] clone_b; + return info; + } - char side ='L'; - char tran = 'T'; + DLLEXPORT MKL_INT c_qr_solve(MKL_INT m, MKL_INT n, MKL_INT bn, MKL_Complex8 a[], MKL_Complex8 b[], MKL_Complex8 x[], MKL_Complex8 work[], MKL_INT len) + { + MKL_INT info = 0; + MKL_INT* jpvt = new MKL_INT[n]; + float* rwork = new float[2*n]; + MKL_INT rank = 0; + float cond = -1.0; + + MKL_Complex8* clone_a = new MKL_Complex8[m*n]; + std::memcpy(clone_a, a, m*n*sizeof(MKL_Complex8)); + + MKL_Complex8* clone_b = new MKL_Complex8[m*bn]; + std::memcpy(clone_b, b, m*bn*sizeof(MKL_Complex8)); + + cgelsy_(&m, &n, &bn, clone_a, &m, clone_b, &m, jpvt, &cond, &rank, work, &len, rwork, &info); - dormqr_(&side, &tran, &m, &bn, &n, clone_r, &m, tau, clone_b, &m, work, &len, &info); - cblas_dtrsm(CblasColMajor, CblasLeft, CblasUpper, CblasNoTrans, CblasNonUnit, n, bn, 1.0, clone_r, m, clone_b, m); for (MKL_INT i = 0; i < n; ++i) { for (MKL_INT j = 0; j < bn; ++j) @@ -694,37 +788,55 @@ extern "C"{ } } + delete[] jpvt; + delete[] rwork; + delete[] clone_a; delete[] clone_b; - delete[] tau; - delete[] clone_r; return info; } - DLLEXPORT MKL_INT c_qr_solve(MKL_INT m, MKL_INT n, MKL_INT bn, MKL_Complex8 r[], MKL_Complex8 b[], MKL_Complex8 x[], MKL_Complex8 work[], MKL_INT len) + DLLEXPORT MKL_INT z_qr_solve(MKL_INT m, MKL_INT n, MKL_INT bn, MKL_Complex16 a[], MKL_Complex16 b[], MKL_Complex16 x[], MKL_Complex16 work[], MKL_INT len) { MKL_INT info = 0; - MKL_Complex8* clone_r = new MKL_Complex8[m*n]; - std::memcpy(clone_r, r, m*n*sizeof(MKL_Complex8)); + MKL_INT* jpvt = new MKL_INT[n]; + double* rwork = new double[2*n]; + MKL_INT rank = 0; + double cond = -1.0; + + MKL_Complex16* clone_a = new MKL_Complex16[m*n]; + std::memcpy(clone_a, a, m*n*sizeof(MKL_Complex16)); + + MKL_Complex16* clone_b = new MKL_Complex16[m*bn]; + std::memcpy(clone_b, b, m*bn*sizeof(MKL_Complex16)); - MKL_Complex8* tau = new MKL_Complex8[std::min(m,n)]; - cgeqrf_(&m, &n, clone_r, &m, tau, work, &len, &info); + zgelsy_(&m, &n, &bn, clone_a, &m, clone_b, &m, jpvt, &cond, &rank, work, &len, rwork, &info); - if (info != 0) + for (MKL_INT i = 0; i < n; ++i) { - delete[] clone_r; - delete[] tau; - return info; + for (MKL_INT j = 0; j < bn; ++j) + { + x[j * n + i] = clone_b[j * m + i]; + } } - char side ='L'; - char tran = 'C'; + delete[] jpvt; + delete[] rwork; + delete[] clone_a; + delete[] clone_b; + return info; + } - MKL_Complex8* clone_b = new MKL_Complex8[m*bn]; - std::memcpy(clone_b, b, m*bn*sizeof(MKL_Complex8)); + DLLEXPORT MKL_INT s_thin_qr_solve(MKL_INT m, MKL_INT n, MKL_INT bn, float a[], float b[], float x[], float work[], MKL_INT len) + { + MKL_INT info = 0; - cunmqr_(&side, &tran, &m, &bn, &n, clone_r, &m, tau, clone_b, &m, work, &len, &info); - MKL_Complex8 one = {1.0, 0.0}; - cblas_ctrsm(CblasColMajor, CblasLeft, CblasUpper, CblasNoTrans, CblasNonUnit, n, bn, &one, clone_r, m, clone_b, m); + float* clone_a = new float[m*n]; + std::memcpy(clone_a, a, m*n*sizeof(float)); + + float* clone_b = new float[m*bn]; + std::memcpy(clone_b, b, m*bn*sizeof(float)); + char N = 'N'; + sgels_(&N, &m, &n, &bn, clone_a, &m, clone_b, &m, work, &len, &info); for (MKL_INT i = 0; i < n; ++i) { @@ -734,37 +846,74 @@ extern "C"{ } } - delete[] clone_r; - delete[] tau; + delete[] clone_a; delete[] clone_b; return info; } - DLLEXPORT MKL_INT z_qr_solve(MKL_INT m, MKL_INT n, MKL_INT bn, MKL_Complex16 r[], MKL_Complex16 b[], MKL_Complex16 x[], MKL_Complex16 work[], MKL_INT len) + DLLEXPORT MKL_INT d_thin_qr_solve(MKL_INT m, MKL_INT n, MKL_INT bn, double a[], double b[], double x[], double work[], MKL_INT len) { MKL_INT info = 0; - MKL_Complex16* clone_r = new MKL_Complex16[m*n]; - std::memcpy(clone_r, r, m*n*sizeof(MKL_Complex16)); - MKL_Complex16* tau = new MKL_Complex16[std::min(m,n)]; - zgeqrf_(&m, &n, clone_r, &m, tau, work, &len, &info); + double* clone_a = new double[m*n]; + std::memcpy(clone_a, a, m*n*sizeof(double)); + + double* clone_b = new double[m*bn]; + std::memcpy(clone_b, b, m*bn*sizeof(double)); - if (info != 0) + char N = 'N'; + dgels_(&N, &m, &n, &bn, clone_a, &m, clone_b, &m, work, &len, &info); + + for (MKL_INT i = 0; i < n; ++i) { - delete[] clone_r; - delete[] tau; - return info; + for (MKL_INT j = 0; j < bn; ++j) + { + x[j * n + i] = clone_b[j * m + i]; + } } - char side ='L'; - char tran = 'C'; + delete[] clone_a; + delete[] clone_b; + return info; + } + + DLLEXPORT MKL_INT c_thin_qr_solve(MKL_INT m, MKL_INT n, MKL_INT bn, MKL_Complex8 a[], MKL_Complex8 b[], MKL_Complex8 x[], MKL_Complex8 work[], MKL_INT len) + { + MKL_INT info = 0; + MKL_Complex8* clone_a = new MKL_Complex8[m*n]; + std::memcpy(clone_a, a, m*n*sizeof(MKL_Complex8)); + + MKL_Complex8* clone_b = new MKL_Complex8[m*bn]; + std::memcpy(clone_b, b, m*bn*sizeof(MKL_Complex8)); + + char N = 'N'; + cgels_(&N, &m, &n, &bn, clone_a, &m, clone_b, &m, work, &len, &info); + + for (MKL_INT i = 0; i < n; ++i) + { + for (MKL_INT j = 0; j < bn; ++j) + { + x[j * n + i] = clone_b[j * m + i]; + } + } + + delete[] clone_a; + delete[] clone_b; + return info; + } + + DLLEXPORT MKL_INT z_thin_qr_solve(MKL_INT m, MKL_INT n, MKL_INT bn, MKL_Complex16 a[], MKL_Complex16 b[], MKL_Complex16 x[], MKL_Complex16 work[], MKL_INT len) + { + MKL_INT info = 0; + + MKL_Complex16* clone_a = new MKL_Complex16[m*n]; + std::memcpy(clone_a, a, m*n*sizeof(MKL_Complex16)); MKL_Complex16* clone_b = new MKL_Complex16[m*bn]; std::memcpy(clone_b, b, m*bn*sizeof(MKL_Complex16)); - zunmqr_(&side, &tran, &m, &bn, &n, clone_r, &m, tau, clone_b, &m, work, &len, &info); - MKL_Complex16 one = {1.0, 0.0}; - cblas_ztrsm(CblasColMajor, CblasLeft, CblasUpper, CblasNoTrans, CblasNonUnit, n, bn, &one, clone_r, m, clone_b, m); + char N = 'N'; + zgels_(&N, &m, &n, &bn, clone_a, &m, clone_b, &m, work, &len, &info); for (MKL_INT i = 0; i < n; ++i) { @@ -774,8 +923,7 @@ extern "C"{ } } - delete[] clone_r; - delete[] tau; + delete[] clone_a; delete[] clone_b; return info; } diff --git a/src/NativeWrappers/MKL/lapack.h b/src/NativeWrappers/MKL/lapack.h index 3026b4af..bd702cfb 100644 --- a/src/NativeWrappers/MKL/lapack.h +++ b/src/NativeWrappers/MKL/lapack.h @@ -1,7 +1,7 @@ #ifndef LAPACK_H #define LAPACK_H -#include "blas.h" +//#include "blas.h" #include "mkl_lapack.h" #endif diff --git a/src/NativeWrappers/Windows/ACMLWrapper/ACMKWrapper.vcxproj b/src/NativeWrappers/Windows/ACML/ACMLWrapper.vcxproj similarity index 97% rename from src/NativeWrappers/Windows/ACMLWrapper/ACMKWrapper.vcxproj rename to src/NativeWrappers/Windows/ACML/ACMLWrapper.vcxproj index adde35e4..e93d47fe 100644 --- a/src/NativeWrappers/Windows/ACMLWrapper/ACMKWrapper.vcxproj +++ b/src/NativeWrappers/Windows/ACML/ACMLWrapper.vcxproj @@ -29,23 +29,27 @@ DynamicLibrary true Unicode + v110 DynamicLibrary true Unicode + v110 DynamicLibrary false true Unicode + v110 DynamicLibrary false true Unicode + v110 diff --git a/src/NativeWrappers/Windows/ACMLWrapper/ACMKWrapper.vcxproj.filters b/src/NativeWrappers/Windows/ACMLWrapper/ACMKWrapper.vcxproj.filters deleted file mode 100644 index 30541be3..00000000 --- a/src/NativeWrappers/Windows/ACMLWrapper/ACMKWrapper.vcxproj.filters +++ /dev/null @@ -1,33 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hpp;hxx;hm;inl;inc;xsd - - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms - - - - - Resource Files - - - - - Source Files - - - Source Files - - - Source Files - - - \ No newline at end of file diff --git a/src/NativeWrappers/Windows/ACMLWrapperTests/ACMLWrapperTests.csproj b/src/NativeWrappers/Windows/ACMLWrapperTests/ACMLWrapperTests.csproj index c5e97dfb..2745705a 100644 --- a/src/NativeWrappers/Windows/ACMLWrapperTests/ACMLWrapperTests.csproj +++ b/src/NativeWrappers/Windows/ACMLWrapperTests/ACMLWrapperTests.csproj @@ -36,7 +36,7 @@ ..\..\..\..\out\debug\Net40\MathNet.Numerics.dll - ..\..\..\..\lib\NUnit.2.5.9\nunit.framework.dll + ..\..\..\..\packages\NUnit.2.6.2\lib\nunit.framework.dll diff --git a/src/NativeWrappers/Windows/MKL/MKLWrapper.vcxproj b/src/NativeWrappers/Windows/MKL/MKLWrapper.vcxproj index 31d75391..e2bbc0bb 100644 --- a/src/NativeWrappers/Windows/MKL/MKLWrapper.vcxproj +++ b/src/NativeWrappers/Windows/MKL/MKLWrapper.vcxproj @@ -25,23 +25,27 @@ DynamicLibrary + v110 MultiByte true Parallel DynamicLibrary + v110 MultiByte Parallel DynamicLibrary + v110 MultiByte true Parallel DynamicLibrary + v110 MultiByte Parallel @@ -62,32 +66,28 @@ - <_ProjectFileVersion>10.0.30319.1 - $(SolutionDir)$(Platform)\$(Configuration)\ - $(Platform)\$(Configuration)\ - $(SolutionDir)$(Platform)\$(Configuration)\ - $(Platform)\$(Configuration)\ - $(SolutionDir)$(Platform)\$(Configuration)\ - $(Platform)\$(Configuration)\ - $(SolutionDir)$(Platform)\$(Configuration)\ - $(Platform)\$(Configuration)\ - AllRules.ruleset - - - AllRules.ruleset - - - AllRules.ruleset - - - AllRules.ruleset - - + <_ProjectFileVersion>11.0.50727.1 + + + $(SolutionDir)$(Platform)\$(Configuration)\ + $(Platform)\$(Configuration)\ + + + $(SolutionDir)$(Platform)\$(Configuration)\ + $(Platform)\$(Configuration)\ + + + $(SolutionDir)$(Platform)\$(Configuration)\ + $(Platform)\$(Configuration)\ + + + $(SolutionDir)$(Platform)\$(Configuration)\ + $(Platform)\$(Configuration)\ Disabled - ..\..\Common;..\..\MKL;C:\Program Files (x86)\Intel\ComposerXE-2011\mkl\include; + $(ProjectDir)..\..\Common;$(ProjectDir)..\..\MKL;$(MKLIncludeDir);%(AdditionalIncludeDirectories) _WINDOWS;%(PreprocessorDefinitions) true EnableFastChecks @@ -99,12 +99,12 @@ mkl_intel_c.lib;mkl_intel_thread.lib;mkl_core.lib;libiomp5md.lib;%(AdditionalDependencies) $(OutDir)MathNET.Numerics.MKL.dll - C:\Program Files (x86)\Intel\ComposerXE-2011\mkl\lib\ia32;C:\Program Files (x86)\Intel\ComposerXE-2011\compiler\lib\ia32; + C:\Program Files (x86)\Intel\Compiler\11.1\046\lib\ia32;C:\Program Files (x86)\Intel\Compiler\11.1\046\mkl\ia32\lib;%(AdditionalLibraryDirectories) true MachineX86 - copy "C:\Program Files (x86)\Intel\Composer XE\redist\ia32\compiler\libiomp5md.dll" $(OutDir) + copy "$(CompilerPathForVC)\libiomp5md.dll" $(OutDir) @@ -113,7 +113,7 @@ Disabled - ..\..\Common;..\..\MKL;C:\Program Files (x86)\Intel\ComposerXE-2011\mkl\include; + $(ProjectDir)..\..\Common;$(ProjectDir)..\..\MKL;$(MKLIncludeDir);%(AdditionalIncludeDirectories) _WINDOWS;%(PreprocessorDefinitions) true EnableFastChecks @@ -125,19 +125,19 @@ mkl_intel_lp64.lib;mkl_intel_thread.lib;mkl_core.lib;libiomp5md.lib;%(AdditionalDependencies) $(OutDir)MathNET.Numerics.MKL.dll - C:\Program Files (x86)\Intel\ComposerXE-2011\mkl\lib\intel64;C:\Program Files (x86)\Intel\ComposerXE-2011\compiler\lib\intel64 + C:\Program Files (x86)\Intel\Compiler\11.1\046\lib\intel64;C:\Program Files (x86)\Intel\Compiler\11.1\046\mkl\em64t\lib;%(AdditionalLibraryDirectories) true MachineX64 - copy "C:\Program Files (x86)\Intel\Composer XE\redist\intel64\compiler\libiomp5md.dll" $(OutDir) + copy "$(CompilerPathForVC)\libiomp5md.dll" $(OutDir) MaxSpeed true - ..\..\Common;..\..\MKL;C:\Program Files (x86)\Intel\ComposerXE-2011\mkl\include; + $(ProjectDir)..\..\Common;$(ProjectDir)..\..\MKL;$(MKLIncludeDir);%(AdditionalIncludeDirectories) _WINDOWS;%(PreprocessorDefinitions) MultiThreaded true @@ -148,14 +148,14 @@ mkl_intel_c.lib;mkl_intel_thread.lib;mkl_core.lib;libiomp5md.lib;%(AdditionalDependencies) $(OutDir)MathNET.Numerics.MKL.dll - C:\Program Files (x86)\Intel\ComposerXE-2011\mkl\lib\ia32;C:\Program Files (x86)\Intel\ComposerXE-2011\compiler\lib\ia32; + C:\Program Files (x86)\Intel\Compiler\11.1\060\lib\ia32;C:\Program Files (x86)\Intel\Compiler\11.1\060\mkl\ia32\lib;%(AdditionalLibraryDirectories) true true true MachineX86 - copy "C:\Program Files (x86)\Intel\Composer XE\redist\ia32\compiler\libiomp5md.dll" $(OutDir) + copy "$(CompilerPathForVC)\libiomp5md.dll" $(OutDir) @@ -165,7 +165,7 @@ MaxSpeed true - ..\..\Common;..\..\MKL;C:\Program Files (x86)\Intel\ComposerXE-2011\mkl\include; + $(ProjectDir)..\..\Common;$(ProjectDir)..\..\MKL;$(MKLIncludeDir);%(AdditionalIncludeDirectories) _WINDOWS;%(PreprocessorDefinitions) MultiThreaded true @@ -176,26 +176,26 @@ mkl_intel_lp64.lib;mkl_intel_thread.lib;mkl_core.lib;libiomp5md.lib;%(AdditionalDependencies) $(OutDir)MathNET.Numerics.MKL.dll - C:\Program Files (x86)\Intel\ComposerXE-2011\mkl\lib\intel64;C:\Program Files (x86)\Intel\ComposerXE-2011\compiler\lib\intel64 + C:\Program Files (x86)\Intel\Compiler\11.1\060\lib\intel64;C:\Program Files (x86)\Intel\Compiler\11.1\060\mkl\em64t\lib;%(AdditionalLibraryDirectories) true true true MachineX64 - copy "C:\Program Files (x86)\Intel\Composer XE\redist\intel64\compiler\libiomp5md.dll" $(OutDir) + copy "$(CompilerPathForVC)\libiomp5md.dll" $(OutDir) - - - - + + + + diff --git a/src/NativeWrappers/Windows/MKL/MKLWrapper.vcxproj.filters b/src/NativeWrappers/Windows/MKL/MKLWrapper.vcxproj.filters index 5d4163bf..631d1787 100644 --- a/src/NativeWrappers/Windows/MKL/MKLWrapper.vcxproj.filters +++ b/src/NativeWrappers/Windows/MKL/MKLWrapper.vcxproj.filters @@ -15,17 +15,6 @@ - - Header Files - - - Header Files - - - - - Source Files - Source Files @@ -35,6 +24,17 @@ Source Files + + Source Files + + + + + Header Files + + + Header Files + diff --git a/src/NativeWrappers/Windows/MKLWrapperTests/MKLWrapperTests.csproj b/src/NativeWrappers/Windows/MKLWrapperTests/MKLWrapperTests.csproj index 19875a21..e8fe842c 100644 --- a/src/NativeWrappers/Windows/MKLWrapperTests/MKLWrapperTests.csproj +++ b/src/NativeWrappers/Windows/MKLWrapperTests/MKLWrapperTests.csproj @@ -59,8 +59,9 @@ False ..\..\..\..\out\debug\Net40\MathNet.Numerics.dll - - ..\..\..\..\lib\NUnit.2.5.9\nunit.framework.dll + + False + ..\..\..\..\packages\NUnit.2.6.2\lib\nunit.framework.dll diff --git a/src/NativeWrappers/Windows/NativeWrappers.sln b/src/NativeWrappers/Windows/NativeWrappers.sln index 0862f0ac..7d2b73a9 100644 --- a/src/NativeWrappers/Windows/NativeWrappers.sln +++ b/src/NativeWrappers/Windows/NativeWrappers.sln @@ -1,6 +1,6 @@  -Microsoft Visual Studio Solution File, Format Version 11.00 -# Visual Studio 2010 +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2012 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Common", "Common", "{5A0892FF-82CE-40FC-BCE1-73810C615F52}" ProjectSection(SolutionItems) = preProject ..\Common\resource.h = ..\Common\resource.h @@ -13,14 +13,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MKLWrapper", "MKL\MKLWrappe EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MKLWrapperTests", "MKLWrapperTests\MKLWrapperTests.csproj", "{D0AD591B-0CE6-4A6D-8DEA-01777EE09BC3}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GotoBLAS2Wrapper", "GotoBLAS2\GotoBLAS2Wrapper.vcxproj", "{507FF69E-32A6-495A-9DE2-20EC10EE8963}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GotoBLAS2WrapperTests", "GotoBLAS2WrapperTests\GotoBLAS2WrapperTests.csproj", "{56FFAB18-CAA6-4913-8123-610872BFD60A}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ACMLWrapper", "ACMLWrapper\ACMKWrapper.vcxproj", "{8774BCBE-27D0-44D2-A1B3-8ED705E252CB}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ACMLWrapperTests", "ACMLWrapperTests\ACMLWrapperTests.csproj", "{8A42A7F3-23C0-46D9-9DBA-B9039EB3C8EB}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -59,57 +51,6 @@ Global {D0AD591B-0CE6-4A6D-8DEA-01777EE09BC3}.Release|Mixed Platforms.Build.0 = Release|Any CPU {D0AD591B-0CE6-4A6D-8DEA-01777EE09BC3}.Release|Win32.ActiveCfg = Release|Any CPU {D0AD591B-0CE6-4A6D-8DEA-01777EE09BC3}.Release|x64.ActiveCfg = Release|Any CPU - {507FF69E-32A6-495A-9DE2-20EC10EE8963}.Debug|Any CPU.ActiveCfg = Debug|x64 - {507FF69E-32A6-495A-9DE2-20EC10EE8963}.Debug|Mixed Platforms.ActiveCfg = Debug|x64 - {507FF69E-32A6-495A-9DE2-20EC10EE8963}.Debug|Mixed Platforms.Build.0 = Debug|x64 - {507FF69E-32A6-495A-9DE2-20EC10EE8963}.Debug|Win32.ActiveCfg = Debug|Win32 - {507FF69E-32A6-495A-9DE2-20EC10EE8963}.Debug|Win32.Build.0 = Debug|Win32 - {507FF69E-32A6-495A-9DE2-20EC10EE8963}.Debug|x64.ActiveCfg = Debug|x64 - {507FF69E-32A6-495A-9DE2-20EC10EE8963}.Debug|x64.Build.0 = Debug|x64 - {507FF69E-32A6-495A-9DE2-20EC10EE8963}.Release|Any CPU.ActiveCfg = Release|x64 - {507FF69E-32A6-495A-9DE2-20EC10EE8963}.Release|Mixed Platforms.ActiveCfg = Release|x64 - {507FF69E-32A6-495A-9DE2-20EC10EE8963}.Release|Mixed Platforms.Build.0 = Release|x64 - {507FF69E-32A6-495A-9DE2-20EC10EE8963}.Release|Win32.ActiveCfg = Release|Win32 - {507FF69E-32A6-495A-9DE2-20EC10EE8963}.Release|Win32.Build.0 = Release|Win32 - {507FF69E-32A6-495A-9DE2-20EC10EE8963}.Release|x64.ActiveCfg = Release|x64 - {507FF69E-32A6-495A-9DE2-20EC10EE8963}.Release|x64.Build.0 = Release|x64 - {56FFAB18-CAA6-4913-8123-610872BFD60A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {56FFAB18-CAA6-4913-8123-610872BFD60A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {56FFAB18-CAA6-4913-8123-610872BFD60A}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {56FFAB18-CAA6-4913-8123-610872BFD60A}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {56FFAB18-CAA6-4913-8123-610872BFD60A}.Debug|Win32.ActiveCfg = Debug|Any CPU - {56FFAB18-CAA6-4913-8123-610872BFD60A}.Debug|x64.ActiveCfg = Debug|Any CPU - {56FFAB18-CAA6-4913-8123-610872BFD60A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {56FFAB18-CAA6-4913-8123-610872BFD60A}.Release|Any CPU.Build.0 = Release|Any CPU - {56FFAB18-CAA6-4913-8123-610872BFD60A}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {56FFAB18-CAA6-4913-8123-610872BFD60A}.Release|Win32.ActiveCfg = Release|Any CPU - {56FFAB18-CAA6-4913-8123-610872BFD60A}.Release|x64.ActiveCfg = Release|Any CPU - {8774BCBE-27D0-44D2-A1B3-8ED705E252CB}.Debug|Any CPU.ActiveCfg = Debug|x64 - {8774BCBE-27D0-44D2-A1B3-8ED705E252CB}.Debug|Mixed Platforms.ActiveCfg = Debug|x64 - {8774BCBE-27D0-44D2-A1B3-8ED705E252CB}.Debug|Mixed Platforms.Build.0 = Debug|x64 - {8774BCBE-27D0-44D2-A1B3-8ED705E252CB}.Debug|Win32.ActiveCfg = Debug|Win32 - {8774BCBE-27D0-44D2-A1B3-8ED705E252CB}.Debug|Win32.Build.0 = Debug|Win32 - {8774BCBE-27D0-44D2-A1B3-8ED705E252CB}.Debug|x64.ActiveCfg = Debug|x64 - {8774BCBE-27D0-44D2-A1B3-8ED705E252CB}.Debug|x64.Build.0 = Debug|x64 - {8774BCBE-27D0-44D2-A1B3-8ED705E252CB}.Release|Any CPU.ActiveCfg = Release|x64 - {8774BCBE-27D0-44D2-A1B3-8ED705E252CB}.Release|Mixed Platforms.ActiveCfg = Release|x64 - {8774BCBE-27D0-44D2-A1B3-8ED705E252CB}.Release|Mixed Platforms.Build.0 = Release|x64 - {8774BCBE-27D0-44D2-A1B3-8ED705E252CB}.Release|Win32.ActiveCfg = Release|Win32 - {8774BCBE-27D0-44D2-A1B3-8ED705E252CB}.Release|Win32.Build.0 = Release|Win32 - {8774BCBE-27D0-44D2-A1B3-8ED705E252CB}.Release|x64.ActiveCfg = Release|x64 - {8774BCBE-27D0-44D2-A1B3-8ED705E252CB}.Release|x64.Build.0 = Release|x64 - {8A42A7F3-23C0-46D9-9DBA-B9039EB3C8EB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8A42A7F3-23C0-46D9-9DBA-B9039EB3C8EB}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8A42A7F3-23C0-46D9-9DBA-B9039EB3C8EB}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {8A42A7F3-23C0-46D9-9DBA-B9039EB3C8EB}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {8A42A7F3-23C0-46D9-9DBA-B9039EB3C8EB}.Debug|Win32.ActiveCfg = Debug|Any CPU - {8A42A7F3-23C0-46D9-9DBA-B9039EB3C8EB}.Debug|x64.ActiveCfg = Debug|Any CPU - {8A42A7F3-23C0-46D9-9DBA-B9039EB3C8EB}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8A42A7F3-23C0-46D9-9DBA-B9039EB3C8EB}.Release|Any CPU.Build.0 = Release|Any CPU - {8A42A7F3-23C0-46D9-9DBA-B9039EB3C8EB}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {8A42A7F3-23C0-46D9-9DBA-B9039EB3C8EB}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {8A42A7F3-23C0-46D9-9DBA-B9039EB3C8EB}.Release|Win32.ActiveCfg = Release|Any CPU - {8A42A7F3-23C0-46D9-9DBA-B9039EB3C8EB}.Release|x64.ActiveCfg = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/Numerics/Algorithms/LinearAlgebra/Acml/AcmlLinearAlgebraProvider.Complex.cs b/src/Numerics/Algorithms/LinearAlgebra/Acml/AcmlLinearAlgebraProvider.Complex.cs index fa88f11d..163af11f 100644 --- a/src/Numerics/Algorithms/LinearAlgebra/Acml/AcmlLinearAlgebraProvider.Complex.cs +++ b/src/Numerics/Algorithms/LinearAlgebra/Acml/AcmlLinearAlgebraProvider.Complex.cs @@ -24,6 +24,8 @@ // OTHER DEALINGS IN THE SOFTWARE. // +using MathNet.Numerics.LinearAlgebra.Generic.Factorization; + namespace MathNet.Numerics.Algorithms.LinearAlgebra.Acml { using System; diff --git a/src/Numerics/Algorithms/LinearAlgebra/Acml/AcmlLinearAlgebraProvider.Complex32.cs b/src/Numerics/Algorithms/LinearAlgebra/Acml/AcmlLinearAlgebraProvider.Complex32.cs index 0cbbc22d..6b68c436 100644 --- a/src/Numerics/Algorithms/LinearAlgebra/Acml/AcmlLinearAlgebraProvider.Complex32.cs +++ b/src/Numerics/Algorithms/LinearAlgebra/Acml/AcmlLinearAlgebraProvider.Complex32.cs @@ -32,6 +32,8 @@ Last generated on UTC 2011-04-17 06:45:26Z */ +using MathNet.Numerics.LinearAlgebra.Generic.Factorization; + namespace MathNet.Numerics.Algorithms.LinearAlgebra.Acml { using System; diff --git a/src/Numerics/Algorithms/LinearAlgebra/Acml/AcmlLinearAlgebraProvider.double.cs b/src/Numerics/Algorithms/LinearAlgebra/Acml/AcmlLinearAlgebraProvider.double.cs index 66442c58..40083473 100644 --- a/src/Numerics/Algorithms/LinearAlgebra/Acml/AcmlLinearAlgebraProvider.double.cs +++ b/src/Numerics/Algorithms/LinearAlgebra/Acml/AcmlLinearAlgebraProvider.double.cs @@ -28,6 +28,8 @@ // OTHER DEALINGS IN THE SOFTWARE. // +using MathNet.Numerics.LinearAlgebra.Generic.Factorization; + namespace MathNet.Numerics.Algorithms.LinearAlgebra.Acml { using System; diff --git a/src/Numerics/Algorithms/LinearAlgebra/Acml/AcmlLinearAlgebraProvider.float.cs b/src/Numerics/Algorithms/LinearAlgebra/Acml/AcmlLinearAlgebraProvider.float.cs index 4e394c37..8a2f05e4 100644 --- a/src/Numerics/Algorithms/LinearAlgebra/Acml/AcmlLinearAlgebraProvider.float.cs +++ b/src/Numerics/Algorithms/LinearAlgebra/Acml/AcmlLinearAlgebraProvider.float.cs @@ -28,6 +28,8 @@ // OTHER DEALINGS IN THE SOFTWARE. // +using MathNet.Numerics.LinearAlgebra.Generic.Factorization; + namespace MathNet.Numerics.Algorithms.LinearAlgebra.Acml { using System; diff --git a/src/Numerics/Algorithms/LinearAlgebra/GotoBlas/GotoBlasLinearAlgebraProvider.Complex.cs b/src/Numerics/Algorithms/LinearAlgebra/GotoBlas/GotoBlasLinearAlgebraProvider.Complex.cs index 705a1d30..3a21567f 100644 --- a/src/Numerics/Algorithms/LinearAlgebra/GotoBlas/GotoBlasLinearAlgebraProvider.Complex.cs +++ b/src/Numerics/Algorithms/LinearAlgebra/GotoBlas/GotoBlasLinearAlgebraProvider.Complex.cs @@ -28,6 +28,8 @@ // OTHER DEALINGS IN THE SOFTWARE. // +using MathNet.Numerics.LinearAlgebra.Generic.Factorization; + namespace MathNet.Numerics.Algorithms.LinearAlgebra.GotoBlas { using System; @@ -541,7 +543,7 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.GotoBlas /// to be used by the QR solve routine. /// This is similar to the GEQRF and ORGQR LAPACK routines. [SecuritySafeCritical] - public override void QRFactor(Complex[] r, int rowsR, int columnsR, Complex[] q, Complex[] tau) + public override void QRFactor(Complex[] r, int rowsR, int columnsR, Complex[] q, Complex[] tau, QRMethod method = QRMethod.Full) { if (r == null) { @@ -747,8 +749,8 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.GotoBlas /// /// 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 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. /// Contains additional information on Q. Only used for the native solver @@ -814,7 +816,7 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.GotoBlas /// /// The Q matrix obtained by QR factor. This is only used for the managed provider and can be /// null for the native provider. The native provider uses the Q portion stored in the R matrix. - /// The R 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. /// Contains additional information on Q. Only used for the native solver diff --git a/src/Numerics/Algorithms/LinearAlgebra/GotoBlas/GotoBlasLinearAlgebraProvider.Complex32.cs b/src/Numerics/Algorithms/LinearAlgebra/GotoBlas/GotoBlasLinearAlgebraProvider.Complex32.cs index 45e8eeac..3afee23b 100644 --- a/src/Numerics/Algorithms/LinearAlgebra/GotoBlas/GotoBlasLinearAlgebraProvider.Complex32.cs +++ b/src/Numerics/Algorithms/LinearAlgebra/GotoBlas/GotoBlasLinearAlgebraProvider.Complex32.cs @@ -28,6 +28,8 @@ // OTHER DEALINGS IN THE SOFTWARE. // +using MathNet.Numerics.LinearAlgebra.Generic.Factorization; + namespace MathNet.Numerics.Algorithms.LinearAlgebra.GotoBlas { using System; @@ -540,7 +542,7 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.GotoBlas /// to be used by the QR solve routine. /// This is similar to the GEQRF and ORGQR LAPACK routines. [SecuritySafeCritical] - public override void QRFactor(Complex32[] r, int rowsR, int columnsR, Complex32[] q, Complex32[] tau) + public override void QRFactor(Complex32[] r, int rowsR, int columnsR, Complex32[] q, Complex32[] tau, QRMethod method = QRMethod.Full) { if (r == null) { @@ -746,8 +748,8 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.GotoBlas /// /// 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 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. /// Contains additional information on Q. Only used for the native solver @@ -813,7 +815,7 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.GotoBlas /// /// The Q matrix obtained by QR factor. This is only used for the managed provider and can be /// null for the native provider. The native provider uses the Q portion stored in the R matrix. - /// The R 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. /// Contains additional information on Q. Only used for the native solver diff --git a/src/Numerics/Algorithms/LinearAlgebra/GotoBlas/GotoBlasLinearAlgebraProvider.double.cs b/src/Numerics/Algorithms/LinearAlgebra/GotoBlas/GotoBlasLinearAlgebraProvider.double.cs index 29824847..76319b64 100644 --- a/src/Numerics/Algorithms/LinearAlgebra/GotoBlas/GotoBlasLinearAlgebraProvider.double.cs +++ b/src/Numerics/Algorithms/LinearAlgebra/GotoBlas/GotoBlasLinearAlgebraProvider.double.cs @@ -28,6 +28,8 @@ // OTHER DEALINGS IN THE SOFTWARE. // +using MathNet.Numerics.LinearAlgebra.Generic.Factorization; + namespace MathNet.Numerics.Algorithms.LinearAlgebra.GotoBlas { using System; @@ -540,7 +542,7 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.GotoBlas /// to be used by the QR solve routine. /// This is similar to the GEQRF and ORGQR LAPACK routines. [SecuritySafeCritical] - public override void QRFactor(double[] r, int rowsR, int columnsR, double[] q, double[] tau) + public override void QRFactor(double[] r, int rowsR, int columnsR, double[] q, double[] tau, QRMethod method = QRMethod.Full) { if (r == null) { @@ -746,8 +748,8 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.GotoBlas /// /// 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 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. /// Contains additional information on Q. Only used for the native solver @@ -813,7 +815,7 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.GotoBlas /// /// The Q matrix obtained by QR factor. This is only used for the managed provider and can be /// null for the native provider. The native provider uses the Q portion stored in the R matrix. - /// The R 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. /// Contains additional information on Q. Only used for the native solver diff --git a/src/Numerics/Algorithms/LinearAlgebra/GotoBlas/GotoBlasLinearAlgebraProvider.float.cs b/src/Numerics/Algorithms/LinearAlgebra/GotoBlas/GotoBlasLinearAlgebraProvider.float.cs index 5dc75c37..76b4e5d5 100644 --- a/src/Numerics/Algorithms/LinearAlgebra/GotoBlas/GotoBlasLinearAlgebraProvider.float.cs +++ b/src/Numerics/Algorithms/LinearAlgebra/GotoBlas/GotoBlasLinearAlgebraProvider.float.cs @@ -28,6 +28,8 @@ // OTHER DEALINGS IN THE SOFTWARE. // +using MathNet.Numerics.LinearAlgebra.Generic.Factorization; + namespace MathNet.Numerics.Algorithms.LinearAlgebra.GotoBlas { using System; @@ -540,7 +542,7 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.GotoBlas /// to be used by the QR solve routine. /// This is similar to the GEQRF and ORGQR LAPACK routines. [SecuritySafeCritical] - public override void QRFactor(float[] r, int rowsR, int columnsR, float[] q, float[] tau) + public override void QRFactor(float[] r, int rowsR, int columnsR, float[] q, float[] tau, QRMethod method = QRMethod.Full) { if (r == null) { @@ -746,8 +748,8 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.GotoBlas /// /// 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 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. /// Contains additional information on Q. Only used for the native solver @@ -813,7 +815,7 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.GotoBlas /// /// The Q matrix obtained by QR factor. This is only used for the managed provider and can be /// null for the native provider. The native provider uses the Q portion stored in the R matrix. - /// The R 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. /// Contains additional information on Q. Only used for the native solver diff --git a/src/Numerics/Algorithms/LinearAlgebra/ILinearAlgebraProviderOfT.cs b/src/Numerics/Algorithms/LinearAlgebra/ILinearAlgebraProviderOfT.cs index 8d4fdb85..a06f1af9 100644 --- a/src/Numerics/Algorithms/LinearAlgebra/ILinearAlgebraProviderOfT.cs +++ b/src/Numerics/Algorithms/LinearAlgebra/ILinearAlgebraProviderOfT.cs @@ -25,6 +25,9 @@ // // INITIAL DRAFT MISSING EXCEPTION SPECIFICATIONS + +using MathNet.Numerics.LinearAlgebra.Generic.Factorization; + namespace MathNet.Numerics.Algorithms.LinearAlgebra { using System.Numerics; @@ -313,27 +316,58 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra void CholeskySolveFactored(T[] a, int orderA, T[] b, int columnsB); /// - /// Computes the QR factorization of A. + /// Computes the full QR factorization of A. /// - /// On entry, it is the M by N A matrix to factor. On exit, + /// 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. /// A min(m,n) vector. On exit, contains additional information /// to be used by the QR solve routine. /// This is similar to the GEQRF and ORGQR LAPACK routines. - void QRFactor(T[] r, int rowsR, int columnsR, T[] q, T[] tau); + void QRFactor(T[] a, int rowsA, int columnsA, T[] q, T[] tau); /// - /// Computes the QR factorization of A. + /// Computes the full 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 + /// 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. + /// A min(m,n) vector. On exit, contains additional information + /// to be used by the QR solve routine. + /// 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[] a, int rowsA, int columnsA, T[] q, T[] tau, T[] work); + + /// + /// Computes the thin QR factorization of A where M > N. + /// + /// On entry, it is the M by N A matrix to factor. On exit, + /// it is overwritten with the Q matrix of the QR factorization. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. + /// On exit, A N by N matrix that holds the R matrix of the + /// QR factorization. + /// A min(m,n) vector. On exit, contains additional information + /// to be used by the QR solve routine. + /// This is similar to the GEQRF and ORGQR LAPACK routines. + void ThinQRFactor(T[] a, int rowsA, int columnsA, T[] r, T[] tau); + + /// + /// Computes the thin QR factorization of A where M > N. + /// + /// On entry, it is the M by N A matrix to factor. On exit, + /// it is overwritten with the Q matrix of the QR factorization. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. + /// On exit, A N by N matrix that holds the R matrix of the /// QR factorization. /// A min(m,n) vector. On exit, contains additional information /// to be used by the QR solve routine. @@ -341,7 +375,7 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra /// 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 rowsR, int columnsR, T[] q, T[] tau, T[] work); + void ThinQRFactor(T[] a, int rowsA, int columnsA, T[] r, T[] tau, T[] work); /// /// Solves A*X=B for X using QR factorization of A. @@ -352,8 +386,9 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra /// The B matrix. /// The number of columns of B. /// On exit, the solution matrix. + /// The type of QR factorization to perform. /// Rows must be greater or equal to columns. - void QRSolve(T[] a, int rows, int columns, T[] b, int columnsB, T[] x); + void QRSolve(T[] a, int rows, int columns, T[] b, int columnsB, T[] x, QRMethod method = QRMethod.Full); /// /// Solves A*X=B for X using QR factorization of A. @@ -367,8 +402,9 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra /// 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. + /// The type of QR factorization to perform. /// Rows must be greater or equal to columns. - void QRSolve(T[] a, int rows, int columns, T[] b, int columnsB, T[] x, T[] work); + void QRSolve(T[] a, int rows, int columns, T[] b, int columnsB, T[] x, T[] work, QRMethod method = QRMethod.Full); /// /// Solves A*X=B for X using a previously QR factored matrix. @@ -384,7 +420,8 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra /// The number of columns of B. /// On exit, the solution matrix. /// Rows must be greater or equal to columns. - void QRSolveFactored(T[] q, T[] r, int rowsR, int columnsR, T[] tau, T[] b, int columnsB, T[] x); + /// The type of QR factorization to perform. + void QRSolveFactored(T[] q, T[] r, int rowsR, int columnsR, T[] tau, T[] b, int columnsB, T[] x, QRMethod method = QRMethod.Full); /// /// Solves A*X=B for X using a previously QR factored matrix. @@ -403,7 +440,8 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra /// but should be N*blocksize. The blocksize is machine dependent. On exit, work[0] contains the optimal /// work size value. /// Rows must be greater or equal to columns. - void QRSolveFactored(T[] q, T[] r, int rowsR, int columnsR, T[] tau, T[] b, int columnsB, T[] x, T[] work); + /// The type of QR factorization to perform. + void QRSolveFactored(T[] q, T[] r, int rowsR, int columnsR, T[] tau, T[] b, int columnsB, T[] x, T[] work, QRMethod method = QRMethod.Full); /// /// Computes the singular value decomposition of A. diff --git a/src/Numerics/Algorithms/LinearAlgebra/ManagedLinearAlgebraProvider.Complex.cs b/src/Numerics/Algorithms/LinearAlgebra/ManagedLinearAlgebraProvider.Complex.cs index 5b84df9d..3a0ff630 100644 --- a/src/Numerics/Algorithms/LinearAlgebra/ManagedLinearAlgebraProvider.Complex.cs +++ b/src/Numerics/Algorithms/LinearAlgebra/ManagedLinearAlgebraProvider.Complex.cs @@ -23,6 +23,9 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // OTHER DEALINGS IN THE SOFTWARE. // + +using MathNet.Numerics.LinearAlgebra.Generic.Factorization; + namespace MathNet.Numerics.Algorithms.LinearAlgebra { using System; @@ -1482,7 +1485,7 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, "rowsR * rowsR"), "q"); } - var work = new Complex[rowsR * rowsR]; + var work = columnsR > rowsR ? new Complex[rowsR * rowsR] : new Complex[rowsR * columnsR]; QRFactor(r, rowsR, columnsR, q, tau, work); } @@ -1533,10 +1536,21 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, "rowsR * rowsR"), "q"); } - if (work.Length < rowsR * rowsR) + if (columnsR > rowsR) { - work[0] = rowsR * rowsR; - throw new ArgumentException(Resources.WorkArrayTooSmall, "work"); + if (work.Length < rowsR * rowsR) + { + work[0] = rowsR * rowsR; + throw new ArgumentException(Resources.WorkArrayTooSmall, "work"); + } + } + else + { + if (work.Length < rowsR * columnsR) + { + work[0] = rowsR * columnsR; + throw new ArgumentException(Resources.WorkArrayTooSmall, "work"); + } } CommonParallel.For(0, rowsR, i => q[(i * rowsR) + i] = Complex.One); @@ -1553,9 +1567,139 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra ComputeQR(work, i, q, i, rowsR, i, rowsR, Control.NumberOfParallelWorkerThreads); } - work[0] = rowsR * rowsR; + work[0] = columnsR > rowsR ? rowsR * rowsR : rowsR * columnsR; + } + + /// + /// 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 Q matrix of the QR factorization. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. + /// On exit, A N by N matrix that holds the R matrix of the + /// QR factorization. + /// A min(m,n) vector. On exit, contains additional information + /// to be used by the QR solve routine. + /// This is similar to the GEQRF and ORGQR LAPACK routines. + public virtual void ThinQRFactor(Complex[] a, int rowsA, int columnsA, Complex[] r, Complex[] tau) + { + if (r == null) + { + throw new ArgumentNullException("r"); + } + + if (a == null) + { + throw new ArgumentNullException("a"); + } + + if (a.Length != rowsA * columnsA) + { + throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, "rowsR * columnsR"), "a"); + } + + if (tau.Length < Math.Min(rowsA, columnsA)) + { + throw new ArgumentException(string.Format(Resources.ArrayTooSmall, "min(m,n)"), "tau"); + } + + if (r.Length != columnsA * columnsA) + { + throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, "columnsA * columnsA"), "r"); + } + + var work = new Complex[rowsA * columnsA]; + ThinQRFactor(a, rowsA, columnsA, r, tau, work); } + /// + /// Computes the QR factorization of A where M > N. + /// + /// On entry, it is the M by N A matrix to factor. On exit, + /// it is overwritten with the Q matrix of the QR factorization. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. + /// On exit, A N by N matrix that holds the R matrix of the + /// QR factorization. + /// A min(m,n) vector. On exit, contains additional information + /// to be used by the QR solve routine. + /// 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 virtual void ThinQRFactor(Complex[] a, int rowsA, int columnsA, Complex[] r, Complex[] tau, Complex[] work) + { + if (r == null) + { + throw new ArgumentNullException("r"); + } + + if (a == null) + { + throw new ArgumentNullException("q"); + } + + if (work == null) + { + throw new ArgumentNullException("q"); + } + + if (a.Length != rowsA * columnsA) + { + throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, "rowsR * columnsR"), "a"); + } + + if (tau.Length < Math.Min(rowsA, columnsA)) + { + throw new ArgumentException(string.Format(Resources.ArrayTooSmall, "min(m,n)"), "tau"); + } + + if (r.Length != columnsA * columnsA) + { + throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, "columnsA * columnsA"), "r"); + } + + if (work.Length < rowsA * columnsA) + { + work[0] = rowsA * columnsA; + throw new ArgumentException(Resources.WorkArrayTooSmall, "work"); + } + + var minmn = Math.Min(rowsA, columnsA); + for (var i = 0; i < minmn; i++) + { + GenerateColumn(work, a, rowsA, i, i); + ComputeQR(work, i, a, i, rowsA, i + 1, columnsA, Control.NumberOfParallelWorkerThreads); + } + + //copy R + for (var j = 0; j < columnsA; j++) + { + var rIndex = j * columnsA; + var aIndex = j * rowsA; + for (var i = 0; i < columnsA; i++) + { + r[rIndex + i] = a[aIndex + i]; + } + } + + //clear A and set diagonals to 1 + Array.Clear(a, 0, a.Length); + for (var i = 0; i < columnsA; i++) + { + a[i * rowsA + i] = Complex.One; + } + + for (var i = minmn - 1; i >= 0; i--) + { + ComputeQR(work, i, a, i, rowsA, i, columnsA, Control.NumberOfParallelWorkerThreads); + } + + work[0] = rowsA * columnsA; + } + + #region QR Factor Helper functions /// @@ -1667,46 +1811,12 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra /// The B matrix. /// The number of columns of B. /// On exit, the solution matrix. + /// The type of QR factorization to perform. /// Rows must be greater or equal to columns. - public virtual void QRSolve(Complex[] a, int rows, int columns, Complex[] b, int columnsB, Complex[] x) + public virtual void QRSolve(Complex[] a, int rows, int columns, Complex[] b, int columnsB, Complex[] x, QRMethod method = QRMethod.Full) { - if (a == null) - { - throw new ArgumentNullException("a"); - } - - if (b == null) - { - throw new ArgumentNullException("b"); - } - - if (x == null) - { - throw new ArgumentNullException("x"); - } - - if (a.Length != rows * columns) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "a"); - } - - if (b.Length != rows * columnsB) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); - } - - if (x.Length != columns * columnsB) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "x"); - } - - if (rows < columns) - { - throw new ArgumentException(Resources.RowsLessThanColumns); - } - - var work = new Complex[rows * rows]; - QRSolve(a, rows, columns, b, columnsB, x, work); + var work = new Complex[rows * columns]; + QRSolve(a, rows, columns, b, columnsB, x, work, method); } /// @@ -1721,8 +1831,9 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra /// 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. + /// The type of QR factorization to perform. /// Rows must be greater or equal to columns. - public virtual void QRSolve(Complex[] a, int rows, int columns, Complex[] b, int columnsB, Complex[] x, Complex[] work) + public virtual void QRSolve(Complex[] a, int rows, int columns, Complex[] b, int columnsB, Complex[] x, Complex[] work, QRMethod method = QRMethod.Full) { if (a == null) { @@ -1764,19 +1875,29 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra throw new ArgumentException(Resources.ArgumentArraysSameLength, "x"); } - if (work.Length < rows * rows) + if (work.Length < rows * columns) { - work[0] = rows * rows; + work[0] = rows * columns; throw new ArgumentException(Resources.WorkArrayTooSmall, "work"); } var clone = new Complex[a.Length]; a.Copy(clone); - var q = new Complex[rows * rows]; - QRFactor(clone, rows, columns, q, work); - QRSolveFactored(q, clone, rows, columns, null, b, columnsB, x); - work[0] = rows * rows; + if (method == QRMethod.Full) + { + var q = new Complex[rows * rows]; + QRFactor(clone, rows, columns, q, work); + QRSolveFactored(q, clone, rows, columns, null, b, columnsB, x, method); + } + else + { + var r = new Complex[columns * columns]; + ThinQRFactor(clone, rows, columns, r, work); + QRSolveFactored(clone, r, rows, columns, null, b, columnsB, x, method); + } + + work[0] = rows * columns; } /// @@ -1795,10 +1916,11 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra /// The work array - only used in the native provider. 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. + /// The type of QR factorization to perform. /// Rows must be greater or equal to columns. - public virtual void QRSolveFactored(Complex[] q, Complex[] r, int rowsR, int columnsR, Complex[] tau, Complex[] b, int columnsB, Complex[] x, Complex[] work) + public virtual void QRSolveFactored(Complex[] q, Complex[] r, int rowsR, int columnsR, Complex[] tau, Complex[] b, int columnsB, Complex[] x, Complex[] work, QRMethod method = QRMethod.Full) { - QRSolveFactored(q, r, rowsR, columnsR, tau, b, columnsB, x); + QRSolveFactored(q, r, rowsR, columnsR, tau, b, columnsB, x, method); } /// @@ -1806,15 +1928,16 @@ 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. /// Contains additional information on Q. Only used for the native solver /// and can be null for the managed provider. /// The B matrix. /// The number of columns of B. /// On exit, the solution matrix. + /// The type of QR factorization to perform. /// Rows must be greater or equal to columns. - public virtual void QRSolveFactored(Complex[] q, Complex[] r, int rowsR, int columnsR, Complex[] tau, Complex[] b, int columnsB, Complex[] x) + public virtual void QRSolveFactored(Complex[] q, Complex[] r, int rowsA, int columnsA, Complex[] tau, Complex[] b, int columnsB, Complex[] x, QRMethod method = QRMethod.Full) { if (r == null) { @@ -1836,50 +1959,63 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra throw new ArgumentNullException("q"); } - if (r.Length != rowsR * columnsR) + if (rowsA < columnsA) { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "r"); + throw new ArgumentException(Resources.RowsLessThanColumns); } - if (q.Length != rowsR * rowsR) + int rowsQ, columnsQ, rowsR, columnsR; + if( method == QRMethod.Full) { - throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, "rowsR * rowsR"), "q"); + rowsQ = columnsQ = rowsR = rowsA; + columnsR = columnsA; + } + else + { + rowsQ = rowsA; + columnsQ = rowsR = columnsR = columnsA; + } + + if (r.Length != rowsR * columnsR) + { + throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, rowsR * columnsR), "r"); } - if (b.Length != rowsR * columnsB) + if (q.Length != rowsQ * columnsQ) { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); + throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, rowsQ * columnsQ), "q"); } - if (x.Length != columnsR * columnsB) + if (b.Length != rowsA * columnsB) { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "x"); + throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, rowsA * columnsB), "b"); } - if (rowsR < columnsR) + if (x.Length != columnsA * columnsB) { - throw new ArgumentException(Resources.RowsLessThanColumns); + throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, columnsA * columnsB), "x"); } var sol = new Complex[b.Length]; // Copy B matrix to "sol", so B data will not be changed - CommonParallel.For(0, b.Length, index => sol[index] = b[index]); + Array.Copy(b, sol, b.Length); // Compute Y = transpose(Q)*B - var column = new Complex[rowsR]; + var column = new Complex[rowsA]; for (var j = 0; j < columnsB; j++) { - var jm = j * rowsR; - CommonParallel.For(0, rowsR, k => column[k] = sol[jm + k]); + var jm = j * rowsA; + CommonParallel.For(0, rowsA, k => column[k] = sol[jm + k]); CommonParallel.For( - 0, - rowsR, + 0, + columnsA, i => { - var im = i * rowsR; + var im = i * rowsA; + var sum = Complex.Zero; - for (var k = 0; k < rowsR; k++) + for (var k = 0; k < rowsA; k++) { sum += q[im + k].Conjugate() * column[k]; } @@ -1889,19 +2025,19 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra } // Solve R*X = Y; - for (var k = columnsR - 1; k >= 0; k--) + for (var k = columnsA - 1; k >= 0; k--) { var km = k * rowsR; for (var j = 0; j < columnsB; j++) { - sol[(j * rowsR) + k] /= r[km + k]; + sol[(j * rowsA) + k] /= r[km + k]; } for (var i = 0; i < k; i++) { for (var j = 0; j < columnsB; j++) { - var jm = j * rowsR; + var jm = j * rowsA; sol[jm + i] -= sol[jm + k] * r[km + i]; } } @@ -1909,16 +2045,16 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra // Fill result matrix CommonParallel.For( - 0, - columnsR, + 0, + columnsR, row => { for (var col = 0; col < columnsB; col++) { - x[(col * columnsR) + row] = sol[row + (col * rowsR)]; + x[(col * columnsA) + row] = sol[row + (col * rowsA)]; } }); - } + } /// /// Computes the singular value decomposition of A. diff --git a/src/Numerics/Algorithms/LinearAlgebra/ManagedLinearAlgebraProvider.Complex32.cs b/src/Numerics/Algorithms/LinearAlgebra/ManagedLinearAlgebraProvider.Complex32.cs index 61c955e4..728caf75 100644 --- a/src/Numerics/Algorithms/LinearAlgebra/ManagedLinearAlgebraProvider.Complex32.cs +++ b/src/Numerics/Algorithms/LinearAlgebra/ManagedLinearAlgebraProvider.Complex32.cs @@ -23,6 +23,10 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // OTHER DEALINGS IN THE SOFTWARE. // + +using System.Numerics; +using MathNet.Numerics.LinearAlgebra.Generic.Factorization; + namespace MathNet.Numerics.Algorithms.LinearAlgebra { using System; @@ -1478,7 +1482,7 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, "rowsR * rowsR"), "q"); } - var work = new Complex32[rowsR * rowsR]; + var work = columnsR > rowsR ? new Complex32 [rowsR * rowsR] : new Complex32[rowsR * columnsR]; QRFactor(r, rowsR, columnsR, q, tau, work); } @@ -1529,10 +1533,21 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, "rowsR * rowsR"), "q"); } - if (work.Length < rowsR * rowsR) + if (columnsR > rowsR) { - work[0] = rowsR * rowsR; - throw new ArgumentException(Resources.WorkArrayTooSmall, "work"); + if (work.Length < rowsR * rowsR) + { + work[0] = rowsR * rowsR; + throw new ArgumentException(Resources.WorkArrayTooSmall, "work"); + } + } + else + { + if (work.Length < rowsR * columnsR) + { + work[0] = rowsR * columnsR; + throw new ArgumentException(Resources.WorkArrayTooSmall, "work"); + } } CommonParallel.For(0, rowsR, i => q[(i * rowsR) + i] = Complex32.One); @@ -1549,9 +1564,139 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra ComputeQR(work, i, q, i, rowsR, i, rowsR, Control.NumberOfParallelWorkerThreads); } - work[0] = rowsR * rowsR; + work[0] = columnsR > rowsR ? rowsR * rowsR : rowsR * columnsR; + } + + /// + /// 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 Q matrix of the QR factorization. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. + /// On exit, A N by N matrix that holds the R matrix of the + /// QR factorization. + /// A min(m,n) vector. On exit, contains additional information + /// to be used by the QR solve routine. + /// This is similar to the GEQRF and ORGQR LAPACK routines. + public virtual void ThinQRFactor(Complex32[] a, int rowsA, int columnsA, Complex32[] r, Complex32[] tau) + { + if (r == null) + { + throw new ArgumentNullException("r"); + } + + if (a == null) + { + throw new ArgumentNullException("a"); + } + + if (a.Length != rowsA * columnsA) + { + throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, "rowsR * columnsR"), "a"); + } + + if (tau.Length < Math.Min(rowsA, columnsA)) + { + throw new ArgumentException(string.Format(Resources.ArrayTooSmall, "min(m,n)"), "tau"); + } + + if (r.Length != columnsA * columnsA) + { + throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, "columnsA * columnsA"), "r"); + } + + var work = new Complex32[rowsA * columnsA]; + ThinQRFactor(a, rowsA, columnsA, r, tau, work); + } + + /// + /// Computes the QR factorization of A where M > N. + /// + /// On entry, it is the M by N A matrix to factor. On exit, + /// it is overwritten with the Q matrix of the QR factorization. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. + /// On exit, A N by N matrix that holds the R matrix of the + /// QR factorization. + /// A min(m,n) vector. On exit, contains additional information + /// to be used by the QR solve routine. + /// 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 virtual void ThinQRFactor(Complex32[] a, int rowsA, int columnsA, Complex32[] r, Complex32[] tau, Complex32[] work) + { + if (r == null) + { + throw new ArgumentNullException("r"); + } + + if (a == null) + { + throw new ArgumentNullException("q"); + } + + if (work == null) + { + throw new ArgumentNullException("q"); + } + + if (a.Length != rowsA * columnsA) + { + throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, "rowsR * columnsR"), "a"); + } + + if (tau.Length < Math.Min(rowsA, columnsA)) + { + throw new ArgumentException(string.Format(Resources.ArrayTooSmall, "min(m,n)"), "tau"); + } + + if (r.Length != columnsA * columnsA) + { + throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, "columnsA * columnsA"), "r"); + } + + if (work.Length < rowsA * columnsA) + { + work[0] = rowsA * columnsA; + throw new ArgumentException(Resources.WorkArrayTooSmall, "work"); + } + + var minmn = Math.Min(rowsA, columnsA); + for (var i = 0; i < minmn; i++) + { + GenerateColumn(work, a, rowsA, i, i); + ComputeQR(work, i, a, i, rowsA, i + 1, columnsA, Control.NumberOfParallelWorkerThreads); + } + + //copy R + for (var j = 0; j < columnsA; j++) + { + var rIndex = j * columnsA; + var aIndex = j * rowsA; + for (var i = 0; i < columnsA; i++) + { + r[rIndex + i] = a[aIndex + i]; + } + } + + //clear A and set diagonals to 1 + Array.Clear(a, 0, a.Length); + for (var i = 0; i < columnsA; i++) + { + a[i * rowsA + i] = Complex32.One; + } + + for (var i = minmn - 1; i >= 0; i--) + { + ComputeQR(work, i, a, i, rowsA, i, columnsA, Control.NumberOfParallelWorkerThreads); + } + + work[0] = rowsA * columnsA; } + #region QR Factor Helper functions /// @@ -1663,46 +1808,12 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra /// The B matrix. /// The number of columns of B. /// On exit, the solution matrix. + /// The type of QR factorization to perform. /// Rows must be greater or equal to columns. - public virtual void QRSolve(Complex32[] a, int rows, int columns, Complex32[] b, int columnsB, Complex32[] x) + public virtual void QRSolve(Complex32[] a, int rows, int columns, Complex32[] b, int columnsB, Complex32[] x, QRMethod method = QRMethod.Full) { - if (a == null) - { - throw new ArgumentNullException("a"); - } - - if (b == null) - { - throw new ArgumentNullException("b"); - } - - if (x == null) - { - throw new ArgumentNullException("x"); - } - - if (a.Length != rows * columns) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "a"); - } - - if (b.Length != rows * columnsB) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); - } - - if (x.Length != columns * columnsB) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "x"); - } - - if (rows < columns) - { - throw new ArgumentException(Resources.RowsLessThanColumns); - } - - var work = new Complex32[rows * rows]; - QRSolve(a, rows, columns, b, columnsB, x, work); + var work = new Complex32[rows * columns]; + QRSolve(a, rows, columns, b, columnsB, x, work, method); } /// @@ -1717,8 +1828,9 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra /// 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. + /// The type of QR factorization to perform. /// Rows must be greater or equal to columns. - public virtual void QRSolve(Complex32[] a, int rows, int columns, Complex32[] b, int columnsB, Complex32[] x, Complex32[] work) + public virtual void QRSolve(Complex32[] a, int rows, int columns, Complex32[] b, int columnsB, Complex32[] x, Complex32[] work, QRMethod method = QRMethod.Full) { if (a == null) { @@ -1760,19 +1872,29 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra throw new ArgumentException(Resources.RowsLessThanColumns); } - if (work.Length < rows * rows) + if (work.Length < rows * columns) { - work[0] = rows * rows; + work[0] = rows * columns; throw new ArgumentException(Resources.WorkArrayTooSmall, "work"); } var clone = new Complex32[a.Length]; a.Copy(clone); - var q = new Complex32[rows * rows]; - QRFactor(clone, rows, columns, q, work); - QRSolveFactored(q, clone, rows, columns, null, b, columnsB, x); - work[0] = rows * rows; + if (method == QRMethod.Full) + { + var q = new Complex32[rows * rows]; + QRFactor(clone, rows, columns, q, work); + QRSolveFactored(q, clone, rows, columns, null, b, columnsB, x, method); + } + else + { + var r = new Complex32[columns * columns]; + ThinQRFactor(clone, rows, columns, r, work); + QRSolveFactored(clone, r, rows, columns, null, b, columnsB, x, method); + } + + work[0] = rows * columns; } /// @@ -1791,10 +1913,11 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra /// The work array - only used in the native provider. 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. + /// The type of QR factorization to perform. /// Rows must be greater or equal to columns. - public virtual void QRSolveFactored(Complex32[] q, Complex32[] r, int rowsR, int columnsR, Complex32[] tau, Complex32[] b, int columnsB, Complex32[] x, Complex32[] work) + public virtual void QRSolveFactored(Complex32[] q, Complex32[] r, int rowsR, int columnsR, Complex32[] tau, Complex32[] b, int columnsB, Complex32[] x, Complex32[] work, QRMethod method = QRMethod.Full) { - QRSolveFactored(q, r, rowsR, columnsR, tau, b, columnsB, x); + QRSolveFactored(q, r, rowsR, columnsR, tau, b, columnsB, x, method); } /// @@ -1802,15 +1925,16 @@ 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. /// Contains additional information on Q. Only used for the native solver /// and can be null for the managed provider. /// The B matrix. /// The number of columns of B. /// On exit, the solution matrix. + /// The type of QR factorization to perform. /// Rows must be greater or equal to columns. - public virtual void QRSolveFactored(Complex32[] q, Complex32[] r, int rowsR, int columnsR, Complex32[] tau, Complex32[] b, int columnsB, Complex32[] x) + public virtual void QRSolveFactored(Complex32[] q, Complex32[] r, int rowsA, int columnsA, Complex32[] tau, Complex32[] b, int columnsB, Complex32[] x, QRMethod method = QRMethod.Full) { if (r == null) { @@ -1832,50 +1956,63 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra throw new ArgumentNullException("q"); } - if (r.Length != rowsR * columnsR) + if (rowsA < columnsA) { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "r"); + throw new ArgumentException(Resources.RowsLessThanColumns); } - if (q.Length != rowsR * rowsR) + int rowsQ, columnsQ, rowsR, columnsR; + if (method == QRMethod.Full) { - throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, "rowsR * rowsR"), "q"); + rowsQ = columnsQ = rowsR = rowsA; + columnsR = columnsA; + } + else + { + rowsQ = rowsA; + columnsQ = rowsR = columnsR = columnsA; } - if (b.Length != rowsR * columnsB) + if (r.Length != rowsR * columnsR) { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); + throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, rowsR * columnsR), "r"); } - if (x.Length != columnsR * columnsB) + if (q.Length != rowsQ * columnsQ) { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "x"); + throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, rowsQ * columnsQ), "q"); } - if (rowsR < columnsR) + if (b.Length != rowsA * columnsB) { - throw new ArgumentException(Resources.RowsLessThanColumns); + throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, rowsA * columnsB), "b"); + } + + if (x.Length != columnsA * columnsB) + { + throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, columnsA * columnsB), "x"); } var sol = new Complex32[b.Length]; // Copy B matrix to "sol", so B data will not be changed - CommonParallel.For(0, b.Length, index => sol[index] = b[index]); + Array.Copy(b, sol, b.Length); // Compute Y = transpose(Q)*B - var column = new Complex32[rowsR]; + var column = new Complex32[rowsA]; for (var j = 0; j < columnsB; j++) { - var jm = j * rowsR; - CommonParallel.For(0, rowsR, k => column[k] = sol[jm + k]); + var jm = j * rowsA; + CommonParallel.For(0, rowsA, k => column[k] = sol[jm + k]); CommonParallel.For( 0, - rowsR, + columnsA, i => { - var im = i * rowsR; + var im = i * rowsA; + var sum = Complex32.Zero; - for (var k = 0; k < rowsR; k++) + for (var k = 0; k < rowsA; k++) { sum += q[im + k].Conjugate() * column[k]; } @@ -1885,19 +2022,19 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra } // Solve R*X = Y; - for (var k = columnsR - 1; k >= 0; k--) + for (var k = columnsA - 1; k >= 0; k--) { var km = k * rowsR; for (var j = 0; j < columnsB; j++) { - sol[(j * rowsR) + k] /= r[km + k]; + sol[(j * rowsA) + k] /= r[km + k]; } for (var i = 0; i < k; i++) { for (var j = 0; j < columnsB; j++) { - var jm = j * rowsR; + var jm = j * rowsA; sol[jm + i] -= sol[jm + k] * r[km + i]; } } @@ -1911,7 +2048,7 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra { for (var col = 0; col < columnsB; col++) { - x[(col * columnsR) + row] = sol[row + (col * rowsR)]; + x[(col * columnsA) + row] = sol[row + (col * rowsA)]; } }); } diff --git a/src/Numerics/Algorithms/LinearAlgebra/ManagedLinearAlgebraProvider.Double.cs b/src/Numerics/Algorithms/LinearAlgebra/ManagedLinearAlgebraProvider.Double.cs index aabfc893..860d886d 100644 --- a/src/Numerics/Algorithms/LinearAlgebra/ManagedLinearAlgebraProvider.Double.cs +++ b/src/Numerics/Algorithms/LinearAlgebra/ManagedLinearAlgebraProvider.Double.cs @@ -23,6 +23,9 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // OTHER DEALINGS IN THE SOFTWARE. // + +using MathNet.Numerics.LinearAlgebra.Generic.Factorization; + namespace MathNet.Numerics.Algorithms.LinearAlgebra { using System; @@ -1339,6 +1342,7 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra /// QR factorization. /// A min(m,n) vector. On exit, contains additional information /// to be used by the QR solve routine. + /// The type of QR factorization to perform. /// This is similar to the GEQRF and ORGQR LAPACK routines. public virtual void QRFactor(double[] r, int rowsR, int columnsR, double[] q, double[] tau) { @@ -1367,7 +1371,8 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, "rowsR * rowsR"), "q"); } - var work = new double[rowsR * rowsR]; + + var work = columnsR > rowsR ? new double[rowsR * rowsR] : new double[rowsR * columnsR]; QRFactor(r, rowsR, columnsR, q, tau, work); } @@ -1418,10 +1423,21 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, "rowsR * rowsR"), "q"); } - if (work.Length < rowsR * rowsR) + if (columnsR > rowsR) { - work[0] = rowsR * rowsR; - throw new ArgumentException(Resources.WorkArrayTooSmall, "work"); + if (work.Length < rowsR * rowsR) + { + work[0] = rowsR * rowsR; + throw new ArgumentException(Resources.WorkArrayTooSmall, "work"); + } + } + else + { + if (work.Length < rowsR * columnsR) + { + work[0] = rowsR * columnsR; + throw new ArgumentException(Resources.WorkArrayTooSmall, "work"); + } } CommonParallel.For(0, rowsR, i => q[(i * rowsR) + i] = 1.0); @@ -1438,7 +1454,136 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra ComputeQR(work, i, q, i, rowsR, i, rowsR, Control.NumberOfParallelWorkerThreads); } - work[0] = rowsR * rowsR; + work[0] = columnsR > rowsR ? rowsR * rowsR : rowsR * columnsR; + } + + /// + /// 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 Q matrix of the QR factorization. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. + /// On exit, A N by N matrix that holds the R matrix of the + /// QR factorization. + /// A min(m,n) vector. On exit, contains additional information + /// to be used by the QR solve routine. + /// This is similar to the GEQRF and ORGQR LAPACK routines. + public virtual void ThinQRFactor(double[] a, int rowsA, int columnsA, double[] r, double[] tau) + { + if (r == null) + { + throw new ArgumentNullException("r"); + } + + if (a == null) + { + throw new ArgumentNullException("a"); + } + + if (a.Length != rowsA * columnsA) + { + throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, "rowsR * columnsR"), "a"); + } + + if (tau.Length < Math.Min(rowsA, columnsA)) + { + throw new ArgumentException(string.Format(Resources.ArrayTooSmall, "min(m,n)"), "tau"); + } + + if (r.Length != columnsA * columnsA) + { + throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, "columnsA * columnsA"), "r"); + } + + var work = new double[rowsA * columnsA]; + ThinQRFactor(a, rowsA, columnsA, r, tau, work); + } + + /// + /// Computes the thin QR factorization of A where M > N. + /// + /// On entry, it is the M by N A matrix to factor. On exit, + /// it is overwritten with the Q matrix of the QR factorization. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. + /// On exit, A N by N matrix that holds the R matrix of the + /// QR factorization. + /// A min(m,n) vector. On exit, contains additional information + /// to be used by the QR solve routine. + /// 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 virtual void ThinQRFactor(double[] a, int rowsA, int columnsA, double[] r, double[] tau, double[] work) + { + if (r == null) + { + throw new ArgumentNullException("r"); + } + + if (a == null) + { + throw new ArgumentNullException("q"); + } + + if (work == null) + { + throw new ArgumentNullException("q"); + } + + if (a.Length != rowsA * columnsA) + { + throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, "rowsR * columnsR"), "a"); + } + + if (tau.Length < Math.Min(rowsA, columnsA)) + { + throw new ArgumentException(string.Format(Resources.ArrayTooSmall, "min(m,n)"), "tau"); + } + + if (r.Length != columnsA * columnsA) + { + throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, "columnsA * columnsA"), "r"); + } + + if (work.Length < rowsA * columnsA) + { + work[0] = rowsA*columnsA; + throw new ArgumentException(Resources.WorkArrayTooSmall, "work"); + } + + var minmn = Math.Min(rowsA, columnsA); + for (var i = 0; i < minmn; i++) + { + GenerateColumn(work, a, rowsA, i, i); + ComputeQR(work, i, a, i, rowsA, i + 1, columnsA, Control.NumberOfParallelWorkerThreads); + } + + //copy R + for (var j = 0; j < columnsA; j++ ) + { + var rIndex = j * columnsA; + var aIndex = j * rowsA; + for (var i = 0; i < columnsA; i++) + { + r[rIndex + i] = a[aIndex+i]; + } + } + + //clear A and set diagonals to 1 + Array.Clear(a, 0, a.Length); + for (var i = 0; i < columnsA; i++ ) + { + a[i * rowsA + i] = 1.0; + } + + for (var i = minmn - 1; i >= 0; i--) + { + ComputeQR(work, i, a, i, rowsA, i, columnsA, Control.NumberOfParallelWorkerThreads); + } + + work[0] = rowsA * columnsA; } #region QR Factor Helper functions @@ -1553,46 +1698,12 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra /// The B matrix. /// The number of columns of B. /// On exit, the solution matrix. + /// The type of QR factorization to perform. /// Rows must be greater or equal to columns. - public virtual void QRSolve(double[] a, int rows, int columns, double[] b, int columnsB, double[] x) + public virtual void QRSolve(double[] a, int rows, int columns, double[] b, int columnsB, double[] x, QRMethod method = QRMethod.Full) { - if (a == null) - { - throw new ArgumentNullException("a"); - } - - if (b == null) - { - throw new ArgumentNullException("b"); - } - - if (x == null) - { - throw new ArgumentNullException("x"); - } - - if (a.Length != rows * columns) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "a"); - } - - if (b.Length != rows * columnsB) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); - } - - if (x.Length != columns * columnsB) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "x"); - } - - if (rows < columns) - { - throw new ArgumentException(Resources.RowsLessThanColumns); - } - - var work = new double[rows * rows]; - QRSolve(a, rows, columns, b, columnsB, x, work); + var work = new double[rows * columns]; + QRSolve(a, rows, columns, b, columnsB, x, work, method); } /// @@ -1607,8 +1718,9 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra /// 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. + /// The type of QR factorization to perform. /// Rows must be greater or equal to columns. - public virtual void QRSolve(double[] a, int rows, int columns, double[] b, int columnsB, double[] x, double[] work) + public virtual void QRSolve(double[] a, int rows, int columns, double[] b, int columnsB, double[] x, double[] work, QRMethod method = QRMethod.Full) { if (a == null) { @@ -1650,19 +1762,28 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra throw new ArgumentException(Resources.RowsLessThanColumns); } - if (work.Length < rows * rows) + if (work.Length < rows * columns) { - work[0] = rows * rows; + work[0] = rows * columns; throw new ArgumentException(Resources.WorkArrayTooSmall, "work"); } var clone = new double[a.Length]; - a.Copy(clone); - var q = new double[rows * rows]; - QRFactor(clone, rows, columns, q, work); - QRSolveFactored(q, clone, rows, columns, null, b, columnsB, x); + a.Copy(clone); + + if (method == QRMethod.Full) + { + var q = new double[rows * rows]; + QRFactor(clone, rows, columns, q, work); + QRSolveFactored(q, clone, rows, columns, null, b, columnsB, x, method); + } else + { + var r = new double[columns * columns]; + ThinQRFactor(clone, rows, columns, r, work); + QRSolveFactored(clone, r, rows, columns, null, b, columnsB, x, method); + } - work[0] = rows * rows; + work[0] = rows * columns; } /// @@ -1671,8 +1792,8 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra /// The Q matrix obtained by QR factor. This is only used for the managed provider and can be /// null for the native provider. The native provider uses the Q portion stored in the R matrix. /// 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. /// Contains additional information on Q. Only used for the native solver /// and can be null for the managed provider. /// On entry the B matrix; on exit the X matrix. @@ -1681,10 +1802,11 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra /// The work array - only used in the native provider. 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. + /// The type of QR factorization to perform. /// Rows must be greater or equal to columns. - public virtual void QRSolveFactored(double[] q, double[] r, int rowsR, int columnsR, double[] tau, double[] b, int columnsB, double[] x, double[] work) + public virtual void QRSolveFactored(double[] q, double[] r, int rowsA, int columnsA, double[] tau, double[] b, int columnsB, double[] x, double[] work, QRMethod method = QRMethod.Full) { - QRSolveFactored(q, r, rowsR, columnsR, tau, b, columnsB, x); + QRSolveFactored(q, r, rowsA, columnsA, tau, b, columnsB, x, method); } /// @@ -1692,15 +1814,16 @@ 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. /// Contains additional information on Q. Only used for the native solver /// and can be null for the managed provider. /// The B matrix. /// The number of columns of B. /// On exit, the solution matrix. + /// The type of QR factorization to perform. /// Rows must be greater or equal to columns. - public virtual void QRSolveFactored(double[] q, double[] r, int rowsR, int columnsR, double[] tau, double[] b, int columnsB, double[] x) + public virtual void QRSolveFactored(double[] q, double[] r, int rowsA, int columnsA, double[] tau, double[] b, int columnsB, double[] x, QRMethod method = QRMethod.Full) { if (r == null) { @@ -1722,29 +1845,41 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra throw new ArgumentNullException("q"); } - if (r.Length != rowsR * columnsR) + if (rowsA < columnsA) { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "r"); + throw new ArgumentException(Resources.RowsLessThanColumns); } - if (q.Length != rowsR * rowsR) + int rowsQ, columnsQ, rowsR, columnsR; + if( method == QRMethod.Full) { - throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, "rowsR * rowsR"), "q"); + rowsQ = columnsQ = rowsR = rowsA; + columnsR = columnsA; + } + else + { + rowsQ = rowsA; + columnsQ = rowsR = columnsR = columnsA; } - if (b.Length != rowsR * columnsB) + if (r.Length != rowsR * columnsR) { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); + throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, rowsR * columnsR), "r"); } - if (x.Length != columnsR * columnsB) + if (q.Length != rowsQ * columnsQ) { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "x"); + throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, rowsQ * columnsQ), "q"); + } + + if (b.Length != rowsA * columnsB) + { + throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, rowsA * columnsB), "b"); } - if (rowsR < columnsR) + if (x.Length != columnsA * columnsB) { - throw new ArgumentException(Resources.RowsLessThanColumns); + throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, columnsA * columnsB), "x"); } var sol = new double[b.Length]; @@ -1753,20 +1888,20 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra Buffer.BlockCopy(b, 0, sol, 0, b.Length * Constants.SizeOfDouble); // Compute Y = transpose(Q)*B - var column = new double[rowsR]; + var column = new double[rowsA]; for (var j = 0; j < columnsB; j++) { - var jm = j * rowsR; - CommonParallel.For(0, rowsR, k => column[k] = sol[jm + k]); + var jm = j * rowsA; + CommonParallel.For(0, rowsA, k => column[k] = sol[jm + k]); CommonParallel.For( 0, - rowsR, + columnsA, i => { - var im = i * rowsR; + var im = i * rowsA; var sum = 0.0; - for (var k = 0; k < rowsR; k++) + for (var k = 0; k < rowsA; k++) { sum += q[im + k] * column[k]; } @@ -1776,19 +1911,19 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra } // Solve R*X = Y; - for (var k = columnsR - 1; k >= 0; k--) + for (var k = columnsA - 1; k >= 0; k--) { var km = k * rowsR; for (var j = 0; j < columnsB; j++) { - sol[(j * rowsR) + k] /= r[km + k]; + sol[(j * rowsA) + k] /= r[km + k]; } for (var i = 0; i < k; i++) { for (var j = 0; j < columnsB; j++) { - var jm = j * rowsR; + var jm = j * rowsA; sol[jm + i] -= sol[jm + k] * r[km + i]; } } @@ -1802,7 +1937,7 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra { for (var col = 0; col < columnsB; col++) { - x[(col * columnsR) + row] = sol[row + (col * rowsR)]; + x[(col * columnsA) + row] = sol[row + (col * rowsA)]; } }); } diff --git a/src/Numerics/Algorithms/LinearAlgebra/ManagedLinearAlgebraProvider.Single.cs b/src/Numerics/Algorithms/LinearAlgebra/ManagedLinearAlgebraProvider.Single.cs index 1a6b3162..925c1791 100644 --- a/src/Numerics/Algorithms/LinearAlgebra/ManagedLinearAlgebraProvider.Single.cs +++ b/src/Numerics/Algorithms/LinearAlgebra/ManagedLinearAlgebraProvider.Single.cs @@ -23,6 +23,9 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // OTHER DEALINGS IN THE SOFTWARE. // + +using MathNet.Numerics.LinearAlgebra.Generic.Factorization; + namespace MathNet.Numerics.Algorithms.LinearAlgebra { using System; @@ -1368,7 +1371,7 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, "rowsR * rowsR"), "q"); } - var work = new float[rowsR * rowsR]; + var work = columnsR > rowsR ? new float[rowsR * rowsR] : new float[rowsR * columnsR]; QRFactor(r, rowsR, columnsR, q, tau, work); } @@ -1419,10 +1422,21 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, "rowsR * rowsR"), "q"); } - if (work.Length < rowsR * rowsR) + if (columnsR > rowsR) { - work[0] = rowsR * rowsR; - throw new ArgumentException(Resources.WorkArrayTooSmall, "work"); + if (work.Length < rowsR * rowsR) + { + work[0] = rowsR * rowsR; + throw new ArgumentException(Resources.WorkArrayTooSmall, "work"); + } + } + else + { + if (work.Length < rowsR * columnsR) + { + work[0] = rowsR * columnsR; + throw new ArgumentException(Resources.WorkArrayTooSmall, "work"); + } } CommonParallel.For(0, rowsR, i => q[(i * rowsR) + i] = 1.0f); @@ -1439,9 +1453,139 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra ComputeQR(work, i, q, i, rowsR, i, rowsR, Control.NumberOfParallelWorkerThreads); } - work[0] = rowsR * rowsR; + work[0] = columnsR > rowsR ? rowsR * rowsR : rowsR * columnsR; } + /// + /// 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 Q matrix of the QR factorization. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. + /// On exit, A N by N matrix that holds the R matrix of the + /// QR factorization. + /// A min(m,n) vector. On exit, contains additional information + /// to be used by the QR solve routine. + /// This is similar to the GEQRF and ORGQR LAPACK routines. + public virtual void ThinQRFactor(float[] a, int rowsA, int columnsA, float[] r, float[] tau) + { + if (r == null) + { + throw new ArgumentNullException("r"); + } + + if (a == null) + { + throw new ArgumentNullException("a"); + } + + if (a.Length != rowsA * columnsA) + { + throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, "rowsR * columnsR"), "a"); + } + + if (tau.Length < Math.Min(rowsA, columnsA)) + { + throw new ArgumentException(string.Format(Resources.ArrayTooSmall, "min(m,n)"), "tau"); + } + + if (r.Length != columnsA * columnsA) + { + throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, "columnsA * columnsA"), "r"); + } + + var work = new float[rowsA * columnsA]; + ThinQRFactor(a, rowsA, columnsA, r, tau, work); + } + + /// + /// Computes the QR factorization of A where M > N. + /// + /// On entry, it is the M by N A matrix to factor. On exit, + /// it is overwritten with the Q matrix of the QR factorization. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. + /// On exit, A N by N matrix that holds the R matrix of the + /// QR factorization. + /// A min(m,n) vector. On exit, contains additional information + /// to be used by the QR solve routine. + /// 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 virtual void ThinQRFactor(float[] a, int rowsA, int columnsA, float[] r, float[] tau, float[] work) + { + if (r == null) + { + throw new ArgumentNullException("r"); + } + + if (a == null) + { + throw new ArgumentNullException("q"); + } + + if (work == null) + { + throw new ArgumentNullException("q"); + } + + if (a.Length != rowsA * columnsA) + { + throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, "rowsR * columnsR"), "a"); + } + + if (tau.Length < Math.Min(rowsA, columnsA)) + { + throw new ArgumentException(string.Format(Resources.ArrayTooSmall, "min(m,n)"), "tau"); + } + + if (r.Length != columnsA * columnsA) + { + throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, "columnsA * columnsA"), "r"); + } + + if (work.Length < rowsA * columnsA) + { + work[0] = rowsA * columnsA; + throw new ArgumentException(Resources.WorkArrayTooSmall, "work"); + } + + var minmn = Math.Min(rowsA, columnsA); + for (var i = 0; i < minmn; i++) + { + GenerateColumn(work, a, rowsA, i, i); + ComputeQR(work, i, a, i, rowsA, i + 1, columnsA, Control.NumberOfParallelWorkerThreads); + } + + //copy R + for (var j = 0; j < columnsA; j++) + { + var rIndex = j * columnsA; + var aIndex = j * rowsA; + for (var i = 0; i < columnsA; i++) + { + r[rIndex + i] = a[aIndex + i]; + } + } + + //clear A and set diagonals to 1 + Array.Clear(a, 0, a.Length); + for (var i = 0; i < columnsA; i++) + { + a[i * rowsA + i] = 1.0f; + } + + for (var i = minmn - 1; i >= 0; i--) + { + ComputeQR(work, i, a, i, rowsA, i, columnsA, Control.NumberOfParallelWorkerThreads); + } + + work[0] = rowsA * columnsA; + } + + #region QR Factor Helper functions /// @@ -1554,46 +1698,12 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra /// The B matrix. /// The number of columns of B. /// On exit, the solution matrix. + /// The type of QR factorization to perform. /// Rows must be greater or equal to columns. - public virtual void QRSolve(float[] a, int rows, int columns, float[] b, int columnsB, float[] x) + public virtual void QRSolve(float[] a, int rows, int columns, float[] b, int columnsB, float[] x, QRMethod method = QRMethod.Full) { - if (a == null) - { - throw new ArgumentNullException("a"); - } - - if (b == null) - { - throw new ArgumentNullException("b"); - } - - if (x == null) - { - throw new ArgumentNullException("x"); - } - - if (a.Length != rows * columns) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "a"); - } - - if (b.Length != rows * columnsB) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); - } - - if (x.Length != columns * columnsB) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "x"); - } - - if (rows < columns) - { - throw new ArgumentException(Resources.RowsLessThanColumns); - } - - var work = new float[rows * rows]; - QRSolve(a, rows, columns, b, columnsB, x, work); + var work = new float[rows * columns]; + QRSolve(a, rows, columns, b, columnsB, x, work, method); } /// @@ -1608,8 +1718,9 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra /// 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. + /// The type of QR factorization to perform. /// Rows must be greater or equal to columns. - public virtual void QRSolve(float[] a, int rows, int columns, float[] b, int columnsB, float[] x, float[] work) + public virtual void QRSolve(float[] a, int rows, int columns, float[] b, int columnsB, float[] x, float[] work, QRMethod method = QRMethod.Full) { if (a == null) { @@ -1651,19 +1762,29 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra throw new ArgumentException(Resources.RowsLessThanColumns); } - if (work.Length < rows * rows) + if (work.Length < rows * columns) { - work[0] = rows * rows; + work[0] = rows * columns; throw new ArgumentException(Resources.WorkArrayTooSmall, "work"); } var clone = new float[a.Length]; - a.Copy(clone); - var q = new float[rows * rows]; - QRFactor(clone, rows, columns, q, work); - QRSolveFactored(q, clone, rows, columns, null, b, columnsB, x); + a.Copy(clone); - work[0] = rows * rows; + if (method == QRMethod.Full) + { + var q = new float[rows * rows]; + QRFactor(clone, rows, columns, q, work); + QRSolveFactored(q, clone, rows, columns, null, b, columnsB, x, method); + } + else + { + var r = new float[columns * columns]; + ThinQRFactor(clone, rows, columns, r, work); + QRSolveFactored(clone, r, rows, columns, null, b, columnsB, x, method); + } + + work[0] = rows * columns; } /// @@ -1672,8 +1793,8 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra /// The Q matrix obtained by QR factor. This is only used for the managed provider and can be /// null for the native provider. The native provider uses the Q portion stored in the R matrix. /// 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. /// Contains additional information on Q. Only used for the native solver /// and can be null for the managed provider. /// On entry the B matrix; on exit the X matrix. @@ -1682,9 +1803,11 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra /// The work array - only used in the native provider. 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 virtual void QRSolveFactored(float[] q, float[] r, int rowsR, int columnsR, float[] tau, float[] b, int columnsB, float[] x, float[] work) + /// The type of QR factorization to perform. + /// Rows must be greater or equal to columns. + public virtual void QRSolveFactored(float[] q, float[] r, int rowsA, int columnsA, float[] tau, float[] b, int columnsB, float[] x, float[] work, QRMethod method = QRMethod.Full) { - QRSolveFactored(q, r, rowsR, columnsR, tau, b, columnsB, x); + QRSolveFactored(q, r, rowsA, columnsA, tau, b, columnsB, x, method); } /// @@ -1692,15 +1815,16 @@ 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. /// Contains additional information on Q. Only used for the native solver /// and can be null for the managed provider. /// The B matrix. /// The number of columns of B. /// On exit, the solution matrix. + /// The type of QR factorization to perform. /// Rows must be greater or equal to columns. - public virtual void QRSolveFactored(float[] q, float[] r, int rowsR, int columnsR, float[] tau, float[] b, int columnsB, float[] x) + public virtual void QRSolveFactored(float[] q, float[] r, int rowsA, int columnsA, float[] tau, float[] b, int columnsB, float[] x, QRMethod method = QRMethod.Full) { if (r == null) { @@ -1722,29 +1846,41 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra throw new ArgumentNullException("q"); } - if (r.Length != rowsR * columnsR) + if (rowsA < columnsA) { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "r"); + throw new ArgumentException(Resources.RowsLessThanColumns); } - if (q.Length != rowsR * rowsR) + int rowsQ, columnsQ, rowsR, columnsR; + if (method == QRMethod.Full) { - throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, "rowsR * rowsR"), "q"); + rowsQ = columnsQ = rowsR = rowsA; + columnsR = columnsA; + } + else + { + rowsQ = rowsA; + columnsQ = rowsR = columnsR = columnsA; } - if (b.Length != rowsR * columnsB) + if (r.Length != rowsR * columnsR) { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); + throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, rowsR * columnsR), "r"); } - if (x.Length != columnsR * columnsB) + if (q.Length != rowsQ * columnsQ) { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "x"); + throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, rowsQ * columnsQ), "q"); + } + + if (b.Length != rowsA * columnsB) + { + throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, rowsA * columnsB), "b"); } - if (rowsR < columnsR) + if (x.Length != columnsA * columnsB) { - throw new ArgumentException(Resources.RowsLessThanColumns); + throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, columnsA * columnsB), "x"); } var sol = new float[b.Length]; @@ -1753,20 +1889,20 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra Buffer.BlockCopy(b, 0, sol, 0, b.Length * Constants.SizeOfFloat); // Compute Y = transpose(Q)*B - var column = new float[rowsR]; + var column = new float[rowsA]; for (var j = 0; j < columnsB; j++) { - var jm = j * rowsR; - CommonParallel.For(0, rowsR, k => column[k] = sol[jm + k]); + var jm = j * rowsA; + CommonParallel.For(0, rowsA, k => column[k] = sol[jm + k]); CommonParallel.For( 0, - rowsR, + columnsA, i => { - var im = i * rowsR; + var im = i * rowsA; var sum = 0.0f; - for (var k = 0; k < rowsR; k++) + for (var k = 0; k < rowsA; k++) { sum += q[im + k] * column[k]; } @@ -1776,19 +1912,19 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra } // Solve R*X = Y; - for (var k = columnsR - 1; k >= 0; k--) + for (var k = columnsA - 1; k >= 0; k--) { var km = k * rowsR; for (var j = 0; j < columnsB; j++) { - sol[(j * rowsR) + k] /= r[km + k]; + sol[(j * rowsA) + k] /= r[km + k]; } for (var i = 0; i < k; i++) { for (var j = 0; j < columnsB; j++) { - var jm = j * rowsR; + var jm = j * rowsA; sol[jm + i] -= sol[jm + k] * r[km + i]; } } @@ -1802,7 +1938,7 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra { for (var col = 0; col < columnsB; col++) { - x[(col * columnsR) + row] = sol[row + (col * rowsR)]; + x[(col * columnsA) + row] = sol[row + (col * rowsA)]; } }); } diff --git a/src/Numerics/Algorithms/LinearAlgebra/Mkl/MklLinearAlgebraProvider.Complex.cs b/src/Numerics/Algorithms/LinearAlgebra/Mkl/MklLinearAlgebraProvider.Complex.cs index f3fd66e5..5ac470e4 100644 --- a/src/Numerics/Algorithms/LinearAlgebra/Mkl/MklLinearAlgebraProvider.Complex.cs +++ b/src/Numerics/Algorithms/LinearAlgebra/Mkl/MklLinearAlgebraProvider.Complex.cs @@ -28,6 +28,8 @@ // OTHER DEALINGS IN THE SOFTWARE. // +using MathNet.Numerics.LinearAlgebra.Generic.Factorization; + namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl { using System; @@ -666,46 +668,13 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl /// The B matrix. /// The number of columns of B. /// On exit, the solution matrix. + /// The type of QR factorization to perform. /// Rows must be greater or equal to columns. - public override void QRSolve(Complex[] a, int rows, int columns, Complex[] b, int columnsB, Complex[] x) + [SecuritySafeCritical] + public override void QRSolve(Complex[] a, int rows, int columns, Complex[] b, int columnsB, Complex[] x, QRMethod method = QRMethod.Full) { - if (a == null) - { - throw new ArgumentNullException("a"); - } - - if (b == null) - { - throw new ArgumentNullException("b"); - } - - if (x == null) - { - throw new ArgumentNullException("x"); - } - - if (a.Length != rows * columns) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "a"); - } - - if (b.Length != rows * columnsB) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); - } - - if (x.Length != columns * columnsB) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "x"); - } - - if (rows < columns) - { - throw new ArgumentException(Resources.RowsLessThanColumns); - } - var work = new Complex[columns * Control.BlockSize]; - QRSolve(a, rows, columns, b, columnsB, x, work); + QRSolve(a, rows, columns, b, columnsB, x, work, method); } /// @@ -720,8 +689,10 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl /// 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. + /// The type of QR factorization to perform. /// Rows must be greater or equal to columns. - public override void QRSolve(Complex[] a, int rows, int columns, Complex[] b, int columnsB, Complex[] x, Complex[] work) + [SecuritySafeCritical] + public override void QRSolve(Complex[] a, int rows, int columns, Complex[] b, int columnsB, Complex[] x, Complex[] work, QRMethod method = QRMethod.Full) { if (a == null) { @@ -769,7 +740,14 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl throw new ArgumentException(Resources.WorkArrayTooSmall, "work"); } - SafeNativeMethods.z_qr_solve(rows, columns, columnsB, a, b, x, work, work.Length); + if (method == QRMethod.Full) + { + SafeNativeMethods.z_qr_solve(rows, columns, columnsB, a, b, x, work, work.Length); + } + else + { + SafeNativeMethods.z_thin_qr_solve(rows, columns, columnsB, a, b, x, work, work.Length); + } } /// @@ -784,57 +762,13 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl /// The B matrix. /// The number of columns of B. /// On exit, the solution matrix. + /// The type of QR factorization to perform. /// Rows must be greater or equal to columns. [SecuritySafeCritical] - public override void QRSolveFactored(Complex[] q, Complex[] r, int rowsR, int columnsR, Complex[] tau, Complex[] b, int columnsB, Complex[] x) + public override void QRSolveFactored(Complex[] q, Complex[] r, int rowsR, int columnsR, Complex[] tau, Complex[] b, int columnsB, Complex[] x, QRMethod method = QRMethod.Full) { - if (r == null) - { - throw new ArgumentNullException("r"); - } - - if (q == null) - { - throw new ArgumentNullException("q"); - } - - if (b == null) - { - 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"); - } - - if (rowsR < columnsR) - { - throw new ArgumentException(Resources.RowsLessThanColumns); - } - var work = new Complex[columnsR * Control.BlockSize]; - QRSolveFactored(q, r, rowsR, columnsR, tau, b, columnsB, x, work); + QRSolveFactored(q, r, rowsR, columnsR, tau, b, columnsB, x, work, method); } /// @@ -843,8 +777,8 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl /// The Q matrix obtained by QR factor. This is only used for the managed provider and can be /// null for the native provider. The native provider uses the Q portion stored in the R matrix. /// 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. /// Contains additional information on Q. Only used for the native solver /// and can be null for the managed provider. /// On entry the B matrix; on exit the X matrix. @@ -853,8 +787,10 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl /// The work array - only used in the native provider. 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. + /// The type of QR factorization to perform. /// Rows must be greater or equal to columns. - public override void QRSolveFactored(Complex[] q, Complex[] r, int rowsR, int columnsR, Complex[] tau, Complex[] b, int columnsB, Complex[] x, Complex[] work) + [SecuritySafeCritical] + public override void QRSolveFactored(Complex[] q, Complex[] r, int rowsA, int columnsA, Complex[] tau, Complex[] b, int columnsB, Complex[] x, Complex[] work, QRMethod method = QRMethod.Full) { if (r == null) { @@ -881,38 +817,54 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl throw new ArgumentNullException("work"); } - if (r.Length != rowsR * columnsR) + int rowsQ, columnsQ, rowsR, columnsR; + if (method == QRMethod.Full) { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "r"); + rowsQ = columnsQ = rowsR = rowsA; + columnsR = columnsA; + } + else + { + rowsQ = rowsA; + columnsQ = rowsR = columnsR = columnsA; } - if (q.Length != rowsR * rowsR) + if (r.Length != rowsR * columnsR) { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "q"); + throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, rowsR * columnsR), "r"); } - if (b.Length != rowsR * columnsB) + if (q.Length != rowsQ * columnsQ) { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); + throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, rowsQ * columnsQ), "q"); } - if (x.Length != columnsR * columnsB) + if (b.Length != rowsA * columnsB) { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "x"); + throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, rowsA * columnsB), "b"); } - if (rowsR < columnsR) + if (x.Length != columnsA * columnsB) { - throw new ArgumentException(Resources.RowsLessThanColumns); + throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, columnsA * columnsB), "x"); } if (work.Length < 1) { - work[0] = rowsR * Control.BlockSize; + work[0] = rowsA * Control.BlockSize; throw new ArgumentException(Resources.WorkArrayTooSmall, "work"); } - SafeNativeMethods.z_qr_solve_factored(rowsR, columnsR, columnsB, r, b, tau, x, work, work.Length); + if (method == QRMethod.Full) + { + SafeNativeMethods.z_qr_solve_factored(rowsA, columnsA, columnsB, r, b, tau, x, work, work.Length); + } + else + { + // we don't have access to the raw Q matrix any more(it is stored in R in the full QR), need to think about this. + // let just call the managed version in the meantime. The heavy lifting has already been done. -marcus + base.QRSolveFactored(q, r, rowsA, columnsA, tau, b, columnsB, x, QRMethod.Thin); + } } /// diff --git a/src/Numerics/Algorithms/LinearAlgebra/Mkl/MklLinearAlgebraProvider.Complex32.cs b/src/Numerics/Algorithms/LinearAlgebra/Mkl/MklLinearAlgebraProvider.Complex32.cs index 95fba8cf..d1a0433c 100644 --- a/src/Numerics/Algorithms/LinearAlgebra/Mkl/MklLinearAlgebraProvider.Complex32.cs +++ b/src/Numerics/Algorithms/LinearAlgebra/Mkl/MklLinearAlgebraProvider.Complex32.cs @@ -28,6 +28,8 @@ // OTHER DEALINGS IN THE SOFTWARE. // +using MathNet.Numerics.LinearAlgebra.Generic.Factorization; + namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl { using System; @@ -665,46 +667,13 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl /// The B matrix. /// The number of columns of B. /// On exit, the solution matrix. + /// The type of QR factorization to perform. /// Rows must be greater or equal to columns. - public override void QRSolve(Complex32[] a, int rows, int columns, Complex32[] b, int columnsB, Complex32[] x) + [SecuritySafeCritical] + public override void QRSolve(Complex32[] a, int rows, int columns, Complex32[] b, int columnsB, Complex32[] x, QRMethod method = QRMethod.Full) { - if (a == null) - { - throw new ArgumentNullException("a"); - } - - if (b == null) - { - throw new ArgumentNullException("b"); - } - - if (x == null) - { - throw new ArgumentNullException("x"); - } - - if (a.Length != rows * columns) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "a"); - } - - if (b.Length != rows * columnsB) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); - } - - if (x.Length != columns * columnsB) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "x"); - } - - if (rows < columns) - { - throw new ArgumentException(Resources.RowsLessThanColumns); - } - var work = new Complex32[columns * Control.BlockSize]; - QRSolve(a, rows, columns, b, columnsB, x, work); + QRSolve(a, rows, columns, b, columnsB, x, work, method); } /// @@ -719,8 +688,10 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl /// 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. + /// The type of QR factorization to perform. /// Rows must be greater or equal to columns. - public override void QRSolve(Complex32[] a, int rows, int columns, Complex32[] b, int columnsB, Complex32[] x, Complex32[] work) + [SecuritySafeCritical] + public override void QRSolve(Complex32[] a, int rows, int columns, Complex32[] b, int columnsB, Complex32[] x, Complex32[] work, QRMethod method = QRMethod.Full) { if (a == null) { @@ -768,7 +739,14 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl throw new ArgumentException(Resources.WorkArrayTooSmall, "work"); } - SafeNativeMethods.c_qr_solve(rows, columns, columnsB, a, b, x, work, work.Length); + if (method == QRMethod.Full) + { + SafeNativeMethods.c_qr_solve(rows, columns, columnsB, a, b, x, work, work.Length); + } + else + { + SafeNativeMethods.c_thin_qr_solve(rows, columns, columnsB, a, b, x, work, work.Length); + } } /// @@ -783,57 +761,13 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl /// The B matrix. /// The number of columns of B. /// On exit, the solution matrix. + /// The type of QR factorization to perform. /// Rows must be greater or equal to columns. [SecuritySafeCritical] - public override void QRSolveFactored(Complex32[] q, Complex32[] r, int rowsR, int columnsR, Complex32[] tau, Complex32[] b, int columnsB, Complex32[] x) + public override void QRSolveFactored(Complex32[] q, Complex32[] r, int rowsR, int columnsR, Complex32[] tau, Complex32[] b, int columnsB, Complex32[] x, QRMethod method = QRMethod.Full) { - if (r == null) - { - throw new ArgumentNullException("r"); - } - - if (q == null) - { - throw new ArgumentNullException("q"); - } - - if (b == null) - { - 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"); - } - - if (rowsR < columnsR) - { - throw new ArgumentException(Resources.RowsLessThanColumns); - } - var work = new Complex32[columnsR * Control.BlockSize]; - QRSolveFactored(q, r, rowsR, columnsR, tau, b, columnsB, x, work); + QRSolveFactored(q, r, rowsR, columnsR, tau, b, columnsB, x, work, method); } /// @@ -842,8 +776,8 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl /// The Q matrix obtained by QR factor. This is only used for the managed provider and can be /// null for the native provider. The native provider uses the Q portion stored in the R matrix. /// 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. /// Contains additional information on Q. Only used for the native solver /// and can be null for the managed provider. /// On entry the B matrix; on exit the X matrix. @@ -852,8 +786,10 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl /// The work array - only used in the native provider. 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. + /// The type of QR factorization to perform. /// Rows must be greater or equal to columns. - public override void QRSolveFactored(Complex32[] q, Complex32[] r, int rowsR, int columnsR, Complex32[] tau, Complex32[] b, int columnsB, Complex32[] x, Complex32[] work) + [SecuritySafeCritical] + public override void QRSolveFactored(Complex32[] q, Complex32[] r, int rowsA, int columnsA, Complex32[] tau, Complex32[] b, int columnsB, Complex32[] x, Complex32[] work, QRMethod method = QRMethod.Full) { if (r == null) { @@ -880,38 +816,54 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl throw new ArgumentNullException("work"); } - if (r.Length != rowsR * columnsR) + int rowsQ, columnsQ, rowsR, columnsR; + if (method == QRMethod.Full) { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "r"); + rowsQ = columnsQ = rowsR = rowsA; + columnsR = columnsA; + } + else + { + rowsQ = rowsA; + columnsQ = rowsR = columnsR = columnsA; } - if (q.Length != rowsR * rowsR) + if (r.Length != rowsR * columnsR) { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "q"); + throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, rowsR * columnsR), "r"); } - if (b.Length != rowsR * columnsB) + if (q.Length != rowsQ * columnsQ) { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); + throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, rowsQ * columnsQ), "q"); } - if (x.Length != columnsR * columnsB) + if (b.Length != rowsA * columnsB) { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "x"); + throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, rowsA * columnsB), "b"); } - if (rowsR < columnsR) + if (x.Length != columnsA * columnsB) { - throw new ArgumentException(Resources.RowsLessThanColumns); + throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, columnsA * columnsB), "x"); } if (work.Length < 1) { - work[0] = rowsR * Control.BlockSize; + work[0] = rowsA * Control.BlockSize; throw new ArgumentException(Resources.WorkArrayTooSmall, "work"); } - SafeNativeMethods.c_qr_solve_factored(rowsR, columnsR, columnsB, r, b, tau, x, work, work.Length); + if (method == QRMethod.Full) + { + SafeNativeMethods.c_qr_solve_factored(rowsA, columnsA, columnsB, r, b, tau, x, work, work.Length); + } + else + { + // we don't have access to the raw Q matrix any more(it is stored in R in the full QR), need to think about this. + // let just call the managed version in the meantime. The heavy lifting has already been done. -marcus + base.QRSolveFactored(q, r, rowsA, columnsA, tau, b, columnsB, x, QRMethod.Thin); + } } /// diff --git a/src/Numerics/Algorithms/LinearAlgebra/Mkl/MklLinearAlgebraProvider.double.cs b/src/Numerics/Algorithms/LinearAlgebra/Mkl/MklLinearAlgebraProvider.double.cs index e7236f26..474f2642 100644 --- a/src/Numerics/Algorithms/LinearAlgebra/Mkl/MklLinearAlgebraProvider.double.cs +++ b/src/Numerics/Algorithms/LinearAlgebra/Mkl/MklLinearAlgebraProvider.double.cs @@ -28,6 +28,8 @@ // OTHER DEALINGS IN THE SOFTWARE. // +using MathNet.Numerics.LinearAlgebra.Generic.Factorization; + namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl { using System; @@ -657,54 +659,125 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl } /// - /// Solves A*X=B for X using QR factorization of A. + /// Computes the thin QR factorization of A where M > N. /// - /// 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. - /// On exit, the solution matrix. - /// Rows must be greater or equal to columns. - public override void QRSolve(double[] a, int rows, int columns, double[] b, int columnsB, double[] x) + /// On entry, it is the M by N A matrix to factor. On exit, + /// it is overwritten with the Q matrix of the QR factorization. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. + /// On exit, A N by N matrix that holds the R matrix of the + /// QR factorization. + /// A min(m,n) vector. On exit, contains additional information + /// to be used by the QR solve routine. + /// This is similar to the GEQRF and ORGQR LAPACK routines. + [SecuritySafeCritical] + public override void ThinQRFactor(double[] q, int rowsA, int columnsA, double[] r, double[] tau) { - if (a == null) + if (r == null) { - throw new ArgumentNullException("a"); + throw new ArgumentNullException("r"); } - if (b == null) + if (q == null) { - throw new ArgumentNullException("b"); + throw new ArgumentNullException("q"); } - if (x == null) + if (q.Length != rowsA * columnsA) { - throw new ArgumentNullException("x"); + throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, "rowsR * columnsR"), "q"); } - if (a.Length != rows * columns) + if (tau.Length < Math.Min(rowsA, columnsA)) { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "a"); + throw new ArgumentException(string.Format(Resources.ArrayTooSmall, "min(m,n)"), "tau"); } - if (b.Length != rows * columnsB) + if (r.Length != columnsA*columnsA) { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); + throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, "columnsA * columnsA"), "r"); } - if (x.Length != columns * columnsB) + var work = new double[columnsA * Control.BlockSize]; + SafeNativeMethods.d_qr_thin_factor(rowsA, columnsA, q, tau, r, work, work.Length); + + } + + + /// + /// Computes the thin QR factorization of A where M > N. + /// + /// On entry, it is the M by N A matrix to factor. On exit, + /// it is overwritten with the Q matrix of the QR factorization. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. + /// On exit, A N by N matrix that holds the R matrix of the + /// QR factorization. + /// A min(m,n) vector. On exit, contains additional information + /// to be used by the QR solve routine. + /// 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. + [SecuritySafeCritical] + public override void ThinQRFactor(double[] q, int rowsA, int columnsA, double[] r, double[] tau, double[] work) + { + if (r == null) { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "x"); + throw new ArgumentNullException("r"); } - if (rows < columns) + if (q == null) { - throw new ArgumentException(Resources.RowsLessThanColumns); + throw new ArgumentNullException("q"); + } + + if (work == null) + { + throw new ArgumentNullException("q"); + } + + if (q.Length != rowsA*columnsA) + { + throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, "rowsR * columnsR"), "q"); + } + + if (tau.Length < Math.Min(rowsA, columnsA)) + { + throw new ArgumentException(string.Format(Resources.ArrayTooSmall, "min(m,n)"), "tau"); } + if (r.Length != columnsA*columnsA) + { + throw new ArgumentException( + string.Format(Resources.ArgumentArrayWrongLength, "columnsA * columnsA"), "r"); + } + + if (work.Length < columnsA*Control.BlockSize) + { + work[0] = columnsA*Control.BlockSize; + throw new ArgumentException(Resources.WorkArrayTooSmall, "work"); + } + + SafeNativeMethods.d_qr_thin_factor(rowsA, columnsA, q, tau, r, work, work.Length); + } + + /// + /// Solves A*X=B for X using QR factorization of A. + /// + /// 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. + /// On exit, the solution matrix. + /// The type of QR factorization to perform. + /// Rows must be greater or equal to columns. + [SecuritySafeCritical] + public override void QRSolve(double[] a, int rows, int columns, double[] b, int columnsB, double[] x, QRMethod method = QRMethod.Full) + { var work = new double[columns * Control.BlockSize]; - QRSolve(a, rows, columns, b, columnsB, x, work); + QRSolve(a, rows, columns, b, columnsB, x, work, method); } /// @@ -719,8 +792,10 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl /// 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. + /// The type of QR factorization to perform. /// Rows must be greater or equal to columns. - public override void QRSolve(double[] a, int rows, int columns, double[] b, int columnsB, double[] x, double[] work) + [SecuritySafeCritical] + public override void QRSolve(double[] a, int rows, int columns, double[] b, int columnsB, double[] x, double[] work, QRMethod method = QRMethod.Full) { if (a == null) { @@ -768,7 +843,14 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl throw new ArgumentException(Resources.WorkArrayTooSmall, "work"); } - SafeNativeMethods.d_qr_solve(rows, columns, columnsB, a, b, x, work, work.Length); + if (method == QRMethod.Full) + { + SafeNativeMethods.d_qr_solve(rows, columns, columnsB, a, b, x, work, work.Length); + } + else + { + SafeNativeMethods.d_thin_qr_solve(rows, columns, columnsB, a, b, x, work, work.Length); + } } /// @@ -783,57 +865,13 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl /// The B matrix. /// The number of columns of B. /// On exit, the solution matrix. + /// The type of QR factorization to perform. /// Rows must be greater or equal to columns. [SecuritySafeCritical] - public override void QRSolveFactored(double[] q, double[] r, int rowsR, int columnsR, double[] tau, double[] b, int columnsB, double[] x) + public override void QRSolveFactored(double[] q, double[] r, int rowsR, int columnsR, double[] tau, double[] b, int columnsB, double[] x, QRMethod method = QRMethod.Full) { - if (r == null) - { - throw new ArgumentNullException("r"); - } - - if (q == null) - { - throw new ArgumentNullException("q"); - } - - if (b == null) - { - 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"); - } - - if (rowsR < columnsR) - { - throw new ArgumentException(Resources.RowsLessThanColumns); - } - var work = new double[columnsR * Control.BlockSize]; - QRSolveFactored(q, r, rowsR, columnsR, tau, b, columnsB, x, work); + QRSolveFactored(q, r, rowsR, columnsR, tau, b, columnsB, x, work, method); } /// @@ -842,8 +880,8 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl /// The Q matrix obtained by QR factor. This is only used for the managed provider and can be /// null for the native provider. The native provider uses the Q portion stored in the R matrix. /// 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. /// Contains additional information on Q. Only used for the native solver /// and can be null for the managed provider. /// On entry the B matrix; on exit the X matrix. @@ -852,8 +890,10 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl /// The work array - only used in the native provider. 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. + /// The type of QR factorization to perform. /// Rows must be greater or equal to columns. - public override void QRSolveFactored(double[] q, double[] r, int rowsR, int columnsR, double[] tau, double[] b, int columnsB, double[] x, double[] work) + [SecuritySafeCritical] + public override void QRSolveFactored(double[] q, double[] r, int rowsA, int columnsA, double[] tau, double[] b, int columnsB, double[] x, double[] work, QRMethod method = QRMethod.Full) { if (r == null) { @@ -880,38 +920,54 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl throw new ArgumentNullException("work"); } - if (r.Length != rowsR * columnsR) + int rowsQ, columnsQ, rowsR, columnsR; + if( method == QRMethod.Full) + { + rowsQ = columnsQ = rowsR = rowsA; + columnsR = columnsA; + } + else { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "r"); + rowsQ = rowsA; + columnsQ = rowsR = columnsR = columnsA; } - if (q.Length != rowsR * rowsR) + if (r.Length != rowsR * columnsR) { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "q"); + throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, rowsR * columnsR), "r"); } - if (b.Length != rowsR * columnsB) + if (q.Length != rowsQ * columnsQ) { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); + throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, rowsQ * columnsQ), "q"); } - if (x.Length != columnsR * columnsB) + if (b.Length != rowsA * columnsB) { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "x"); + throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, rowsA * columnsB), "b"); } - if (rowsR < columnsR) + if (x.Length != columnsA * columnsB) { - throw new ArgumentException(Resources.RowsLessThanColumns); + throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, columnsA * columnsB), "x"); } if (work.Length < 1) { - work[0] = rowsR * Control.BlockSize; + work[0] = rowsA * Control.BlockSize; throw new ArgumentException(Resources.WorkArrayTooSmall, "work"); } - SafeNativeMethods.d_qr_solve_factored(rowsR, columnsR, columnsB, r, b, tau, x, work, work.Length); + if (method == QRMethod.Full) + { + SafeNativeMethods.d_qr_solve_factored(rowsA, columnsA, columnsB, r, b, tau, x, work, work.Length); + } + else + { + // we don't have access to the raw Q matrix any more(it is stored in R in the full QR), need to think about this. + // let just call the managed version in the meantime. The heavy lifting has already been done. -marcus + base.QRSolveFactored(q, r, rowsA, columnsA, tau, b, columnsB, x, QRMethod.Thin); + } } /// diff --git a/src/Numerics/Algorithms/LinearAlgebra/Mkl/MklLinearAlgebraProvider.float.cs b/src/Numerics/Algorithms/LinearAlgebra/Mkl/MklLinearAlgebraProvider.float.cs index a95d3e38..6da7054e 100644 --- a/src/Numerics/Algorithms/LinearAlgebra/Mkl/MklLinearAlgebraProvider.float.cs +++ b/src/Numerics/Algorithms/LinearAlgebra/Mkl/MklLinearAlgebraProvider.float.cs @@ -32,6 +32,8 @@ Last generated on UTC 2011-04-17 06:45:23Z */ +using MathNet.Numerics.LinearAlgebra.Generic.Factorization; + namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl { using System; @@ -669,46 +671,13 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl /// The B matrix. /// The number of columns of B. /// On exit, the solution matrix. + /// The type of QR factorization to perform. /// Rows must be greater or equal to columns. - public override void QRSolve(float[] a, int rows, int columns, float[] b, int columnsB, float[] x) + [SecuritySafeCritical] + public override void QRSolve(float[] a, int rows, int columns, float[] b, int columnsB, float[] x, QRMethod method = QRMethod.Full) { - if (a == null) - { - throw new ArgumentNullException("a"); - } - - if (b == null) - { - throw new ArgumentNullException("b"); - } - - if (x == null) - { - throw new ArgumentNullException("x"); - } - - if (a.Length != rows * columns) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "a"); - } - - if (b.Length != rows * columnsB) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); - } - - if (x.Length != columns * columnsB) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "x"); - } - - if (rows < columns) - { - throw new ArgumentException(Resources.RowsLessThanColumns); - } - var work = new float[columns * Control.BlockSize]; - QRSolve(a, rows, columns, b, columnsB, x, work); + QRSolve(a, rows, columns, b, columnsB, x, work, method); } /// @@ -723,8 +692,10 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl /// 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. + /// The type of QR factorization to perform. /// Rows must be greater or equal to columns. - public override void QRSolve(float[] a, int rows, int columns, float[] b, int columnsB, float[] x, float[] work) + [SecuritySafeCritical] + public override void QRSolve(float[] a, int rows, int columns, float[] b, int columnsB, float[] x, float[] work, QRMethod method = QRMethod.Full) { if (a == null) { @@ -772,7 +743,14 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl throw new ArgumentException(Resources.WorkArrayTooSmall, "work"); } - SafeNativeMethods.s_qr_solve(rows, columns, columnsB, a, b, x, work, work.Length); + if (method == QRMethod.Full) + { + SafeNativeMethods.s_qr_solve(rows, columns, columnsB, a, b, x, work, work.Length); + } + else + { + SafeNativeMethods.s_thin_qr_solve(rows, columns, columnsB, a, b, x, work, work.Length); + } } /// @@ -787,57 +765,13 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl /// The B matrix. /// The number of columns of B. /// On exit, the solution matrix. + /// The type of QR factorization to perform. /// Rows must be greater or equal to columns. [SecuritySafeCritical] - public override void QRSolveFactored(float[] q, float[] r, int rowsR, int columnsR, float[] tau, float[] b, int columnsB, float[] x) + public override void QRSolveFactored(float[] q, float[] r, int rowsR, int columnsR, float[] tau, float[] b, int columnsB, float[] x, QRMethod method = QRMethod.Full) { - if (r == null) - { - throw new ArgumentNullException("r"); - } - - if (q == null) - { - throw new ArgumentNullException("q"); - } - - if (b == null) - { - 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"); - } - - if (rowsR < columnsR) - { - throw new ArgumentException(Resources.RowsLessThanColumns); - } - var work = new float[columnsR * Control.BlockSize]; - QRSolveFactored(q, r, rowsR, columnsR, tau, b, columnsB, x, work); + QRSolveFactored(q, r, rowsR, columnsR, tau, b, columnsB, x, work, method); } /// @@ -846,8 +780,8 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl /// The Q matrix obtained by QR factor. This is only used for the managed provider and can be /// null for the native provider. The native provider uses the Q portion stored in the R matrix. /// 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. /// Contains additional information on Q. Only used for the native solver /// and can be null for the managed provider. /// On entry the B matrix; on exit the X matrix. @@ -856,8 +790,10 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl /// The work array - only used in the native provider. 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. + /// The type of QR factorization to perform. /// Rows must be greater or equal to columns. - public override void QRSolveFactored(float[] q, float[] r, int rowsR, int columnsR, float[] tau, float[] b, int columnsB, float[] x, float[] work) + [SecuritySafeCritical] + public override void QRSolveFactored(float[] q, float[] r, int rowsA, int columnsA, float[] tau, float[] b, int columnsB, float[] x, float[] work, QRMethod method = QRMethod.Full) { if (r == null) { @@ -884,38 +820,54 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl throw new ArgumentNullException("work"); } - if (r.Length != rowsR * columnsR) + int rowsQ, columnsQ, rowsR, columnsR; + if (method == QRMethod.Full) { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "r"); + rowsQ = columnsQ = rowsR = rowsA; + columnsR = columnsA; + } + else + { + rowsQ = rowsA; + columnsQ = rowsR = columnsR = columnsA; } - if (q.Length != rowsR * rowsR) + if (r.Length != rowsR * columnsR) { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "q"); + throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, rowsR * columnsR), "r"); } - if (b.Length != rowsR * columnsB) + if (q.Length != rowsQ * columnsQ) { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); + throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, rowsQ * columnsQ), "q"); } - if (x.Length != columnsR * columnsB) + if (b.Length != rowsA * columnsB) { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "x"); + throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, rowsA * columnsB), "b"); } - if (rowsR < columnsR) + if (x.Length != columnsA * columnsB) { - throw new ArgumentException(Resources.RowsLessThanColumns); + throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, columnsA * columnsB), "x"); } if (work.Length < 1) { - work[0] = rowsR * Control.BlockSize; + work[0] = rowsA * Control.BlockSize; throw new ArgumentException(Resources.WorkArrayTooSmall, "work"); } - SafeNativeMethods.s_qr_solve_factored(rowsR, columnsR, columnsB, r, b, tau, x, work, work.Length); + if (method == QRMethod.Full) + { + SafeNativeMethods.s_qr_solve_factored(rowsA, columnsA, columnsB, r, b, tau, x, work, work.Length); + } + else + { + // we don't have access to the raw Q matrix any more(it is stored in R in the full QR), need to think about this. + // let just call the managed version in the meantime. The heavy lifting has already been done. -marcus + base.QRSolveFactored(q, r, rowsA, columnsA, tau, b, columnsB, x, QRMethod.Thin); + } } /// diff --git a/src/Numerics/Algorithms/LinearAlgebra/Mkl/SafeNativeMethods.cs b/src/Numerics/Algorithms/LinearAlgebra/Mkl/SafeNativeMethods.cs index 4be8e91d..eceb2d2a 100644 --- a/src/Numerics/Algorithms/LinearAlgebra/Mkl/SafeNativeMethods.cs +++ b/src/Numerics/Algorithms/LinearAlgebra/Mkl/SafeNativeMethods.cs @@ -218,6 +218,18 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl [DllImport(DllName, ExactSpelling = true, SetLastError = false, CallingConvention = CallingConvention.Cdecl)] internal static extern int z_qr_factor(int m, int n, [In, Out] Complex[] r, [In, Out] Complex[] tau, [In, Out] Complex[] q, [In, Out] Complex[] work, int len); + [DllImport(DllName, ExactSpelling = true, SetLastError = false, CallingConvention = CallingConvention.Cdecl)] + internal static extern int s_qr_thin_factor(int m, int n, [In, Out] float[] q, [In, Out] float[] tau, [In, Out] float[] r, [In, Out] float[] work, int len); + + [DllImport(DllName, ExactSpelling = true, SetLastError = false, CallingConvention = CallingConvention.Cdecl)] + internal static extern int d_qr_thin_factor(int m, int n, [In, Out] double[] q, [In, Out] double[] tau, [In, Out] double[] r, [In, Out] double[] work, int len); + + [DllImport(DllName, ExactSpelling = true, SetLastError = false, CallingConvention = CallingConvention.Cdecl)] + internal static extern int c_qr_thin_factor(int m, int n, [In, Out] Complex32[] q, [In, Out] Complex32[] tau, [In, Out] Complex32[] r, [In, Out] Complex32[] work, int len); + + [DllImport(DllName, ExactSpelling = true, SetLastError = false, CallingConvention = CallingConvention.Cdecl)] + internal static extern int z_qr_thin_factor(int m, int n, [In, Out] Complex[] q, [In, Out] Complex[] tau, [In, Out] Complex[] r, [In, Out] Complex[] work, int len); + [DllImport(DllName, ExactSpelling = true, SetLastError = false, CallingConvention = CallingConvention.Cdecl)] internal static extern int s_qr_solve(int m, int n, int bn, float[] r, float[] b, [In, Out] float[] x, [In, Out] float[] work, int len); @@ -230,6 +242,18 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.Mkl [DllImport(DllName, ExactSpelling = true, SetLastError = false, CallingConvention = CallingConvention.Cdecl)] internal static extern int z_qr_solve(int m, int n, int bn, Complex[] r, Complex[] b, [In, Out] Complex[] x, [In, Out] Complex[] work, int len); + [DllImport(DllName, ExactSpelling = true, SetLastError = false, CallingConvention = CallingConvention.Cdecl)] + internal static extern int s_thin_qr_solve(int m, int n, int bn, float[] r, float[] b, [In, Out] float[] x, [In, Out] float[] work, int len); + + [DllImport(DllName, ExactSpelling = true, SetLastError = false, CallingConvention = CallingConvention.Cdecl)] + internal static extern int d_thin_qr_solve(int m, int n, int bn, double[] r, double[] b, [In, Out] double[] x, [In, Out] double[] work, int len); + + [DllImport(DllName, ExactSpelling = true, SetLastError = false, CallingConvention = CallingConvention.Cdecl)] + internal static extern int c_thin_qr_solve(int m, int n, int bn, Complex32[] r, Complex32[] b, [In, Out] Complex32[] x, [In, Out] Complex32[] work, int len); + + [DllImport(DllName, ExactSpelling = true, SetLastError = false, CallingConvention = CallingConvention.Cdecl)] + internal static extern int z_thin_qr_solve(int m, int n, int bn, Complex[] r, Complex[] b, [In, Out] Complex[] x, [In, Out] Complex[] work, int len); + [DllImport(DllName, ExactSpelling = true, SetLastError = false, CallingConvention = CallingConvention.Cdecl)] internal static extern int s_qr_solve_factored(int m, int n, int bn, float[] r, float[] b, float[] tau, [In, Out] float[] x, [In, Out] float[] work, int len); diff --git a/src/Numerics/LinearAlgebra/Complex/ExtensionMethods.cs b/src/Numerics/LinearAlgebra/Complex/ExtensionMethods.cs index 0b228593..fb5e2c8d 100644 --- a/src/Numerics/LinearAlgebra/Complex/ExtensionMethods.cs +++ b/src/Numerics/LinearAlgebra/Complex/ExtensionMethods.cs @@ -60,10 +60,11 @@ namespace MathNet.Numerics.LinearAlgebra.Complex /// Computes the QR decomposition for a matrix. /// /// The matrix to factor. + /// The type of QR factorization to perform. /// The QR decomposition object. - public static QR QR(this Matrix matrix) + public static QR QR(this Matrix matrix, QRMethod method = QRMethod.Full) { - return (QR)QR.Create(matrix); + return (QR)QR.Create(matrix, method); } /// diff --git a/src/Numerics/LinearAlgebra/Complex/Factorization/DenseQR.cs b/src/Numerics/LinearAlgebra/Complex/Factorization/DenseQR.cs index 8cd0e2e4..341b9e9e 100644 --- a/src/Numerics/LinearAlgebra/Complex/Factorization/DenseQR.cs +++ b/src/Numerics/LinearAlgebra/Complex/Factorization/DenseQR.cs @@ -28,6 +28,8 @@ // OTHER DEALINGS IN THE SOFTWARE. // +using MathNet.Numerics.LinearAlgebra.Generic.Factorization; + namespace MathNet.Numerics.LinearAlgebra.Complex.Factorization { using System; @@ -60,9 +62,10 @@ namespace MathNet.Numerics.LinearAlgebra.Complex.Factorization /// QR factorization when the constructor is called and cache it's factorization. /// /// The matrix to factor. + /// The type of QR factorization to perform. /// If is null. /// If row count is less then column count - public DenseQR(DenseMatrix matrix) + public DenseQR(DenseMatrix matrix, QRMethod method = QRMethod.Full) { if (matrix == null) { @@ -74,10 +77,22 @@ namespace MathNet.Numerics.LinearAlgebra.Complex.Factorization throw Matrix.DimensionsDontMatch(matrix); } - MatrixR = matrix.Clone(); - MatrixQ = new DenseMatrix(matrix.RowCount); Tau = new Complex[Math.Min(matrix.RowCount, matrix.ColumnCount)]; - Control.LinearAlgebraProvider.QRFactor(((DenseMatrix)MatrixR).Data, matrix.RowCount, matrix.ColumnCount, ((DenseMatrix)MatrixQ).Data, Tau); + + if (method == QRMethod.Full) + { + MatrixR = matrix.Clone(); + MatrixQ = new DenseMatrix(matrix.RowCount); + Control.LinearAlgebraProvider.QRFactor(((DenseMatrix)MatrixR).Data, matrix.RowCount, matrix.ColumnCount, + ((DenseMatrix)MatrixQ).Data, Tau); + } + else + { + MatrixQ = matrix.Clone(); + MatrixR = new DenseMatrix(matrix.ColumnCount); + Control.LinearAlgebraProvider.ThinQRFactor(((DenseMatrix)MatrixQ).Data, matrix.RowCount, matrix.ColumnCount, + ((DenseMatrix)MatrixR).Data, Tau); + } } /// diff --git a/src/Numerics/LinearAlgebra/Complex/Factorization/QR.cs b/src/Numerics/LinearAlgebra/Complex/Factorization/QR.cs index 3c5d9161..95919f1e 100644 --- a/src/Numerics/LinearAlgebra/Complex/Factorization/QR.cs +++ b/src/Numerics/LinearAlgebra/Complex/Factorization/QR.cs @@ -33,12 +33,15 @@ namespace MathNet.Numerics.LinearAlgebra.Complex.Factorization /// /// A class which encapsulates the functionality of the QR decomposition. - /// Any real square matrix A (m x n) may be decomposed as A = QR where Q is an orthogonal matrix (m x m) - /// (its columns are orthogonal unit vectors meaning QTQ = I) and R (m x n) is an upper triangular matrix + /// Any real square matrix A (m x n) 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. + /// If a factorization is peformed, the resulting Q matrix is an m x m matrix + /// and the R matrix is an m x n matrix. If a factorization is performed, the + /// resulting Q matrix is an m x n matrix and the R matrix is an n x n matrix. /// public abstract class QR : QR { diff --git a/src/Numerics/LinearAlgebra/Complex/Factorization/UserQR.cs b/src/Numerics/LinearAlgebra/Complex/Factorization/UserQR.cs index 9051fa9f..04a1fcbe 100644 --- a/src/Numerics/LinearAlgebra/Complex/Factorization/UserQR.cs +++ b/src/Numerics/LinearAlgebra/Complex/Factorization/UserQR.cs @@ -28,6 +28,8 @@ // OTHER DEALINGS IN THE SOFTWARE. // +using MathNet.Numerics.LinearAlgebra.Generic.Factorization; + namespace MathNet.Numerics.LinearAlgebra.Complex.Factorization { using System; @@ -53,8 +55,9 @@ namespace MathNet.Numerics.LinearAlgebra.Complex.Factorization /// QR factorization when the constructor is called and cache it's factorization. /// /// The matrix to factor. + /// The QR factorization method to use. /// If is null. - public UserQR(Matrix matrix) + public UserQR(Matrix matrix, QRMethod method = QRMethod.Full) { if (matrix == null) { @@ -66,25 +69,57 @@ namespace MathNet.Numerics.LinearAlgebra.Complex.Factorization throw Matrix.DimensionsDontMatch(matrix); } - MatrixR = matrix.Clone(); - MatrixQ = matrix.CreateMatrix(matrix.RowCount, matrix.RowCount); - - for (var i = 0; i < matrix.RowCount; i++) - { - MatrixQ.At(i, i, 1.0); - } - var minmn = Math.Min(matrix.RowCount, matrix.ColumnCount); var u = new Complex[minmn][]; - for (var i = 0; i < minmn; i++) + + if (method == QRMethod.Full) { - u[i] = GenerateColumn(MatrixR, i, i); - ComputeQR(u[i], MatrixR, i, matrix.RowCount, i + 1, matrix.ColumnCount, Control.NumberOfParallelWorkerThreads); - } + MatrixR = matrix.Clone(); + MatrixQ = matrix.CreateMatrix(matrix.RowCount, matrix.RowCount); - for (var i = minmn - 1; i >= 0; i--) + for (var i = 0; i < matrix.RowCount; i++) + { + MatrixQ.At(i, i, 1.0f); + } + + for (var i = 0; i < minmn; i++) + { + u[i] = GenerateColumn(MatrixR, i, i); + ComputeQR(u[i], MatrixR, i, matrix.RowCount, i + 1, matrix.ColumnCount, + Control.NumberOfParallelWorkerThreads); + } + + for (var i = minmn - 1; i >= 0; i--) + { + ComputeQR(u[i], MatrixQ, i, matrix.RowCount, i, matrix.RowCount, + Control.NumberOfParallelWorkerThreads); + } + } + else { - ComputeQR(u[i], MatrixQ, i, matrix.RowCount, i, matrix.RowCount, Control.NumberOfParallelWorkerThreads); + MatrixR = matrix.CreateMatrix(matrix.ColumnCount, matrix.ColumnCount); + MatrixQ = matrix.Clone(); + + for (var i = 0; i < minmn; i++) + { + u[i] = GenerateColumn(MatrixQ, i, i); + ComputeQR(u[i], MatrixQ, i, matrix.RowCount, i + 1, matrix.ColumnCount, + Control.NumberOfParallelWorkerThreads); + } + + MatrixR = MatrixQ.SubMatrix(0, matrix.ColumnCount, 0, matrix.ColumnCount); + MatrixQ.Clear(); + + for (var i = 0; i < matrix.ColumnCount; i++) + { + MatrixQ.At(i, i, 1.0f); + } + + for (var i = minmn - 1; i >= 0; i--) + { + ComputeQR(u[i], MatrixQ, i, matrix.RowCount, i, matrix.ColumnCount, + Control.NumberOfParallelWorkerThreads); + } } } diff --git a/src/Numerics/LinearAlgebra/Complex32/ExtensionMethods.cs b/src/Numerics/LinearAlgebra/Complex32/ExtensionMethods.cs index 8960dc5c..cc6c5856 100644 --- a/src/Numerics/LinearAlgebra/Complex32/ExtensionMethods.cs +++ b/src/Numerics/LinearAlgebra/Complex32/ExtensionMethods.cs @@ -60,10 +60,11 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32 /// Computes the QR decomposition for a matrix. /// /// The matrix to factor. + /// The type of QR factorization to perform. /// The QR decomposition object. - public static QR QR(this Matrix matrix) + public static QR QR(this Matrix matrix, QRMethod method = QRMethod.Full) { - return (QR)QR.Create(matrix); + return (QR)QR.Create(matrix, method); } /// diff --git a/src/Numerics/LinearAlgebra/Complex32/Factorization/DenseQR.cs b/src/Numerics/LinearAlgebra/Complex32/Factorization/DenseQR.cs index 5a54f50a..04f9d8cb 100644 --- a/src/Numerics/LinearAlgebra/Complex32/Factorization/DenseQR.cs +++ b/src/Numerics/LinearAlgebra/Complex32/Factorization/DenseQR.cs @@ -28,6 +28,8 @@ // OTHER DEALINGS IN THE SOFTWARE. // +using MathNet.Numerics.LinearAlgebra.Generic.Factorization; + namespace MathNet.Numerics.LinearAlgebra.Complex32.Factorization { using System; @@ -60,9 +62,10 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32.Factorization /// QR factorization when the constructor is called and cache it's factorization. /// /// The matrix to factor. + /// The QR factorization method to use. /// If is null. /// If row count is less then column count - public DenseQR(DenseMatrix matrix) + public DenseQR(DenseMatrix matrix, QRMethod method = QRMethod.Full) { if (matrix == null) { @@ -74,10 +77,22 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32.Factorization throw Matrix.DimensionsDontMatch(matrix); } - MatrixR = matrix.Clone(); - MatrixQ = new DenseMatrix(matrix.RowCount); Tau = new Complex32[Math.Min(matrix.RowCount, matrix.ColumnCount)]; - Control.LinearAlgebraProvider.QRFactor(((DenseMatrix)MatrixR).Data, matrix.RowCount, matrix.ColumnCount, ((DenseMatrix)MatrixQ).Data, Tau); + + if (method == QRMethod.Full) + { + MatrixR = matrix.Clone(); + MatrixQ = new DenseMatrix(matrix.RowCount); + Control.LinearAlgebraProvider.QRFactor(((DenseMatrix)MatrixR).Data, matrix.RowCount, matrix.ColumnCount, + ((DenseMatrix)MatrixQ).Data, Tau); + } + else + { + MatrixQ = matrix.Clone(); + MatrixR = new DenseMatrix(matrix.ColumnCount); + Control.LinearAlgebraProvider.ThinQRFactor(((DenseMatrix)MatrixQ).Data, matrix.RowCount, matrix.ColumnCount, + ((DenseMatrix)MatrixR).Data, Tau); + } } /// diff --git a/src/Numerics/LinearAlgebra/Complex32/Factorization/QR.cs b/src/Numerics/LinearAlgebra/Complex32/Factorization/QR.cs index c899365e..02450a73 100644 --- a/src/Numerics/LinearAlgebra/Complex32/Factorization/QR.cs +++ b/src/Numerics/LinearAlgebra/Complex32/Factorization/QR.cs @@ -33,12 +33,15 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32.Factorization /// /// A class which encapsulates the functionality of the QR decomposition. - /// Any real square matrix A (m x n) may be decomposed as A = QR where Q is an orthogonal matrix (m x m) - /// (its columns are orthogonal unit vectors meaning QTQ = I) and R (m x n) is an upper triangular matrix + /// Any real square matrix A (m x n) 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. + /// If a factorization is peformed, the resulting Q matrix is an m x m matrix + /// and the R matrix is an m x n matrix. If a factorization is performed, the + /// resulting Q matrix is an m x n matrix and the R matrix is an n x n matrix. /// public abstract class QR : QR { diff --git a/src/Numerics/LinearAlgebra/Complex32/Factorization/UserQR.cs b/src/Numerics/LinearAlgebra/Complex32/Factorization/UserQR.cs index cba8adab..4cc9907a 100644 --- a/src/Numerics/LinearAlgebra/Complex32/Factorization/UserQR.cs +++ b/src/Numerics/LinearAlgebra/Complex32/Factorization/UserQR.cs @@ -28,6 +28,8 @@ // OTHER DEALINGS IN THE SOFTWARE. // +using MathNet.Numerics.LinearAlgebra.Generic.Factorization; + namespace MathNet.Numerics.LinearAlgebra.Complex32.Factorization { using System; @@ -53,8 +55,9 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32.Factorization /// QR factorization when the constructor is called and cache it's factorization. /// /// The matrix to factor. + /// The QR factorization method to use. /// If is null. - public UserQR(Matrix matrix) + public UserQR(Matrix matrix, QRMethod method = QRMethod.Full) { if (matrix == null) { @@ -66,25 +69,57 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32.Factorization throw Matrix.DimensionsDontMatch(matrix); } - 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 Complex32[minmn][]; - for (var i = 0; i < minmn; i++) + + if (method == QRMethod.Full) { - u[i] = GenerateColumn(MatrixR, i, i); - ComputeQR(u[i], MatrixR, i, matrix.RowCount, i + 1, matrix.ColumnCount, Control.NumberOfParallelWorkerThreads); - } + MatrixR = matrix.Clone(); + MatrixQ = matrix.CreateMatrix(matrix.RowCount, matrix.RowCount); - for (var i = minmn - 1; i >= 0; i--) + for (var i = 0; i < matrix.RowCount; i++) + { + MatrixQ.At(i, i, 1.0f); + } + + for (var i = 0; i < minmn; i++) + { + u[i] = GenerateColumn(MatrixR, i, i); + ComputeQR(u[i], MatrixR, i, matrix.RowCount, i + 1, matrix.ColumnCount, + Control.NumberOfParallelWorkerThreads); + } + + for (var i = minmn - 1; i >= 0; i--) + { + ComputeQR(u[i], MatrixQ, i, matrix.RowCount, i, matrix.RowCount, + Control.NumberOfParallelWorkerThreads); + } + } + else { - ComputeQR(u[i], MatrixQ, i, matrix.RowCount, i, matrix.RowCount, Control.NumberOfParallelWorkerThreads); + MatrixR = matrix.CreateMatrix(matrix.ColumnCount, matrix.ColumnCount); + MatrixQ = matrix.Clone(); + + for (var i = 0; i < minmn; i++) + { + u[i] = GenerateColumn(MatrixQ, i, i); + ComputeQR(u[i], MatrixQ, i, matrix.RowCount, i + 1, matrix.ColumnCount, + Control.NumberOfParallelWorkerThreads); + } + + MatrixR = MatrixQ.SubMatrix(0, matrix.ColumnCount, 0, matrix.ColumnCount); + MatrixQ.Clear(); + + for (var i = 0; i < matrix.ColumnCount; i++) + { + MatrixQ.At(i, i, 1.0f); + } + + for (var i = minmn - 1; i >= 0; i--) + { + ComputeQR(u[i], MatrixQ, i, matrix.RowCount, i, matrix.ColumnCount, + Control.NumberOfParallelWorkerThreads); + } } } diff --git a/src/Numerics/LinearAlgebra/Double/ExtensionMethods.cs b/src/Numerics/LinearAlgebra/Double/ExtensionMethods.cs index e46fb617..b00cdc6e 100644 --- a/src/Numerics/LinearAlgebra/Double/ExtensionMethods.cs +++ b/src/Numerics/LinearAlgebra/Double/ExtensionMethods.cs @@ -59,10 +59,11 @@ namespace MathNet.Numerics.LinearAlgebra.Double /// Computes the QR decomposition for a matrix. /// /// The matrix to factor. + /// The type of QR factorization to perform. /// The QR decomposition object. - public static QR QR(this Matrix matrix) + public static QR QR(this Matrix matrix, QRMethod method = QRMethod.Full) { - return (QR)QR.Create(matrix); + return (QR)QR.Create(matrix, method); } /// diff --git a/src/Numerics/LinearAlgebra/Double/Factorization/DenseQR.cs b/src/Numerics/LinearAlgebra/Double/Factorization/DenseQR.cs index 0feb4858..3cb58563 100644 --- a/src/Numerics/LinearAlgebra/Double/Factorization/DenseQR.cs +++ b/src/Numerics/LinearAlgebra/Double/Factorization/DenseQR.cs @@ -28,6 +28,8 @@ // OTHER DEALINGS IN THE SOFTWARE. // +using MathNet.Numerics.LinearAlgebra.Generic.Factorization; + namespace MathNet.Numerics.LinearAlgebra.Double.Factorization { using System; @@ -59,9 +61,10 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Factorization /// QR factorization when the constructor is called and cache it's factorization. /// /// The matrix to factor. + /// The type of QR factorization to perform. /// If is null. /// If row count is less then column count - public DenseQR(DenseMatrix matrix) + public DenseQR(DenseMatrix matrix, QRMethod method = QRMethod.Full) { if (matrix == null) { @@ -73,10 +76,23 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Factorization throw Matrix.DimensionsDontMatch(matrix); } - MatrixR = matrix.Clone(); - MatrixQ = new DenseMatrix(matrix.RowCount); Tau = new double[Math.Min(matrix.RowCount, matrix.ColumnCount)]; - Control.LinearAlgebraProvider.QRFactor(((DenseMatrix)MatrixR).Data, matrix.RowCount, matrix.ColumnCount, ((DenseMatrix)MatrixQ).Data, Tau); + + if (method == QRMethod.Full) + { + MatrixR = matrix.Clone(); + MatrixQ = new DenseMatrix(matrix.RowCount); + Control.LinearAlgebraProvider.QRFactor(((DenseMatrix)MatrixR).Data, matrix.RowCount, matrix.ColumnCount, + ((DenseMatrix)MatrixQ).Data, Tau); + } + else + { + MatrixQ = matrix.Clone(); + MatrixR = new DenseMatrix(matrix.ColumnCount); + Control.LinearAlgebraProvider.ThinQRFactor(((DenseMatrix) MatrixQ).Data, matrix.RowCount, + matrix.ColumnCount, + ((DenseMatrix) MatrixR).Data, Tau); + } } /// diff --git a/src/Numerics/LinearAlgebra/Double/Factorization/QR.cs b/src/Numerics/LinearAlgebra/Double/Factorization/QR.cs index 59fad79a..a6dba90d 100644 --- a/src/Numerics/LinearAlgebra/Double/Factorization/QR.cs +++ b/src/Numerics/LinearAlgebra/Double/Factorization/QR.cs @@ -32,12 +32,15 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Factorization /// /// A class which encapsulates the functionality of the QR decomposition. - /// Any real square matrix A (m x n) may be decomposed as A = QR where Q is an orthogonal matrix (m x m) - /// (its columns are orthogonal unit vectors meaning QTQ = I) and R (m x n) is an upper triangular matrix + /// Any real square matrix A (m x n) 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. + /// If a factorization is performed, the resulting Q matrix is an m x m matrix + /// and the R matrix is an m x n matrix. If a factorization is performed, the + /// resulting Q matrix is an m x n matrix and the R matrix is an n x n matrix. /// public abstract class QR : QR { diff --git a/src/Numerics/LinearAlgebra/Double/Factorization/UserQR.cs b/src/Numerics/LinearAlgebra/Double/Factorization/UserQR.cs index 591bc5a3..eaac74ee 100644 --- a/src/Numerics/LinearAlgebra/Double/Factorization/UserQR.cs +++ b/src/Numerics/LinearAlgebra/Double/Factorization/UserQR.cs @@ -28,6 +28,8 @@ // OTHER DEALINGS IN THE SOFTWARE. // +using MathNet.Numerics.LinearAlgebra.Generic.Factorization; + namespace MathNet.Numerics.LinearAlgebra.Double.Factorization { using System; @@ -52,8 +54,9 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Factorization /// QR factorization when the constructor is called and cache it's factorization. /// /// The matrix to factor. + /// The QR factorization method to use. /// If is null. - public UserQR(Matrix matrix) + public UserQR(Matrix matrix, QRMethod method = QRMethod.Full) { if (matrix == null) { @@ -65,25 +68,58 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Factorization throw Matrix.DimensionsDontMatch(matrix); } - MatrixR = matrix.Clone(); - MatrixQ = matrix.CreateMatrix(matrix.RowCount, matrix.RowCount); - - for (var i = 0; i < matrix.RowCount; i++) - { - MatrixQ.At(i, i, 1.0); - } - var minmn = Math.Min(matrix.RowCount, matrix.ColumnCount); var u = new double[minmn][]; - for (var i = 0; i < minmn; i++) + + if (method == QRMethod.Full) { - u[i] = GenerateColumn(MatrixR, i, i); - ComputeQR(u[i], MatrixR, i, matrix.RowCount, i + 1, matrix.ColumnCount, Control.NumberOfParallelWorkerThreads); - } + MatrixR = matrix.Clone(); + MatrixQ = matrix.CreateMatrix(matrix.RowCount, matrix.RowCount); + + for (var i = 0; i < matrix.RowCount; i++) + { + MatrixQ.At(i, i, 1.0); + } + + for (var i = 0; i < minmn; i++) + { + u[i] = GenerateColumn(MatrixR, i, i); + ComputeQR(u[i], MatrixR, i, matrix.RowCount, i + 1, matrix.ColumnCount, + Control.NumberOfParallelWorkerThreads); + } - for (var i = minmn - 1; i >= 0; i--) + for (var i = minmn - 1; i >= 0; i--) + { + ComputeQR(u[i], MatrixQ, i, matrix.RowCount, i, matrix.RowCount, + Control.NumberOfParallelWorkerThreads); + } + } + else { - ComputeQR(u[i], MatrixQ, i, matrix.RowCount, i, matrix.RowCount, Control.NumberOfParallelWorkerThreads); + MatrixR = matrix.CreateMatrix(matrix.ColumnCount, matrix.ColumnCount); + MatrixQ = matrix.Clone(); + + for (var i = 0; i < minmn; i++) + { + u[i] = GenerateColumn(MatrixQ, i, i); + ComputeQR(u[i], MatrixQ, i, matrix.RowCount, i + 1, matrix.ColumnCount, + Control.NumberOfParallelWorkerThreads); + } + + MatrixR = MatrixQ.SubMatrix(0, matrix.ColumnCount, 0, matrix.ColumnCount); + MatrixQ.Clear(); + + for (var i = 0; i < matrix.ColumnCount; i++) + { + MatrixQ.At(i, i, 1.0); + } + + for (var i = minmn - 1; i >= 0; i--) + { + ComputeQR(u[i], MatrixQ, i, matrix.RowCount, i, matrix.ColumnCount, + Control.NumberOfParallelWorkerThreads); + } + } } diff --git a/src/Numerics/LinearAlgebra/Generic/Factorization/GramSchmidt.cs b/src/Numerics/LinearAlgebra/Generic/Factorization/GramSchmidt.cs index 70bb4722..697e96a0 100644 --- a/src/Numerics/LinearAlgebra/Generic/Factorization/GramSchmidt.cs +++ b/src/Numerics/LinearAlgebra/Generic/Factorization/GramSchmidt.cs @@ -47,7 +47,7 @@ namespace MathNet.Numerics.LinearAlgebra.Generic.Factorization /// /// The matrix to factor. /// A QR factorization object. - new internal static GramSchmidt Create(Matrix matrix) + internal static GramSchmidt Create(Matrix matrix) { if (typeof(T) == typeof(double)) { diff --git a/src/Numerics/LinearAlgebra/Generic/Factorization/QR.cs b/src/Numerics/LinearAlgebra/Generic/Factorization/QR.cs index 669375a7..0afbe870 100644 --- a/src/Numerics/LinearAlgebra/Generic/Factorization/QR.cs +++ b/src/Numerics/LinearAlgebra/Generic/Factorization/QR.cs @@ -31,14 +31,33 @@ namespace MathNet.Numerics.LinearAlgebra.Generic.Factorization using Generic; using Numerics; + /// + /// The type of QR factorization go perform. + /// + public enum QRMethod + { + /// + /// Compute the full QR factorization of a matrix. + /// + Full = 0, + + /// + /// Compute the thin QR factorixation of a matrix. + /// + Thin = 1 + } + /// /// A class which encapsulates the functionality of the QR decomposition. - /// Any real square matrix A (m x n) may be decomposed as A = QR where Q is an orthogonal matrix (m x m) - /// (its columns are orthogonal unit vectors meaning QTQ = I) and R (m x n) is an upper triangular matrix + /// Any real square matrix A (m x n) 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. + /// If a factorization is performed, the resulting Q matrix is an m x m matrix + /// and the R matrix is an m x n matrix. If a factorization is performed, the + /// resulting Q matrix is an m x n matrix and the R matrix is an n x n matrix. /// /// Supported data types are double, single, , and . public abstract class QR : ISolver @@ -66,18 +85,19 @@ namespace MathNet.Numerics.LinearAlgebra.Generic.Factorization /// Internal method which routes the call to perform the QR factorization to the appropriate class. /// /// The matrix to factor. + /// The type of QR factorization to perform. /// A QR factorization object. - internal static QR Create(Matrix matrix) + internal static QR Create(Matrix matrix, QRMethod method = QRMethod.Full) { if (typeof(T) == typeof(double)) { var dense = matrix as LinearAlgebra.Double.DenseMatrix; if (dense != null) { - return new LinearAlgebra.Double.Factorization.DenseQR(dense) as QR; + return new LinearAlgebra.Double.Factorization.DenseQR(dense, method) as QR; } - return new LinearAlgebra.Double.Factorization.UserQR(matrix as Matrix) as QR; + return new LinearAlgebra.Double.Factorization.UserQR(matrix as Matrix, method) as QR; } if (typeof(T) == typeof(float)) @@ -85,10 +105,10 @@ namespace MathNet.Numerics.LinearAlgebra.Generic.Factorization var dense = matrix as LinearAlgebra.Single.DenseMatrix; if (dense != null) { - return new LinearAlgebra.Single.Factorization.DenseQR(dense) as QR; + return new LinearAlgebra.Single.Factorization.DenseQR(dense, method) as QR; } - return new LinearAlgebra.Single.Factorization.UserQR(matrix as Matrix) as QR; + return new LinearAlgebra.Single.Factorization.UserQR(matrix as Matrix, method) as QR; } if (typeof(T) == typeof(Complex)) @@ -96,10 +116,10 @@ namespace MathNet.Numerics.LinearAlgebra.Generic.Factorization var dense = matrix as LinearAlgebra.Complex.DenseMatrix; if (dense != null) { - return new LinearAlgebra.Complex.Factorization.DenseQR(dense) as QR; + return new LinearAlgebra.Complex.Factorization.DenseQR(dense, method) as QR; } - return new LinearAlgebra.Complex.Factorization.UserQR(matrix as Matrix) as QR; + return new LinearAlgebra.Complex.Factorization.UserQR(matrix as Matrix, method) as QR; } if (typeof(T) == typeof(Complex32)) @@ -107,10 +127,10 @@ namespace MathNet.Numerics.LinearAlgebra.Generic.Factorization var dense = matrix as LinearAlgebra.Complex32.DenseMatrix; if (dense != null) { - return new LinearAlgebra.Complex32.Factorization.DenseQR(dense) as QR; + return new LinearAlgebra.Complex32.Factorization.DenseQR(dense, method) as QR; } - return new LinearAlgebra.Complex32.Factorization.UserQR(matrix as Matrix) as QR; + return new LinearAlgebra.Complex32.Factorization.UserQR(matrix as Matrix, method) as QR; } throw new NotSupportedException(); diff --git a/src/Numerics/LinearAlgebra/Single/ExtensionMethods.cs b/src/Numerics/LinearAlgebra/Single/ExtensionMethods.cs index b0855c7a..720c14d1 100644 --- a/src/Numerics/LinearAlgebra/Single/ExtensionMethods.cs +++ b/src/Numerics/LinearAlgebra/Single/ExtensionMethods.cs @@ -59,10 +59,11 @@ namespace MathNet.Numerics.LinearAlgebra.Single /// Computes the QR decomposition for a matrix. /// /// The matrix to factor. + /// The type of QR factorization to perform. /// The QR decomposition object. - public static QR QR(this Matrix matrix) + public static QR QR(this Matrix matrix, QRMethod method = QRMethod.Full) { - return (QR)QR.Create(matrix); + return (QR)QR.Create(matrix, method); } /// diff --git a/src/Numerics/LinearAlgebra/Single/Factorization/DenseQR.cs b/src/Numerics/LinearAlgebra/Single/Factorization/DenseQR.cs index 05c4a593..9157b412 100644 --- a/src/Numerics/LinearAlgebra/Single/Factorization/DenseQR.cs +++ b/src/Numerics/LinearAlgebra/Single/Factorization/DenseQR.cs @@ -28,6 +28,8 @@ // OTHER DEALINGS IN THE SOFTWARE. // +using MathNet.Numerics.LinearAlgebra.Generic.Factorization; + namespace MathNet.Numerics.LinearAlgebra.Single.Factorization { using System; @@ -59,9 +61,10 @@ namespace MathNet.Numerics.LinearAlgebra.Single.Factorization /// QR factorization when the constructor is called and cache it's factorization. /// /// The matrix to factor. + /// The QR factorization method to use. /// If is null. /// If row count is less then column count - public DenseQR(DenseMatrix matrix) + public DenseQR(DenseMatrix matrix, QRMethod method = QRMethod.Full) { if (matrix == null) { @@ -73,10 +76,22 @@ namespace MathNet.Numerics.LinearAlgebra.Single.Factorization throw Matrix.DimensionsDontMatch(matrix); } - MatrixR = matrix.Clone(); - MatrixQ = new DenseMatrix(matrix.RowCount); Tau = new float[Math.Min(matrix.RowCount, matrix.ColumnCount)]; - Control.LinearAlgebraProvider.QRFactor(((DenseMatrix)MatrixR).Data, matrix.RowCount, matrix.ColumnCount, ((DenseMatrix)MatrixQ).Data, Tau); + + if (method == QRMethod.Full) + { + MatrixR = matrix.Clone(); + MatrixQ = new DenseMatrix(matrix.RowCount); + Control.LinearAlgebraProvider.QRFactor(((DenseMatrix)MatrixR).Data, matrix.RowCount, matrix.ColumnCount, + ((DenseMatrix)MatrixQ).Data, Tau); + } + else + { + MatrixQ = matrix.Clone(); + MatrixR = new DenseMatrix(matrix.ColumnCount); + Control.LinearAlgebraProvider.ThinQRFactor(((DenseMatrix)MatrixQ).Data, matrix.RowCount, matrix.ColumnCount, + ((DenseMatrix)MatrixR).Data, Tau); + } } /// diff --git a/src/Numerics/LinearAlgebra/Single/Factorization/QR.cs b/src/Numerics/LinearAlgebra/Single/Factorization/QR.cs index 1b3b6af0..cb84c150 100644 --- a/src/Numerics/LinearAlgebra/Single/Factorization/QR.cs +++ b/src/Numerics/LinearAlgebra/Single/Factorization/QR.cs @@ -32,12 +32,15 @@ namespace MathNet.Numerics.LinearAlgebra.Single.Factorization /// /// A class which encapsulates the functionality of the QR decomposition. - /// Any real square matrix A (m x n) may be decomposed as A = QR where Q is an orthogonal matrix (m x m) - /// (its columns are orthogonal unit vectors meaning QTQ = I) and R (m x n) is an upper triangular matrix + /// Any real square matrix A (m x n) 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. + /// If a factorization is peformed, the resulting Q matrix is an m x m matrix + /// and the R matrix is an m x n matrix. If a factorization is performed, the + /// resulting Q matrix is an m x n matrix and the R matrix is an n x n matrix. /// public abstract class QR : QR { diff --git a/src/Numerics/LinearAlgebra/Single/Factorization/UserQR.cs b/src/Numerics/LinearAlgebra/Single/Factorization/UserQR.cs index af3bc5aa..047c4ff7 100644 --- a/src/Numerics/LinearAlgebra/Single/Factorization/UserQR.cs +++ b/src/Numerics/LinearAlgebra/Single/Factorization/UserQR.cs @@ -28,6 +28,8 @@ // OTHER DEALINGS IN THE SOFTWARE. // +using MathNet.Numerics.LinearAlgebra.Generic.Factorization; + namespace MathNet.Numerics.LinearAlgebra.Single.Factorization { using System; @@ -52,8 +54,9 @@ namespace MathNet.Numerics.LinearAlgebra.Single.Factorization /// QR factorization when the constructor is called and cache it's factorization. /// /// The matrix to factor. + /// The QR factorization method to use. /// If is null. - public UserQR(Matrix matrix) + public UserQR(Matrix matrix, QRMethod method = QRMethod.Full) { if (matrix == null) { @@ -65,25 +68,57 @@ namespace MathNet.Numerics.LinearAlgebra.Single.Factorization throw Matrix.DimensionsDontMatch(matrix); } - 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++) + + if (method == QRMethod.Full) { - u[i] = GenerateColumn(MatrixR, i, i); - ComputeQR(u[i], MatrixR, i, matrix.RowCount, i + 1, matrix.ColumnCount, Control.NumberOfParallelWorkerThreads); - } + MatrixR = matrix.Clone(); + MatrixQ = matrix.CreateMatrix(matrix.RowCount, matrix.RowCount); - for (var i = minmn - 1; i >= 0; i--) + for (var i = 0; i < matrix.RowCount; i++) + { + MatrixQ.At(i, i, 1.0f); + } + + for (var i = 0; i < minmn; i++) + { + u[i] = GenerateColumn(MatrixR, i, i); + ComputeQR(u[i], MatrixR, i, matrix.RowCount, i + 1, matrix.ColumnCount, + Control.NumberOfParallelWorkerThreads); + } + + for (var i = minmn - 1; i >= 0; i--) + { + ComputeQR(u[i], MatrixQ, i, matrix.RowCount, i, matrix.RowCount, + Control.NumberOfParallelWorkerThreads); + } + } + else { - ComputeQR(u[i], MatrixQ, i, matrix.RowCount, i, matrix.RowCount, Control.NumberOfParallelWorkerThreads); + MatrixR = matrix.CreateMatrix(matrix.ColumnCount, matrix.ColumnCount); + MatrixQ = matrix.Clone(); + + for (var i = 0; i < minmn; i++) + { + u[i] = GenerateColumn(MatrixQ, i, i); + ComputeQR(u[i], MatrixQ, i, matrix.RowCount, i + 1, matrix.ColumnCount, + Control.NumberOfParallelWorkerThreads); + } + + MatrixR = MatrixQ.SubMatrix(0, matrix.ColumnCount, 0, matrix.ColumnCount); + MatrixQ.Clear(); + + for (var i = 0; i < matrix.ColumnCount; i++) + { + MatrixQ.At(i, i, 1.0f); + } + + for (var i = minmn - 1; i >= 0; i--) + { + ComputeQR(u[i], MatrixQ, i, matrix.RowCount, i, matrix.ColumnCount, + Control.NumberOfParallelWorkerThreads); + } } } diff --git a/src/Numerics/Numerics.csproj b/src/Numerics/Numerics.csproj index 18d2fdbb..4eda2e3b 100644 --- a/src/Numerics/Numerics.csproj +++ b/src/Numerics/Numerics.csproj @@ -85,22 +85,6 @@ - - - - - - - - - - - - - - - - diff --git a/src/UnitTests/LinearAlgebraProviderTests/Complex/LinearAlgebraProviderTests.cs b/src/UnitTests/LinearAlgebraProviderTests/Complex/LinearAlgebraProviderTests.cs index a1f5b6b9..8d431820 100644 --- a/src/UnitTests/LinearAlgebraProviderTests/Complex/LinearAlgebraProviderTests.cs +++ b/src/UnitTests/LinearAlgebraProviderTests/Complex/LinearAlgebraProviderTests.cs @@ -789,6 +789,115 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraProviderTests.Complex } } + /// + /// Can compute thin QR factorization of a square matrix. + /// + [Test] + public void CanComputeThinQRFactorSquareMatrix() + { + var matrix = _matrices["Square3x3"]; + var r = new Complex[matrix.ColumnCount * matrix.ColumnCount]; + var tau = new Complex[3]; + var q = new Complex[matrix.RowCount * matrix.ColumnCount]; + Array.Copy(matrix.Data, q, q.Length); + + Control.LinearAlgebraProvider.ThinQRFactor(q, matrix.RowCount, matrix.ColumnCount, r, tau); + + var mq = new DenseMatrix(matrix.RowCount, matrix.ColumnCount, q); + var mr = new DenseMatrix(matrix.ColumnCount, matrix.ColumnCount, r); + var a = mq * mr; + + for (var row = 0; row < matrix.RowCount; row++) + { + for (var col = 0; col < matrix.ColumnCount; col++) + { + AssertHelpers.AlmostEqual(matrix[row, col], a[row, col], 14); + } + } + } + + /// + /// Can compute thin QR factorization of a tall matrix. + /// + [Test] + public void CanComputeThinQRFactorTallMatrix() + { + var matrix = _matrices["Tall3x2"]; + var r = new Complex[matrix.ColumnCount * matrix.ColumnCount]; + var tau = new Complex[3]; + var q = new Complex[matrix.RowCount * matrix.ColumnCount]; + Array.Copy(matrix.Data, q, q.Length); + + Control.LinearAlgebraProvider.ThinQRFactor(q, matrix.RowCount, matrix.ColumnCount, r, tau); + + var mq = new DenseMatrix(matrix.RowCount, matrix.ColumnCount, q); + var mr = new DenseMatrix(matrix.ColumnCount, matrix.ColumnCount, r); + var a = mq * mr; + + for (var row = 0; row < matrix.RowCount; row++) + { + for (var col = 0; col < matrix.ColumnCount; col++) + { + AssertHelpers.AlmostEqual(matrix[row, col], a[row, col], 14); + } + } + } + + /// + /// Can compute thin QR factorization of a square matrix using a work array. + /// + [Test] + public void CanComputeThinQRFactorSquareMatrixWithWorkArray() + { + var matrix = _matrices["Square3x3"]; + var r = new Complex[matrix.ColumnCount * matrix.ColumnCount]; + var tau = new Complex[3]; + var q = new Complex[matrix.RowCount * matrix.ColumnCount]; + Array.Copy(matrix.Data, q, q.Length); + + var work = new Complex[matrix.RowCount * matrix.ColumnCount]; + Control.LinearAlgebraProvider.ThinQRFactor(q, matrix.RowCount, matrix.ColumnCount, r, tau, work); + + var mq = new DenseMatrix(matrix.RowCount, matrix.ColumnCount, q); + var mr = new DenseMatrix(matrix.ColumnCount, matrix.ColumnCount, r); + var a = mq * mr; + + for (var row = 0; row < matrix.RowCount; row++) + { + for (var col = 0; col < matrix.ColumnCount; col++) + { + AssertHelpers.AlmostEqual(matrix[row, col], a[row, col], 14); + } + } + } + + /// + /// Can compute thin QR factorization of a tall matrix using a work matrix. + /// + [Test] + public void CanComputeThinQRFactorTallMatrixWithWorkArray() + { + var matrix = _matrices["Tall3x2"]; + var r = new Complex[matrix.ColumnCount * matrix.ColumnCount]; + var tau = new Complex[3]; + var q = new Complex[matrix.RowCount * matrix.ColumnCount]; + Array.Copy(matrix.Data, q, q.Length); + + var work = new Complex[matrix.RowCount * matrix.ColumnCount]; + Control.LinearAlgebraProvider.ThinQRFactor(q, matrix.RowCount, matrix.ColumnCount, r, tau, work); + + var mq = new DenseMatrix(matrix.RowCount, matrix.ColumnCount, q); + var mr = new DenseMatrix(matrix.ColumnCount, matrix.ColumnCount, r); + var a = mq * mr; + for (var row = 0; row < matrix.RowCount; row++) + { + for (var col = 0; col < matrix.ColumnCount; col++) + { + AssertHelpers.AlmostEqual(matrix[row, col], a[row, col], 14); + } + } + } + /// /// Can solve Ax=b using QR factorization with a square A matrix. /// @@ -807,7 +916,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraProviderTests.Complex var mx = new DenseMatrix(matrix.ColumnCount, 2, x); var mb = matrix * mx; - + Console.WriteLine(mx); AssertHelpers.AlmostEqual(mb[0, 0], b[0], 14); AssertHelpers.AlmostEqual(mb[1, 0], b[1], 14); AssertHelpers.AlmostEqual(mb[2, 0], b[2], 14); @@ -1015,6 +1124,232 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraProviderTests.Complex AssertHelpers.AlmostEqual(test[1, 1], x[3], 14); } + /// + /// Can solve Ax=b using thin QR factorization with a square A matrix. + /// + [Test] + public void CanSolveUsingThinQRSquareMatrix() + { + var matrix = _matrices["Square3x3"]; + var a = new Complex[matrix.RowCount * matrix.ColumnCount]; + Array.Copy(matrix.Data, a, a.Length); + + var b = new[] { new Complex(1.0, 0), 2.0, 3.0, 4.0, 5.0, 6.0 }; + var x = new Complex[matrix.ColumnCount * 2]; + Control.LinearAlgebraProvider.QRSolve(a, matrix.RowCount, matrix.ColumnCount, b, 2, x, QRMethod.Thin); + + NotModified(3, 3, a, matrix); + + var mx = new DenseMatrix(matrix.ColumnCount, 2, x); + var mb = matrix * mx; + + AssertHelpers.AlmostEqual(mb[0, 0], b[0], 14); + AssertHelpers.AlmostEqual(mb[1, 0], b[1], 14); + AssertHelpers.AlmostEqual(mb[2, 0], b[2], 14); + AssertHelpers.AlmostEqual(mb[0, 1], b[3], 14); + AssertHelpers.AlmostEqual(mb[1, 1], b[4], 14); + AssertHelpers.AlmostEqual(mb[2, 1], b[5], 14); + } + + /// + /// Can solve Ax=b using thin QR factorization with a tall A matrix. + /// + [Test] + public void CanSolveUsingThinQRTallMatrix() + { + var matrix = _matrices["Tall3x2"]; + var a = new Complex[matrix.RowCount * matrix.ColumnCount]; + Array.Copy(matrix.Data, a, a.Length); + + var b = new[] { new Complex(1.0, 0), 2.0, 3.0, 4.0, 5.0, 6.0 }; + var x = new Complex[matrix.ColumnCount * 2]; + Control.LinearAlgebraProvider.QRSolve(a, matrix.RowCount, matrix.ColumnCount, b, 2, x, QRMethod.Thin); + + NotModified(3, 2, a, matrix); + + var mb = new DenseMatrix(matrix.RowCount, 2, b); + var test = (matrix.Transpose() * matrix).Inverse() * matrix.Transpose() * mb; + + AssertHelpers.AlmostEqual(test[0, 0], x[0], 14); + AssertHelpers.AlmostEqual(test[1, 0], x[1], 14); + AssertHelpers.AlmostEqual(test[0, 1], x[2], 14); + AssertHelpers.AlmostEqual(test[1, 1], x[3], 14); + } + + /// + /// Can solve Ax=b using thin QR factorization with a square A matrix + /// using a work array. + /// + [Test] + public void CanSolveUsingThinQRSquareMatrixUsingWorkArray() + { + var matrix = _matrices["Square3x3"]; + var a = new Complex[matrix.RowCount * matrix.ColumnCount]; + Array.Copy(matrix.Data, a, a.Length); + + var b = new[] { new Complex(1.0, 0), 2.0, 3.0, 4.0, 5.0, 6.0 }; + var x = new Complex[matrix.ColumnCount * 2]; + var work = new Complex[matrix.RowCount * matrix.ColumnCount]; + Control.LinearAlgebraProvider.QRSolve(a, matrix.RowCount, matrix.ColumnCount, b, 2, x, work, QRMethod.Thin); + + NotModified(3, 3, a, matrix); + + var mx = new DenseMatrix(matrix.ColumnCount, 2, x); + var mb = matrix * mx; + + AssertHelpers.AlmostEqual(mb[0, 0], b[0], 14); + AssertHelpers.AlmostEqual(mb[1, 0], b[1], 14); + AssertHelpers.AlmostEqual(mb[2, 0], b[2], 14); + AssertHelpers.AlmostEqual(mb[0, 1], b[3], 14); + AssertHelpers.AlmostEqual(mb[1, 1], b[4], 14); + AssertHelpers.AlmostEqual(mb[2, 1], b[5], 14); + } + + /// + /// Can solve Ax=b using thin QR factorization with a tall A matrix + /// using a work array. + /// + [Test] + public void CanSolveUsingThinQRTallMatrixUsingWorkArray() + { + var matrix = _matrices["Tall3x2"]; + var a = new Complex[matrix.RowCount * matrix.ColumnCount]; + Array.Copy(matrix.Data, a, a.Length); + + var b = new[] { new Complex(1.0, 0), 2.0, 3.0, 4.0, 5.0, 6.0 }; + var x = new Complex[matrix.ColumnCount * 2]; + var work = new Complex[matrix.RowCount * matrix.ColumnCount]; + Control.LinearAlgebraProvider.QRSolve(a, matrix.RowCount, matrix.ColumnCount, b, 2, x, work, QRMethod.Thin); + + NotModified(3, 2, a, matrix); + + var mb = new DenseMatrix(matrix.RowCount, 2, b); + var test = (matrix.Transpose() * matrix).Inverse() * matrix.Transpose() * mb; + + AssertHelpers.AlmostEqual(test[0, 0], x[0], 14); + AssertHelpers.AlmostEqual(test[1, 0], x[1], 14); + AssertHelpers.AlmostEqual(test[0, 1], x[2], 14); + AssertHelpers.AlmostEqual(test[1, 1], x[3], 14); + } + + /// + /// Can solve Ax=b using thin QR factorization with a square A matrix + /// using a factored A matrix. + /// + [Test] + public void CanSolveUsingThinQRSquareMatrixOnFactoredMatrix() + { + var matrix = _matrices["Square3x3"]; + var a = new Complex[matrix.RowCount * matrix.ColumnCount]; + Array.Copy(matrix.Data, a, a.Length); + + var tau = new Complex[matrix.ColumnCount]; + var r = new Complex[matrix.ColumnCount * matrix.ColumnCount]; + Control.LinearAlgebraProvider.ThinQRFactor(a, matrix.RowCount, matrix.ColumnCount, r, tau); + + var b = new[] { new Complex(1.0, 0), 2.0, 3.0, 4.0, 5.0, 6.0 }; + var x = new Complex[matrix.ColumnCount * 2]; + Control.LinearAlgebraProvider.QRSolveFactored(a, r, matrix.RowCount, matrix.ColumnCount, tau, b, 2, x, QRMethod.Thin); + + var mx = new DenseMatrix(matrix.ColumnCount, 2, x); + var mb = matrix * mx; + + AssertHelpers.AlmostEqual(mb[0, 0], b[0], 14); + AssertHelpers.AlmostEqual(mb[1, 0], b[1], 14); + AssertHelpers.AlmostEqual(mb[2, 0], b[2], 14); + AssertHelpers.AlmostEqual(mb[0, 1], b[3], 14); + AssertHelpers.AlmostEqual(mb[1, 1], b[4], 14); + AssertHelpers.AlmostEqual(mb[2, 1], b[5], 14); + } + + /// + /// Can solve Ax=b using thin QR factorization with a tall A matrix + /// using a factored A matrix. + /// + [Test] + public void CanSolveUsingThinQRTallMatrixOnFactoredMatrix() + { + var matrix = _matrices["Tall3x2"]; + var a = new Complex[matrix.RowCount * matrix.ColumnCount]; + Array.Copy(matrix.Data, a, a.Length); + + var tau = new Complex[matrix.ColumnCount]; + var r = new Complex[matrix.ColumnCount * matrix.ColumnCount]; + Control.LinearAlgebraProvider.ThinQRFactor(a, matrix.RowCount, matrix.ColumnCount, r, tau); + + var b = new[] { new Complex(1.0, 0), 2.0, 3.0, 4.0, 5.0, 6.0 }; + var x = new Complex[matrix.ColumnCount * 2]; + Control.LinearAlgebraProvider.QRSolveFactored(a, r, matrix.RowCount, matrix.ColumnCount, tau, b, 2, x, QRMethod.Thin); + + var mb = new DenseMatrix(matrix.RowCount, 2, b); + var test = (matrix.Transpose() * matrix).Inverse() * matrix.Transpose() * mb; + + AssertHelpers.AlmostEqual(test[0, 0], x[0], 14); + AssertHelpers.AlmostEqual(test[1, 0], x[1], 14); + AssertHelpers.AlmostEqual(test[0, 1], x[2], 14); + AssertHelpers.AlmostEqual(test[1, 1], x[3], 14); + } + + /// + /// Can solve Ax=b using thin QR factorization with a square A matrix + /// using a factored A matrix with a work array. + /// + [Test] + public void CanSolveUsingThinQRSquareMatrixOnFactoredMatrixWithWorkArray() + { + var matrix = _matrices["Square3x3"]; + var a = new Complex[matrix.RowCount * matrix.ColumnCount]; + Array.Copy(matrix.Data, a, a.Length); + + var tau = new Complex[matrix.ColumnCount]; + var r = new Complex[matrix.ColumnCount * matrix.ColumnCount]; + var work = new Complex[2048]; + Control.LinearAlgebraProvider.ThinQRFactor(a, matrix.RowCount, matrix.ColumnCount, r, tau, work); + + var b = new[] { new Complex(1.0, 0), 2.0, 3.0, 4.0, 5.0, 6.0 }; + var x = new Complex[matrix.ColumnCount * 2]; + Control.LinearAlgebraProvider.QRSolveFactored(a, r, matrix.RowCount, matrix.ColumnCount, tau, b, 2, x, work, QRMethod.Thin); + + var mx = new DenseMatrix(matrix.ColumnCount, 2, x); + var mb = matrix * mx; + + AssertHelpers.AlmostEqual(mb[0, 0], b[0], 14); + AssertHelpers.AlmostEqual(mb[1, 0], b[1], 14); + AssertHelpers.AlmostEqual(mb[2, 0], b[2], 14); + AssertHelpers.AlmostEqual(mb[0, 1], b[3], 14); + AssertHelpers.AlmostEqual(mb[1, 1], b[4], 14); + AssertHelpers.AlmostEqual(mb[2, 1], b[5], 14); + } + + /// + /// Can solve Ax=b using thin QR factorization with a tall A matrix + /// using a factored A matrix with a work array. + /// + [Test] + public void CanSolveUsingThinQRTallMatrixOnFactoredMatrixWithWorkArray() + { + var matrix = _matrices["Tall3x2"]; + var a = new Complex[matrix.RowCount * matrix.ColumnCount]; + Array.Copy(matrix.Data, a, a.Length); + + var tau = new Complex[matrix.ColumnCount]; + var r = new Complex[matrix.ColumnCount * matrix.ColumnCount]; + var work = new Complex[2048]; + Control.LinearAlgebraProvider.ThinQRFactor(a, matrix.RowCount, matrix.ColumnCount, r, tau, work); + + var b = new[] { new Complex(1.0, 0), 2.0, 3.0, 4.0, 5.0, 6.0 }; + var x = new Complex[matrix.ColumnCount * 2]; + Control.LinearAlgebraProvider.QRSolveFactored(a, r, matrix.RowCount, matrix.ColumnCount, tau, b, 2, x, work, QRMethod.Thin); + + var mb = new DenseMatrix(matrix.RowCount, 2, b); + var test = (matrix.Transpose() * matrix).Inverse() * matrix.Transpose() * mb; + + AssertHelpers.AlmostEqual(test[0, 0], x[0], 14); + AssertHelpers.AlmostEqual(test[1, 0], x[1], 14); + AssertHelpers.AlmostEqual(test[0, 1], x[2], 14); + AssertHelpers.AlmostEqual(test[1, 1], x[3], 14); + } + /// /// Can compute the SVD factorization of a square matrix. /// diff --git a/src/UnitTests/LinearAlgebraProviderTests/Complex32/LinearAlgebraProviderTests.cs b/src/UnitTests/LinearAlgebraProviderTests/Complex32/LinearAlgebraProviderTests.cs index 5a664193..f8ae7f55 100644 --- a/src/UnitTests/LinearAlgebraProviderTests/Complex32/LinearAlgebraProviderTests.cs +++ b/src/UnitTests/LinearAlgebraProviderTests/Complex32/LinearAlgebraProviderTests.cs @@ -24,6 +24,8 @@ // OTHER DEALINGS IN THE SOFTWARE. // +using MathNet.Numerics.LinearAlgebra.Generic.Factorization; + namespace MathNet.Numerics.UnitTests.LinearAlgebraProviderTests.Complex32 { using System; @@ -796,6 +798,115 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraProviderTests.Complex32 } } + /// + /// Can compute thin QR factorization of a square matrix. + /// + [Test] + public void CanComputeThinQRFactorSquareMatrix() + { + var matrix = _matrices["Square3x3"]; + var r = new Complex32[matrix.ColumnCount * matrix.ColumnCount]; + var tau = new Complex32[3]; + var q = new Complex32[matrix.RowCount * matrix.ColumnCount]; + Array.Copy(matrix.Data, q, q.Length); + + Control.LinearAlgebraProvider.ThinQRFactor(q, matrix.RowCount, matrix.ColumnCount, r, tau); + + var mq = new DenseMatrix(matrix.RowCount, matrix.ColumnCount, q); + var mr = new DenseMatrix(matrix.ColumnCount, matrix.ColumnCount, r); + var a = mq * mr; + + for (var row = 0; row < matrix.RowCount; row++) + { + for (var col = 0; col < matrix.ColumnCount; col++) + { + AssertHelpers.AlmostEqual(matrix[row, col], a[row, col], 6); + } + } + } + + /// + /// Can compute thin QR factorization of a tall matrix. + /// + [Test] + public void CanComputeThinQRFactorTallMatrix() + { + var matrix = _matrices["Tall3x2"]; + var r = new Complex32[matrix.ColumnCount * matrix.ColumnCount]; + var tau = new Complex32[3]; + var q = new Complex32[matrix.RowCount * matrix.ColumnCount]; + Array.Copy(matrix.Data, q, q.Length); + + Control.LinearAlgebraProvider.ThinQRFactor(q, matrix.RowCount, matrix.ColumnCount, r, tau); + + var mq = new DenseMatrix(matrix.RowCount, matrix.ColumnCount, q); + var mr = new DenseMatrix(matrix.ColumnCount, matrix.ColumnCount, r); + var a = mq * mr; + + for (var row = 0; row < matrix.RowCount; row++) + { + for (var col = 0; col < matrix.ColumnCount; col++) + { + AssertHelpers.AlmostEqual(matrix[row, col], a[row, col], 6); + } + } + } + + /// + /// Can compute thin QR factorization of a square matrix using a work array. + /// + [Test] + public void CanComputeThinQRFactorSquareMatrixWithWorkArray() + { + var matrix = _matrices["Square3x3"]; + var r = new Complex32[matrix.ColumnCount * matrix.ColumnCount]; + var tau = new Complex32[3]; + var q = new Complex32[matrix.RowCount * matrix.ColumnCount]; + Array.Copy(matrix.Data, q, q.Length); + + var work = new Complex32[matrix.RowCount * matrix.ColumnCount]; + Control.LinearAlgebraProvider.ThinQRFactor(q, matrix.RowCount, matrix.ColumnCount, r, tau, work); + + var mq = new DenseMatrix(matrix.RowCount, matrix.ColumnCount, q); + var mr = new DenseMatrix(matrix.ColumnCount, matrix.ColumnCount, r); + var a = mq * mr; + + for (var row = 0; row < matrix.RowCount; row++) + { + for (var col = 0; col < matrix.ColumnCount; col++) + { + AssertHelpers.AlmostEqual(matrix[row, col], a[row, col], 6); + } + } + } + + /// + /// Can compute thin QR factorization of a tall matrix using a work matrix. + /// + [Test] + public void CanComputeThinQRFactorTallMatrixWithWorkArray() + { + var matrix = _matrices["Tall3x2"]; + var r = new Complex32[matrix.ColumnCount * matrix.ColumnCount]; + var tau = new Complex32[3]; + var q = new Complex32[matrix.RowCount * matrix.ColumnCount]; + Array.Copy(matrix.Data, q, q.Length); + + var work = new Complex32[matrix.RowCount * matrix.ColumnCount]; + Control.LinearAlgebraProvider.ThinQRFactor(q, matrix.RowCount, matrix.ColumnCount, r, tau, work); + + var mq = new DenseMatrix(matrix.RowCount, matrix.ColumnCount, q); + var mr = new DenseMatrix(matrix.ColumnCount, matrix.ColumnCount, r); + var a = mq * mr; + for (var row = 0; row < matrix.RowCount; row++) + { + for (var col = 0; col < matrix.ColumnCount; col++) + { + AssertHelpers.AlmostEqual(matrix[row, col], a[row, col], 6); + } + } + } + /// /// Can solve Ax=b using QR factorization with a square A matrix. /// @@ -1022,6 +1133,232 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraProviderTests.Complex32 AssertHelpers.AlmostEqual(test[1, 1], x[3], 6); } + /// + /// Can solve Ax=b using thin QR factorization with a square A matrix. + /// + [Test] + public void CanSolveUsingThinQRSquareMatrix() + { + var matrix = _matrices["Square3x3"]; + var a = new Complex32[matrix.RowCount * matrix.ColumnCount]; + Array.Copy(matrix.Data, a, a.Length); + + var b = new[] { new Complex32(1.0f, 0.0f), 2.0f, 3.0f, 4.0f, 5.0f, 6.0f }; + var x = new Complex32[matrix.ColumnCount * 2]; + Control.LinearAlgebraProvider.QRSolve(a, matrix.RowCount, matrix.ColumnCount, b, 2, x, QRMethod.Thin); + + NotModified(3, 3, a, matrix); + + var mx = new DenseMatrix(matrix.ColumnCount, 2, x); + var mb = matrix * mx; + + AssertHelpers.AlmostEqual(mb[0, 0], b[0], 5); + AssertHelpers.AlmostEqual(mb[1, 0], b[1], 5); + AssertHelpers.AlmostEqual(mb[2, 0], b[2], 5); + AssertHelpers.AlmostEqual(mb[0, 1], b[3], 5); + AssertHelpers.AlmostEqual(mb[1, 1], b[4], 5); + AssertHelpers.AlmostEqual(mb[2, 1], b[5], 5); + } + + /// + /// Can solve Ax=b using thin QR factorization with a tall A matrix. + /// + [Test] + public void CanSolveUsingThinQRTallMatrix() + { + var matrix = _matrices["Tall3x2"]; + var a = new Complex32[matrix.RowCount * matrix.ColumnCount]; + Array.Copy(matrix.Data, a, a.Length); + + var b = new[] { new Complex32(1.0f, 0.0f), 2.0f, 3.0f, 4.0f, 5.0f, 6.0f }; + var x = new Complex32[matrix.ColumnCount * 2]; + Control.LinearAlgebraProvider.QRSolve(a, matrix.RowCount, matrix.ColumnCount, b, 2, x, QRMethod.Thin); + + NotModified(3, 2, a, matrix); + + var mb = new DenseMatrix(matrix.RowCount, 2, b); + var test = (matrix.Transpose() * matrix).Inverse() * matrix.Transpose() * mb; + + AssertHelpers.AlmostEqual(test[0, 0], x[0], 6); + AssertHelpers.AlmostEqual(test[1, 0], x[1], 6); + AssertHelpers.AlmostEqual(test[0, 1], x[2], 6); + AssertHelpers.AlmostEqual(test[1, 1], x[3], 6); + } + + /// + /// Can solve Ax=b using thin QR factorization with a square A matrix + /// using a work array. + /// + [Test] + public void CanSolveUsingThinQRSquareMatrixUsingWorkArray() + { + var matrix = _matrices["Square3x3"]; + var a = new Complex32[matrix.RowCount * matrix.ColumnCount]; + Array.Copy(matrix.Data, a, a.Length); + + var b = new[] { new Complex32(1.0f, 0.0f), 2.0f, 3.0f, 4.0f, 5.0f, 6.0f }; + var x = new Complex32[matrix.ColumnCount * 2]; + var work = new Complex32[matrix.RowCount * matrix.ColumnCount]; + Control.LinearAlgebraProvider.QRSolve(a, matrix.RowCount, matrix.ColumnCount, b, 2, x, work, QRMethod.Thin); + + NotModified(3, 3, a, matrix); + + var mx = new DenseMatrix(matrix.ColumnCount, 2, x); + var mb = matrix * mx; + + AssertHelpers.AlmostEqual(mb[0, 0], b[0], 5); + AssertHelpers.AlmostEqual(mb[1, 0], b[1], 5); + AssertHelpers.AlmostEqual(mb[2, 0], b[2], 5); + AssertHelpers.AlmostEqual(mb[0, 1], b[3], 5); + AssertHelpers.AlmostEqual(mb[1, 1], b[4], 5); + AssertHelpers.AlmostEqual(mb[2, 1], b[5], 5); + } + + /// + /// Can solve Ax=b using thin QR factorization with a tall A matrix + /// using a work array. + /// + [Test] + public void CanSolveUsingThinQRTallMatrixUsingWorkArray() + { + var matrix = _matrices["Tall3x2"]; + var a = new Complex32[matrix.RowCount * matrix.ColumnCount]; + Array.Copy(matrix.Data, a, a.Length); + + var b = new[] { new Complex32(1.0f, 0.0f), 2.0f, 3.0f, 4.0f, 5.0f, 6.0f }; + var x = new Complex32[matrix.ColumnCount * 2]; + var work = new Complex32[matrix.RowCount * matrix.ColumnCount]; + Control.LinearAlgebraProvider.QRSolve(a, matrix.RowCount, matrix.ColumnCount, b, 2, x, work, QRMethod.Thin); + + NotModified(3, 2, a, matrix); + + var mb = new DenseMatrix(matrix.RowCount, 2, b); + var test = (matrix.Transpose() * matrix).Inverse() * matrix.Transpose() * mb; + + AssertHelpers.AlmostEqual(test[0, 0], x[0], 6); + AssertHelpers.AlmostEqual(test[1, 0], x[1], 6); + AssertHelpers.AlmostEqual(test[0, 1], x[2], 6); + AssertHelpers.AlmostEqual(test[1, 1], x[3], 6); + } + + /// + /// Can solve Ax=b using thin QR factorization with a square A matrix + /// using a factored A matrix. + /// + [Test] + public void CanSolveUsingThinQRSquareMatrixOnFactoredMatrix() + { + var matrix = _matrices["Square3x3"]; + var a = new Complex32[matrix.RowCount * matrix.ColumnCount]; + Array.Copy(matrix.Data, a, a.Length); + + var tau = new Complex32[matrix.ColumnCount]; + var r = new Complex32[matrix.ColumnCount * matrix.ColumnCount]; + Control.LinearAlgebraProvider.ThinQRFactor(a, matrix.RowCount, matrix.ColumnCount, r, tau); + + var b = new[] { new Complex32(1.0f, 0.0f), 2.0f, 3.0f, 4.0f, 5.0f, 6.0f }; + var x = new Complex32[matrix.ColumnCount * 2]; + Control.LinearAlgebraProvider.QRSolveFactored(a, r, matrix.RowCount, matrix.ColumnCount, tau, b, 2, x, QRMethod.Thin); + + var mx = new DenseMatrix(matrix.ColumnCount, 2, x); + var mb = matrix * mx; + + AssertHelpers.AlmostEqual(mb[0, 0], b[0], 5); + AssertHelpers.AlmostEqual(mb[1, 0], b[1], 5); + AssertHelpers.AlmostEqual(mb[2, 0], b[2], 5); + AssertHelpers.AlmostEqual(mb[0, 1], b[3], 5); + AssertHelpers.AlmostEqual(mb[1, 1], b[4], 5); + AssertHelpers.AlmostEqual(mb[2, 1], b[5], 5); + } + + /// + /// Can solve Ax=b using thin QR factorization with a tall A matrix + /// using a factored A matrix. + /// + [Test] + public void CanSolveUsingThinQRTallMatrixOnFactoredMatrix() + { + var matrix = _matrices["Tall3x2"]; + var a = new Complex32[matrix.RowCount * matrix.ColumnCount]; + Array.Copy(matrix.Data, a, a.Length); + + var tau = new Complex32[matrix.ColumnCount]; + var r = new Complex32[matrix.ColumnCount * matrix.ColumnCount]; + Control.LinearAlgebraProvider.ThinQRFactor(a, matrix.RowCount, matrix.ColumnCount, r, tau); + + var b = new[] { new Complex32(1.0f, 0.0f), 2.0f, 3.0f, 4.0f, 5.0f, 6.0f }; + var x = new Complex32[matrix.ColumnCount * 2]; + Control.LinearAlgebraProvider.QRSolveFactored(a, r, matrix.RowCount, matrix.ColumnCount, tau, b, 2, x, QRMethod.Thin); + + var mb = new DenseMatrix(matrix.RowCount, 2, b); + var test = (matrix.Transpose() * matrix).Inverse() * matrix.Transpose() * mb; + + AssertHelpers.AlmostEqual(test[0, 0], x[0], 6); + AssertHelpers.AlmostEqual(test[1, 0], x[1], 6); + AssertHelpers.AlmostEqual(test[0, 1], x[2], 6); + AssertHelpers.AlmostEqual(test[1, 1], x[3], 6); + } + + /// + /// Can solve Ax=b using thin QR factorization with a square A matrix + /// using a factored A matrix with a work array. + /// + [Test] + public void CanSolveUsingThinQRSquareMatrixOnFactoredMatrixWithWorkArray() + { + var matrix = _matrices["Square3x3"]; + var a = new Complex32[matrix.RowCount * matrix.ColumnCount]; + Array.Copy(matrix.Data, a, a.Length); + + var tau = new Complex32[matrix.ColumnCount]; + var r = new Complex32[matrix.ColumnCount * matrix.ColumnCount]; + var work = new Complex32[2048]; + Control.LinearAlgebraProvider.ThinQRFactor(a, matrix.RowCount, matrix.ColumnCount, r, tau, work); + + var b = new[] { new Complex32(1.0f, 0.0f), 2.0f, 3.0f, 4.0f, 5.0f, 6.0f }; + var x = new Complex32[matrix.ColumnCount * 2]; + Control.LinearAlgebraProvider.QRSolveFactored(a, r, matrix.RowCount, matrix.ColumnCount, tau, b, 2, x, work, QRMethod.Thin); + + var mx = new DenseMatrix(matrix.ColumnCount, 2, x); + var mb = matrix * mx; + + AssertHelpers.AlmostEqual(mb[0, 0], b[0], 5); + AssertHelpers.AlmostEqual(mb[1, 0], b[1], 5); + AssertHelpers.AlmostEqual(mb[2, 0], b[2], 5); + AssertHelpers.AlmostEqual(mb[0, 1], b[3], 5); + AssertHelpers.AlmostEqual(mb[1, 1], b[4], 5); + AssertHelpers.AlmostEqual(mb[2, 1], b[5], 5); + } + + /// + /// Can solve Ax=b using thin QR factorization with a tall A matrix + /// using a factored A matrix with a work array. + /// + [Test] + public void CanSolveUsingThinQRTallMatrixOnFactoredMatrixWithWorkArray() + { + var matrix = _matrices["Tall3x2"]; + var a = new Complex32[matrix.RowCount * matrix.ColumnCount]; + Array.Copy(matrix.Data, a, a.Length); + + var tau = new Complex32[matrix.ColumnCount]; + var r = new Complex32[matrix.ColumnCount * matrix.ColumnCount]; + var work = new Complex32[2048]; + Control.LinearAlgebraProvider.ThinQRFactor(a, matrix.RowCount, matrix.ColumnCount, r, tau, work); + + var b = new[] { new Complex32(1.0f, 0.0f), 2.0f, 3.0f, 4.0f, 5.0f, 6.0f }; + var x = new Complex32[matrix.ColumnCount * 2]; + Control.LinearAlgebraProvider.QRSolveFactored(a, r, matrix.RowCount, matrix.ColumnCount, tau, b, 2, x, work, QRMethod.Thin); + + var mb = new DenseMatrix(matrix.RowCount, 2, b); + var test = (matrix.Transpose() * matrix).Inverse() * matrix.Transpose() * mb; + + AssertHelpers.AlmostEqual(test[0, 0], x[0], 6); + AssertHelpers.AlmostEqual(test[1, 0], x[1], 6); + AssertHelpers.AlmostEqual(test[0, 1], x[2], 6); + AssertHelpers.AlmostEqual(test[1, 1], x[3], 6); + } + /// /// Can compute the SVD factorization of a square matrix. /// diff --git a/src/UnitTests/LinearAlgebraProviderTests/Double/LinearAlgebraProviderTests.cs b/src/UnitTests/LinearAlgebraProviderTests/Double/LinearAlgebraProviderTests.cs index d73274a1..6b414058 100644 --- a/src/UnitTests/LinearAlgebraProviderTests/Double/LinearAlgebraProviderTests.cs +++ b/src/UnitTests/LinearAlgebraProviderTests/Double/LinearAlgebraProviderTests.cs @@ -24,6 +24,8 @@ // OTHER DEALINGS IN THE SOFTWARE. // +using MathNet.Numerics.LinearAlgebra.Generic.Factorization; + namespace MathNet.Numerics.UnitTests.LinearAlgebraProviderTests.Double { using System; @@ -787,11 +789,121 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraProviderTests.Double } } + /// + /// Can compute thin QR factorization of a square matrix. + /// + [Test] + public void CanComputeThinQRFactorSquareMatrix() + { + var matrix = _matrices["Square3x3"]; + var r = new double[matrix.ColumnCount * matrix.ColumnCount]; + var tau = new double[3]; + var q = new double[matrix.RowCount * matrix.ColumnCount]; + Array.Copy(matrix.Data, q, q.Length); + + Control.LinearAlgebraProvider.ThinQRFactor(q, matrix.RowCount, matrix.ColumnCount, r, tau); + + var mq = new DenseMatrix(matrix.RowCount, matrix.ColumnCount, q); + var mr = new DenseMatrix(matrix.ColumnCount, matrix.ColumnCount, r); + var a = mq * mr; + + for (var row = 0; row < matrix.RowCount; row++) + { + for (var col = 0; col < matrix.ColumnCount; col++) + { + AssertHelpers.AlmostEqual(matrix[row, col], a[row, col], 14); + } + } + } + + /// + /// Can compute thin QR factorization of a tall matrix. + /// + [Test] + public void CanComputeThinQRFactorTallMatrix() + { + var matrix = _matrices["Tall3x2"]; + var r = new double[matrix.ColumnCount * matrix.ColumnCount]; + var tau = new double[3]; + var q = new double[matrix.RowCount * matrix.ColumnCount]; + Array.Copy(matrix.Data, q, q.Length); + + Control.LinearAlgebraProvider.ThinQRFactor(q, matrix.RowCount, matrix.ColumnCount, r, tau); + + var mq = new DenseMatrix(matrix.RowCount, matrix.ColumnCount, q); + var mr = new DenseMatrix(matrix.ColumnCount, matrix.ColumnCount, r); + var a = mq * mr; + + for (var row = 0; row < matrix.RowCount; row++) + { + for (var col = 0; col < matrix.ColumnCount; col++) + { + AssertHelpers.AlmostEqual(matrix[row, col], a[row, col], 14); + } + } + } + + /// + /// Can compute thin QR factorization of a square matrix using a work array. + /// + [Test] + public void CanComputeThinQRFactorSquareMatrixWithWorkArray() + { + var matrix = _matrices["Square3x3"]; + var r = new double[matrix.ColumnCount * matrix.ColumnCount]; + var tau = new double[3]; + var q = new double[matrix.RowCount * matrix.ColumnCount]; + Array.Copy(matrix.Data, q, q.Length); + + var work = new double[matrix.ColumnCount * Control.BlockSize]; + Control.LinearAlgebraProvider.ThinQRFactor(q, matrix.RowCount, matrix.ColumnCount, r, tau, work); + + var mq = new DenseMatrix(matrix.RowCount, matrix.ColumnCount, q); + var mr = new DenseMatrix(matrix.ColumnCount, matrix.ColumnCount, r); + + var a = mq * mr; + + for (var row = 0; row < matrix.RowCount; row++) + { + for (var col = 0; col < matrix.ColumnCount; col++) + { + AssertHelpers.AlmostEqual(matrix[row, col], a[row, col], 14); + } + } + } + + /// + /// Can compute thin QR factorization of a tall matrix using a work matrix. + /// + [Test] + public void CanComputeThinQRFactorTallMatrixWithWorkArray() + { + var matrix = _matrices["Tall3x2"]; + var r = new double[matrix.ColumnCount * matrix.ColumnCount]; + var tau = new double[3]; + var q = new double[matrix.RowCount * matrix.ColumnCount]; + Array.Copy(matrix.Data, q, q.Length); + + var work = new double[matrix.ColumnCount * Control.BlockSize]; + Control.LinearAlgebraProvider.ThinQRFactor(q, matrix.RowCount, matrix.ColumnCount, r, tau, work); + + var mq = new DenseMatrix(matrix.RowCount, matrix.ColumnCount, q); + var mr = new DenseMatrix(matrix.ColumnCount, matrix.ColumnCount, r); + var a = mq * mr; + for (var row = 0; row < matrix.RowCount; row++) + { + for (var col = 0; col < matrix.ColumnCount; col++) + { + AssertHelpers.AlmostEqual(matrix[row, col], a[row, col], 14); + } + } + } + /// /// Can solve Ax=b using QR factorization with a square A matrix. /// [Test] - public void CanSolveUsingQRSquareMatrix() + public void CanSolveUsingQRSquareMatrix() { var matrix = _matrices["Square3x3"]; var a = new double[matrix.RowCount * matrix.ColumnCount]; @@ -852,7 +964,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraProviderTests.Double var b = new[] { 1.0, 2.0, 3.0, 4.0, 5.0, 6.0 }; var x = new double[matrix.ColumnCount * 2]; - var work = new double[matrix.RowCount * matrix.RowCount]; + var work = new double[matrix.RowCount * Control.BlockSize]; Control.LinearAlgebraProvider.QRSolve(a, matrix.RowCount, matrix.ColumnCount, b, 2, x, work); NotModified(3, 3, a, matrix); @@ -1013,6 +1125,232 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraProviderTests.Double AssertHelpers.AlmostEqual(test[1, 1], x[3], 14); } + /// + /// Can solve Ax=b using thin QR factorization with a square A matrix. + /// + [Test] + public void CanSolveUsingThinQRSquareMatrix() + { + var matrix = _matrices["Square3x3"]; + var a = new double[matrix.RowCount * matrix.ColumnCount]; + Array.Copy(matrix.Data, a, a.Length); + + var b = new[] { 1.0, 2.0, 3.0, 4.0, 5.0, 6.0 }; + var x = new double[matrix.ColumnCount * 2]; + Control.LinearAlgebraProvider.QRSolve(a, matrix.RowCount, matrix.ColumnCount, b, 2, x, QRMethod.Thin); + + NotModified(3, 3, a, matrix); + + var mx = new DenseMatrix(matrix.ColumnCount, 2, x); + var mb = matrix * mx; + + AssertHelpers.AlmostEqual(mb[0, 0], b[0], 14); + AssertHelpers.AlmostEqual(mb[1, 0], b[1], 14); + AssertHelpers.AlmostEqual(mb[2, 0], b[2], 14); + AssertHelpers.AlmostEqual(mb[0, 1], b[3], 14); + AssertHelpers.AlmostEqual(mb[1, 1], b[4], 14); + AssertHelpers.AlmostEqual(mb[2, 1], b[5], 14); + } + + /// + /// Can solve Ax=b using thin QR factorization with a tall A matrix. + /// + [Test] + public void CanSolveUsingThinQRTallMatrix() + { + var matrix = _matrices["Tall3x2"]; + var a = new double[matrix.RowCount * matrix.ColumnCount]; + Array.Copy(matrix.Data, a, a.Length); + + var b = new[] { 1.0, 2.0, 3.0, 4.0, 5.0, 6.0 }; + var x = new double[matrix.ColumnCount * 2]; + Control.LinearAlgebraProvider.QRSolve(a, matrix.RowCount, matrix.ColumnCount, b, 2, x, QRMethod.Thin); + + NotModified(3, 2, a, matrix); + + var mb = new DenseMatrix(matrix.RowCount, 2, b); + var test = (matrix.Transpose() * matrix).Inverse() * matrix.Transpose() * mb; + + AssertHelpers.AlmostEqual(test[0, 0], x[0], 14); + AssertHelpers.AlmostEqual(test[1, 0], x[1], 14); + AssertHelpers.AlmostEqual(test[0, 1], x[2], 14); + AssertHelpers.AlmostEqual(test[1, 1], x[3], 14); + } + + /// + /// Can solve Ax=b using thin QR factorization with a square A matrix + /// using a work array. + /// + [Test] + public void CanSolveUsingThinQRSquareMatrixUsingWorkArray() + { + var matrix = _matrices["Square3x3"]; + var a = new double[matrix.RowCount * matrix.ColumnCount]; + Array.Copy(matrix.Data, a, a.Length); + + var b = new[] { 1.0, 2.0, 3.0, 4.0, 5.0, 6.0 }; + var x = new double[matrix.ColumnCount * 2]; + var work = new double[matrix.RowCount * matrix.ColumnCount]; + Control.LinearAlgebraProvider.QRSolve(a, matrix.RowCount, matrix.ColumnCount, b, 2, x, work, QRMethod.Thin); + + NotModified(3, 3, a, matrix); + + var mx = new DenseMatrix(matrix.ColumnCount, 2, x); + var mb = matrix * mx; + + AssertHelpers.AlmostEqual(mb[0, 0], b[0], 14); + AssertHelpers.AlmostEqual(mb[1, 0], b[1], 14); + AssertHelpers.AlmostEqual(mb[2, 0], b[2], 14); + AssertHelpers.AlmostEqual(mb[0, 1], b[3], 14); + AssertHelpers.AlmostEqual(mb[1, 1], b[4], 14); + AssertHelpers.AlmostEqual(mb[2, 1], b[5], 14); + } + + /// + /// Can solve Ax=b using thin QR factorization with a tall A matrix + /// using a work array. + /// + [Test] + public void CanSolveUsingThinQRTallMatrixUsingWorkArray() + { + var matrix = _matrices["Tall3x2"]; + var a = new double[matrix.RowCount * matrix.ColumnCount]; + Array.Copy(matrix.Data, a, a.Length); + + var b = new[] { 1.0, 2.0, 3.0, 4.0, 5.0, 6.0 }; + var x = new double[matrix.ColumnCount * 2]; + var work = new double[matrix.RowCount * matrix.ColumnCount]; + Control.LinearAlgebraProvider.QRSolve(a, matrix.RowCount, matrix.ColumnCount, b, 2, x, work, QRMethod.Thin); + + NotModified(3, 2, a, matrix); + + var mb = new DenseMatrix(matrix.RowCount, 2, b); + var test = (matrix.Transpose() * matrix).Inverse() * matrix.Transpose() * mb; + + AssertHelpers.AlmostEqual(test[0, 0], x[0], 14); + AssertHelpers.AlmostEqual(test[1, 0], x[1], 14); + AssertHelpers.AlmostEqual(test[0, 1], x[2], 14); + AssertHelpers.AlmostEqual(test[1, 1], x[3], 14); + } + + /// + /// Can solve Ax=b using thin QR factorization with a square A matrix + /// using a factored A matrix. + /// + [Test] + public void CanSolveUsingThinQRSquareMatrixOnFactoredMatrix() + { + var matrix = _matrices["Square3x3"]; + var a = new double[matrix.RowCount * matrix.ColumnCount]; + Array.Copy(matrix.Data, a, a.Length); + + var tau = new double[matrix.ColumnCount]; + var r = new double[matrix.ColumnCount * matrix.ColumnCount]; + Control.LinearAlgebraProvider.ThinQRFactor(a, matrix.RowCount, matrix.ColumnCount, r, tau); + + var b = new[] { 1.0, 2.0, 3.0, 4.0, 5.0, 6.0 }; + var x = new double[matrix.ColumnCount * 2]; + Control.LinearAlgebraProvider.QRSolveFactored(a, r, matrix.RowCount, matrix.ColumnCount, tau, b, 2, x, QRMethod.Thin); + + var mx = new DenseMatrix(matrix.ColumnCount, 2, x); + var mb = matrix * mx; + + AssertHelpers.AlmostEqual(mb[0, 0], b[0], 14); + AssertHelpers.AlmostEqual(mb[1, 0], b[1], 14); + AssertHelpers.AlmostEqual(mb[2, 0], b[2], 14); + AssertHelpers.AlmostEqual(mb[0, 1], b[3], 14); + AssertHelpers.AlmostEqual(mb[1, 1], b[4], 14); + AssertHelpers.AlmostEqual(mb[2, 1], b[5], 14); + } + + /// + /// Can solve Ax=b using thin QR factorization with a tall A matrix + /// using a factored A matrix. + /// + [Test] + public void CanSolveUsingThinQRTallMatrixOnFactoredMatrix() + { + var matrix = _matrices["Tall3x2"]; + var a = new double[matrix.RowCount * matrix.ColumnCount]; + Array.Copy(matrix.Data, a, a.Length); + + var tau = new double[matrix.ColumnCount]; + var r = new double[matrix.ColumnCount * matrix.ColumnCount]; + Control.LinearAlgebraProvider.ThinQRFactor(a, matrix.RowCount, matrix.ColumnCount, r, tau); + + var b = new[] { 1.0, 2.0, 3.0, 4.0, 5.0, 6.0 }; + var x = new double[matrix.ColumnCount * 2]; + Control.LinearAlgebraProvider.QRSolveFactored(a, r, matrix.RowCount, matrix.ColumnCount, tau, b, 2, x, QRMethod.Thin); + + var mb = new DenseMatrix(matrix.RowCount, 2, b); + var test = (matrix.Transpose() * matrix).Inverse() * matrix.Transpose() * mb; + + AssertHelpers.AlmostEqual(test[0, 0], x[0], 14); + AssertHelpers.AlmostEqual(test[1, 0], x[1], 14); + AssertHelpers.AlmostEqual(test[0, 1], x[2], 14); + AssertHelpers.AlmostEqual(test[1, 1], x[3], 14); + } + + /// + /// Can solve Ax=b using thin QR factorization with a square A matrix + /// using a factored A matrix with a work array. + /// + [Test] + public void CanSolveUsingThinQRSquareMatrixOnFactoredMatrixWithWorkArray() + { + var matrix = _matrices["Square3x3"]; + var a = new double[matrix.RowCount * matrix.ColumnCount]; + Array.Copy(matrix.Data, a, a.Length); + + var tau = new double[matrix.ColumnCount]; + var r = new double[matrix.ColumnCount * matrix.ColumnCount]; + var work = new double[2048]; + Control.LinearAlgebraProvider.ThinQRFactor(a, matrix.RowCount, matrix.ColumnCount, r, tau, work); + + var b = new[] { 1.0, 2.0, 3.0, 4.0, 5.0, 6.0 }; + var x = new double[matrix.ColumnCount * 2]; + Control.LinearAlgebraProvider.QRSolveFactored(a, r, matrix.RowCount, matrix.ColumnCount, tau, b, 2, x, work, QRMethod.Thin); + + var mx = new DenseMatrix(matrix.ColumnCount, 2, x); + var mb = matrix * mx; + + AssertHelpers.AlmostEqual(mb[0, 0], b[0], 14); + AssertHelpers.AlmostEqual(mb[1, 0], b[1], 14); + AssertHelpers.AlmostEqual(mb[2, 0], b[2], 14); + AssertHelpers.AlmostEqual(mb[0, 1], b[3], 14); + AssertHelpers.AlmostEqual(mb[1, 1], b[4], 14); + AssertHelpers.AlmostEqual(mb[2, 1], b[5], 14); + } + + /// + /// Can solve Ax=b using thin QR factorization with a tall A matrix + /// using a factored A matrix with a work array. + /// + [Test] + public void CanSolveUsingThinQRTallMatrixOnFactoredMatrixWithWorkArray() + { + var matrix = _matrices["Tall3x2"]; + var a = new double[matrix.RowCount * matrix.ColumnCount]; + Array.Copy(matrix.Data, a, a.Length); + + var tau = new double[matrix.ColumnCount]; + var r = new double[matrix.ColumnCount * matrix.ColumnCount]; + var work = new double[2048]; + Control.LinearAlgebraProvider.ThinQRFactor(a, matrix.RowCount, matrix.ColumnCount, r, tau, work); + + var b = new[] { 1.0, 2.0, 3.0, 4.0, 5.0, 6.0 }; + var x = new double[matrix.ColumnCount * 2]; + Control.LinearAlgebraProvider.QRSolveFactored(a, r, matrix.RowCount, matrix.ColumnCount, tau, b, 2, x, work, QRMethod.Thin ); + + var mb = new DenseMatrix(matrix.RowCount, 2, b); + var test = (matrix.Transpose() * matrix).Inverse() * matrix.Transpose() * mb; + + AssertHelpers.AlmostEqual(test[0, 0], x[0], 14); + AssertHelpers.AlmostEqual(test[1, 0], x[1], 14); + AssertHelpers.AlmostEqual(test[0, 1], x[2], 14); + AssertHelpers.AlmostEqual(test[1, 1], x[3], 14); + } + /// /// Can compute the SVD factorization of a square matrix. /// diff --git a/src/UnitTests/LinearAlgebraProviderTests/Single/LinearAlgebraProviderTests.cs b/src/UnitTests/LinearAlgebraProviderTests/Single/LinearAlgebraProviderTests.cs index 56ce00d2..d5e30da8 100644 --- a/src/UnitTests/LinearAlgebraProviderTests/Single/LinearAlgebraProviderTests.cs +++ b/src/UnitTests/LinearAlgebraProviderTests/Single/LinearAlgebraProviderTests.cs @@ -24,6 +24,8 @@ // OTHER DEALINGS IN THE SOFTWARE. // +using MathNet.Numerics.LinearAlgebra.Generic.Factorization; + namespace MathNet.Numerics.UnitTests.LinearAlgebraProviderTests.Single { using System; @@ -795,6 +797,115 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraProviderTests.Single } } + /// + /// Can compute thin QR factorization of a square matrix. + /// + [Test] + public void CanComputeThinQRFactorSquareMatrix() + { + var matrix = _matrices["Square3x3"]; + var r = new float[matrix.ColumnCount * matrix.ColumnCount]; + var tau = new float[3]; + var q = new float[matrix.RowCount * matrix.ColumnCount]; + Array.Copy(matrix.Data, q, q.Length); + + Control.LinearAlgebraProvider.ThinQRFactor(q, matrix.RowCount, matrix.ColumnCount, r, tau); + + var mq = new DenseMatrix(matrix.RowCount, matrix.ColumnCount, q); + var mr = new DenseMatrix(matrix.ColumnCount, matrix.ColumnCount, r); + var a = mq * mr; + + for (var row = 0; row < matrix.RowCount; row++) + { + for (var col = 0; col < matrix.ColumnCount; col++) + { + AssertHelpers.AlmostEqual(matrix[row, col], a[row, col], 6); + } + } + } + + /// + /// Can compute thin QR factorization of a tall matrix. + /// + [Test] + public void CanComputeThinQRFactorTallMatrix() + { + var matrix = _matrices["Tall3x2"]; + var r = new float[matrix.ColumnCount * matrix.ColumnCount]; + var tau = new float[3]; + var q = new float[matrix.RowCount * matrix.ColumnCount]; + Array.Copy(matrix.Data, q, q.Length); + + Control.LinearAlgebraProvider.ThinQRFactor(q, matrix.RowCount, matrix.ColumnCount, r, tau); + + var mq = new DenseMatrix(matrix.RowCount, matrix.ColumnCount, q); + var mr = new DenseMatrix(matrix.ColumnCount, matrix.ColumnCount, r); + var a = mq * mr; + + for (var row = 0; row < matrix.RowCount; row++) + { + for (var col = 0; col < matrix.ColumnCount; col++) + { + AssertHelpers.AlmostEqual(matrix[row, col], a[row, col], 6); + } + } + } + + /// + /// Can compute thin QR factorization of a square matrix using a work array. + /// + [Test] + public void CanComputeThinQRFactorSquareMatrixWithWorkArray() + { + var matrix = _matrices["Square3x3"]; + var r = new float[matrix.ColumnCount * matrix.ColumnCount]; + var tau = new float[3]; + var q = new float[matrix.RowCount * matrix.ColumnCount]; + Array.Copy(matrix.Data, q, q.Length); + + var work = new float[matrix.RowCount * matrix.ColumnCount]; + Control.LinearAlgebraProvider.ThinQRFactor(q, matrix.RowCount, matrix.ColumnCount, r, tau, work); + + var mq = new DenseMatrix(matrix.RowCount, matrix.ColumnCount, q); + var mr = new DenseMatrix(matrix.ColumnCount, matrix.ColumnCount, r); + var a = mq * mr; + + for (var row = 0; row < matrix.RowCount; row++) + { + for (var col = 0; col < matrix.ColumnCount; col++) + { + AssertHelpers.AlmostEqual(matrix[row, col], a[row, col], 6); + } + } + } + + /// + /// Can compute thin QR factorization of a tall matrix using a work matrix. + /// + [Test] + public void CanComputeThinQRFactorTallMatrixWithWorkArray() + { + var matrix = _matrices["Tall3x2"]; + var r = new float[matrix.ColumnCount * matrix.ColumnCount]; + var tau = new float[3]; + var q = new float[matrix.RowCount * matrix.ColumnCount]; + Array.Copy(matrix.Data, q, q.Length); + + var work = new float[matrix.RowCount * matrix.ColumnCount]; + Control.LinearAlgebraProvider.ThinQRFactor(q, matrix.RowCount, matrix.ColumnCount, r, tau, work); + + var mq = new DenseMatrix(matrix.RowCount, matrix.ColumnCount, q); + var mr = new DenseMatrix(matrix.ColumnCount, matrix.ColumnCount, r); + var a = mq * mr; + for (var row = 0; row < matrix.RowCount; row++) + { + for (var col = 0; col < matrix.ColumnCount; col++) + { + AssertHelpers.AlmostEqual(matrix[row, col], a[row, col], 6); + } + } + } + /// /// Can solve Ax=b using QR factorization with a square A matrix. /// @@ -1021,6 +1132,232 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraProviderTests.Single AssertHelpers.AlmostEqual(test[1, 1], x[3], 6); } + /// + /// Can solve Ax=b using thin QR factorization with a square A matrix. + /// + [Test] + public void CanSolveUsingThinQRSquareMatrix() + { + var matrix = _matrices["Square3x3"]; + var a = new float[matrix.RowCount * matrix.ColumnCount]; + Array.Copy(matrix.Data, a, a.Length); + + var b = new[] { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f }; + var x = new float[matrix.ColumnCount * 2]; + Control.LinearAlgebraProvider.QRSolve(a, matrix.RowCount, matrix.ColumnCount, b, 2, x, QRMethod.Thin); + + NotModified(3, 3, a, matrix); + + var mx = new DenseMatrix(matrix.ColumnCount, 2, x); + var mb = matrix * mx; + + AssertHelpers.AlmostEqual(mb[0, 0], b[0], 5); + AssertHelpers.AlmostEqual(mb[1, 0], b[1], 5); + AssertHelpers.AlmostEqual(mb[2, 0], b[2], 5); + AssertHelpers.AlmostEqual(mb[0, 1], b[3], 5); + AssertHelpers.AlmostEqual(mb[1, 1], b[4], 5); + AssertHelpers.AlmostEqual(mb[2, 1], b[5], 5); + } + + /// + /// Can solve Ax=b using thin QR factorization with a tall A matrix. + /// + [Test] + public void CanSolveUsingThinQRTallMatrix() + { + var matrix = _matrices["Tall3x2"]; + var a = new float[matrix.RowCount * matrix.ColumnCount]; + Array.Copy(matrix.Data, a, a.Length); + + var b = new[] { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f }; + var x = new float[matrix.ColumnCount * 2]; + Control.LinearAlgebraProvider.QRSolve(a, matrix.RowCount, matrix.ColumnCount, b, 2, x, QRMethod.Thin); + + NotModified(3, 2, a, matrix); + + var mb = new DenseMatrix(matrix.RowCount, 2, b); + var test = (matrix.Transpose() * matrix).Inverse() * matrix.Transpose() * mb; + + AssertHelpers.AlmostEqual(test[0, 0], x[0], 6); + AssertHelpers.AlmostEqual(test[1, 0], x[1], 6); + AssertHelpers.AlmostEqual(test[0, 1], x[2], 6); + AssertHelpers.AlmostEqual(test[1, 1], x[3], 6); + } + + /// + /// Can solve Ax=b using thin QR factorization with a square A matrix + /// using a work array. + /// + [Test] + public void CanSolveUsingThinQRSquareMatrixUsingWorkArray() + { + var matrix = _matrices["Square3x3"]; + var a = new float[matrix.RowCount * matrix.ColumnCount]; + Array.Copy(matrix.Data, a, a.Length); + + var b = new[] { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f }; + var x = new float[matrix.ColumnCount * 2]; + var work = new float[matrix.RowCount * matrix.ColumnCount]; + Control.LinearAlgebraProvider.QRSolve(a, matrix.RowCount, matrix.ColumnCount, b, 2, x, work, QRMethod.Thin); + + NotModified(3, 3, a, matrix); + + var mx = new DenseMatrix(matrix.ColumnCount, 2, x); + var mb = matrix * mx; + + AssertHelpers.AlmostEqual(mb[0, 0], b[0], 5); + AssertHelpers.AlmostEqual(mb[1, 0], b[1], 5); + AssertHelpers.AlmostEqual(mb[2, 0], b[2], 5); + AssertHelpers.AlmostEqual(mb[0, 1], b[3], 5); + AssertHelpers.AlmostEqual(mb[1, 1], b[4], 5); + AssertHelpers.AlmostEqual(mb[2, 1], b[5], 5); + } + + /// + /// Can solve Ax=b using thin QR factorization with a tall A matrix + /// using a work array. + /// + [Test] + public void CanSolveUsingThinQRTallMatrixUsingWorkArray() + { + var matrix = _matrices["Tall3x2"]; + var a = new float[matrix.RowCount * matrix.ColumnCount]; + Array.Copy(matrix.Data, a, a.Length); + + var b = new[] { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f }; + var x = new float[matrix.ColumnCount * 2]; + var work = new float[matrix.RowCount * matrix.ColumnCount]; + Control.LinearAlgebraProvider.QRSolve(a, matrix.RowCount, matrix.ColumnCount, b, 2, x, work, QRMethod.Thin); + + NotModified(3, 2, a, matrix); + + var mb = new DenseMatrix(matrix.RowCount, 2, b); + var test = (matrix.Transpose() * matrix).Inverse() * matrix.Transpose() * mb; + + AssertHelpers.AlmostEqual(test[0, 0], x[0], 6); + AssertHelpers.AlmostEqual(test[1, 0], x[1], 6); + AssertHelpers.AlmostEqual(test[0, 1], x[2], 6); + AssertHelpers.AlmostEqual(test[1, 1], x[3], 6); + } + + /// + /// Can solve Ax=b using thin QR factorization with a square A matrix + /// using a factored A matrix. + /// + [Test] + public void CanSolveUsingThinQRSquareMatrixOnFactoredMatrix() + { + var matrix = _matrices["Square3x3"]; + var a = new float[matrix.RowCount * matrix.ColumnCount]; + Array.Copy(matrix.Data, a, a.Length); + + var tau = new float[matrix.ColumnCount]; + var r = new float[matrix.ColumnCount * matrix.ColumnCount]; + Control.LinearAlgebraProvider.ThinQRFactor(a, matrix.RowCount, matrix.ColumnCount, r, tau); + + var b = new[] { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f }; + var x = new float[matrix.ColumnCount * 2]; + Control.LinearAlgebraProvider.QRSolveFactored(a, r, matrix.RowCount, matrix.ColumnCount, tau, b, 2, x, QRMethod.Thin); + + var mx = new DenseMatrix(matrix.ColumnCount, 2, x); + var mb = matrix * mx; + + AssertHelpers.AlmostEqual(mb[0, 0], b[0], 5); + AssertHelpers.AlmostEqual(mb[1, 0], b[1], 5); + AssertHelpers.AlmostEqual(mb[2, 0], b[2], 5); + AssertHelpers.AlmostEqual(mb[0, 1], b[3], 5); + AssertHelpers.AlmostEqual(mb[1, 1], b[4], 5); + AssertHelpers.AlmostEqual(mb[2, 1], b[5], 5); + } + + /// + /// Can solve Ax=b using thin QR factorization with a tall A matrix + /// using a factored A matrix. + /// + [Test] + public void CanSolveUsingThinQRTallMatrixOnFactoredMatrix() + { + var matrix = _matrices["Tall3x2"]; + var a = new float[matrix.RowCount * matrix.ColumnCount]; + Array.Copy(matrix.Data, a, a.Length); + + var tau = new float[matrix.ColumnCount]; + var r = new float[matrix.ColumnCount * matrix.ColumnCount]; + Control.LinearAlgebraProvider.ThinQRFactor(a, matrix.RowCount, matrix.ColumnCount, r, tau); + + var b = new[] { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f }; + var x = new float[matrix.ColumnCount * 2]; + Control.LinearAlgebraProvider.QRSolveFactored(a, r, matrix.RowCount, matrix.ColumnCount, tau, b, 2, x, QRMethod.Thin); + + var mb = new DenseMatrix(matrix.RowCount, 2, b); + var test = (matrix.Transpose() * matrix).Inverse() * matrix.Transpose() * mb; + + AssertHelpers.AlmostEqual(test[0, 0], x[0], 6); + AssertHelpers.AlmostEqual(test[1, 0], x[1], 6); + AssertHelpers.AlmostEqual(test[0, 1], x[2], 6); + AssertHelpers.AlmostEqual(test[1, 1], x[3], 6); + } + + /// + /// Can solve Ax=b using thin QR factorization with a square A matrix + /// using a factored A matrix with a work array. + /// + [Test] + public void CanSolveUsingThinQRSquareMatrixOnFactoredMatrixWithWorkArray() + { + var matrix = _matrices["Square3x3"]; + var a = new float[matrix.RowCount * matrix.ColumnCount]; + Array.Copy(matrix.Data, a, a.Length); + + var tau = new float[matrix.ColumnCount]; + var r = new float[matrix.ColumnCount * matrix.ColumnCount]; + var work = new float[2048]; + Control.LinearAlgebraProvider.ThinQRFactor(a, matrix.RowCount, matrix.ColumnCount, r, tau, work); + + var b = new[] { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f }; + var x = new float[matrix.ColumnCount * 2]; + Control.LinearAlgebraProvider.QRSolveFactored(a, r, matrix.RowCount, matrix.ColumnCount, tau, b, 2, x, work, QRMethod.Thin); + + var mx = new DenseMatrix(matrix.ColumnCount, 2, x); + var mb = matrix * mx; + + AssertHelpers.AlmostEqual(mb[0, 0], b[0], 5); + AssertHelpers.AlmostEqual(mb[1, 0], b[1], 5); + AssertHelpers.AlmostEqual(mb[2, 0], b[2], 5); + AssertHelpers.AlmostEqual(mb[0, 1], b[3], 5); + AssertHelpers.AlmostEqual(mb[1, 1], b[4], 5); + AssertHelpers.AlmostEqual(mb[2, 1], b[5], 5); + } + + /// + /// Can solve Ax=b using thin QR factorization with a tall A matrix + /// using a factored A matrix with a work array. + /// + [Test] + public void CanSolveUsingThinQRTallMatrixOnFactoredMatrixWithWorkArray() + { + var matrix = _matrices["Tall3x2"]; + var a = new float[matrix.RowCount * matrix.ColumnCount]; + Array.Copy(matrix.Data, a, a.Length); + + var tau = new float[matrix.ColumnCount]; + var r = new float[matrix.ColumnCount * matrix.ColumnCount]; + var work = new float[2048]; + Control.LinearAlgebraProvider.ThinQRFactor(a, matrix.RowCount, matrix.ColumnCount, r, tau, work); + + var b = new[] { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f }; + var x = new float[matrix.ColumnCount * 2]; + Control.LinearAlgebraProvider.QRSolveFactored(a, r, matrix.RowCount, matrix.ColumnCount, tau, b, 2, x, work, QRMethod.Thin); + + var mb = new DenseMatrix(matrix.RowCount, 2, b); + var test = (matrix.Transpose() * matrix).Inverse() * matrix.Transpose() * mb; + + AssertHelpers.AlmostEqual(test[0, 0], x[0], 6); + AssertHelpers.AlmostEqual(test[1, 0], x[1], 6); + AssertHelpers.AlmostEqual(test[0, 1], x[2], 6); + AssertHelpers.AlmostEqual(test[1, 1], x[3], 6); + } + /// /// Can compute the SVD factorization of a square matrix. /// diff --git a/src/UnitTests/LinearAlgebraTests/Complex/Factorization/QRTests.cs b/src/UnitTests/LinearAlgebraTests/Complex/Factorization/QRTests.cs index e7d56d70..0be6f6e6 100644 --- a/src/UnitTests/LinearAlgebraTests/Complex/Factorization/QRTests.cs +++ b/src/UnitTests/LinearAlgebraTests/Complex/Factorization/QRTests.cs @@ -1,4 +1,4 @@ -// +// // Math.NET Numerics, part of the Math.NET Project // http://numerics.mathdotnet.com // http://github.com/mathnet/mathnet-numerics @@ -88,6 +88,38 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex.Factorization } } + /// + /// Can factorize identity matrix using thin QR. + /// + /// Matrix order. + [TestCase(1)] + [TestCase(10)] + [TestCase(100)] + public void CanFactorizeIdentityUsingThinQR(int order) + { + var matrixI = DenseMatrix.Identity(order); + var factorQR = matrixI.QR(QRMethod.Thin); + var r = factorQR.R; + + Assert.AreEqual(matrixI.ColumnCount, r.RowCount); + Assert.AreEqual(matrixI.ColumnCount, r.ColumnCount); + + for (var i = 0; i < r.RowCount; i++) + { + for (var j = 0; j < r.ColumnCount; j++) + { + if (i == j) + { + Assert.AreEqual(1.0, r[i, j].Magnitude); + } + else + { + Assert.AreEqual(Complex.Zero, r[i, j]); + } + } + } + } + /// /// Identity determinant is one. /// @@ -151,6 +183,64 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex.Factorization } } + /// + /// Can factorize a random matrix using thin QR. + /// + /// Matrix row number. + /// Matrix column number. + [TestCase(1, 1)] + [TestCase(2, 2)] + [TestCase(5, 5)] + [TestCase(10, 6)] + [TestCase(50, 48)] + [TestCase(100, 98)] + public void CanFactorizeRandomMatrixUsingThinQR(int row, int column) + { + var matrixA = MatrixLoader.GenerateRandomDenseMatrix(row, column); + var factorQR = matrixA.QR(QRMethod.Thin); + var q = factorQR.Q; + var r = factorQR.R; + + // Make sure the R has the right dimensions. + Assert.AreEqual(column, r.RowCount); + Assert.AreEqual(column, r.ColumnCount); + + // Make sure the Q has the right dimensions. + Assert.AreEqual(row, q.RowCount); + Assert.AreEqual(column, q.ColumnCount); + + // Make sure the R factor is upper triangular. + for (var i = 0; i < r.RowCount; i++) + { + for (var j = 0; j < r.ColumnCount; j++) + { + if (i > j) + { + Assert.AreEqual(Complex.Zero, r[i, j]); + } + } + } + + // Make sure the Q is unitary --> (Q*)x(Q) = I + var matrixQсtQ = q.ConjugateTranspose() * q; + for (var i = 0; i < matrixQсtQ.RowCount; i++) + { + for (var j = 0; j < matrixQсtQ.ColumnCount; j++) + { + if (i == j) + { + Assert.AreEqual(matrixQсtQ[i, j].Real, 1.0f, 1e-3f); + Assert.AreEqual(matrixQсtQ[i, j].Imaginary, 0.0f, 1e-3f); + } + else + { + Assert.AreEqual(matrixQсtQ[i, j].Real, 0.0f, 1e-3f); + Assert.AreEqual(matrixQсtQ[i, j].Imaginary, 0.0f, 1e-3f); + } + } + } + } + /// /// Can solve a system of linear equations for a random vector (Ax=b). /// @@ -339,5 +429,194 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex.Factorization } } } + + /// + /// Can solve a system of linear equations for a random vector (Ax=b). + /// + /// Matrix order. + [TestCase(1)] + [TestCase(2)] + [TestCase(5)] + [TestCase(10)] + [TestCase(50)] + [TestCase(100)] + public void CanSolveForRandomVectorUsingThinQR(int order) + { + var matrixA = MatrixLoader.GenerateRandomDenseMatrix(order, order); + var matrixACopy = matrixA.Clone(); + var factorQR = matrixA.QR(QRMethod.Thin); + + var vectorb = MatrixLoader.GenerateRandomDenseVector(order); + var resultx = factorQR.Solve(vectorb); + + Assert.AreEqual(matrixA.ColumnCount, resultx.Count); + + var matrixBReconstruct = matrixA * resultx; + + // Check the reconstruction. + for (var i = 0; i < order; i++) + { + AssertHelpers.AlmostEqual(vectorb[i], matrixBReconstruct[i], 9); + } + + // Make sure A didn't change. + for (var i = 0; i < matrixA.RowCount; i++) + { + for (var j = 0; j < matrixA.ColumnCount; j++) + { + Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]); + } + } + } + + /// + /// Can solve a system of linear equations for a random matrix (AX=B). + /// + /// Matrix order. + [TestCase(1)] + [TestCase(2)] + [TestCase(5)] + [TestCase(10)] + [TestCase(50)] + [TestCase(100)] + public void CanSolveForRandomMatrixUsingThinQR(int order) + { + var matrixA = MatrixLoader.GenerateRandomDenseMatrix(order, order); + var matrixACopy = matrixA.Clone(); + var factorQR = matrixA.QR(QRMethod.Thin); + + 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++) + { + AssertHelpers.AlmostEqual(matrixB[i, j], matrixBReconstruct[i, j], 9); + } + } + + // Make sure A didn't change. + for (var i = 0; i < matrixA.RowCount; i++) + { + for (var j = 0; j < matrixA.ColumnCount; j++) + { + Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]); + } + } + } + + /// + /// Can solve for a random vector into a result vector. + /// + /// Matrix order. + [TestCase(1)] + [TestCase(2)] + [TestCase(5)] + [TestCase(10)] + [TestCase(50)] + [TestCase(100)] + public void CanSolveForRandomVectorWhenResultVectorGivenUsingThinQR(int order) + { + var matrixA = MatrixLoader.GenerateRandomDenseMatrix(order, order); + var matrixACopy = matrixA.Clone(); + var factorQR = matrixA.QR(QRMethod.Thin); + 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 matrixBReconstruct = matrixA * resultx; + + // Check the reconstruction. + for (var i = 0; i < vectorb.Count; i++) + { + AssertHelpers.AlmostEqual(vectorb[i], matrixBReconstruct[i], 9); + } + + // Make sure A didn't change. + for (var i = 0; i < matrixA.RowCount; i++) + { + for (var j = 0; j < matrixA.ColumnCount; j++) + { + Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]); + } + } + + // Make sure b didn't change. + for (var i = 0; i < vectorb.Count; i++) + { + Assert.AreEqual(vectorbCopy[i], vectorb[i]); + } + } + + /// + /// Can solve a system of linear equations for a random matrix (AX=B) into a result matrix. + /// + /// Matrix order. + [TestCase(1)] + [TestCase(2)] + [TestCase(5)] + [TestCase(10)] + [TestCase(50)] + [TestCase(100)] + public void CanSolveForRandomMatrixWhenResultMatrixGivenUsingThinQR(int order) + { + var matrixA = MatrixLoader.GenerateRandomDenseMatrix(order, order); + var matrixACopy = matrixA.Clone(); + var factorQR = matrixA.QR(QRMethod.Thin); + + 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++) + { + AssertHelpers.AlmostEqual(matrixB[i, j], matrixBReconstruct[i, j], 9); + } + } + + // 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/Complex/Factorization/UserQRTests.cs b/src/UnitTests/LinearAlgebraTests/Complex/Factorization/UserQRTests.cs index 43ea2fcf..f1e1d399 100644 --- a/src/UnitTests/LinearAlgebraTests/Complex/Factorization/UserQRTests.cs +++ b/src/UnitTests/LinearAlgebraTests/Complex/Factorization/UserQRTests.cs @@ -24,6 +24,8 @@ // OTHER DEALINGS IN THE SOFTWARE. // +using MathNet.Numerics.LinearAlgebra.Generic.Factorization; + namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex.Factorization { using System; @@ -88,6 +90,38 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex.Factorization } } + /// + /// Can factorize identity matrix using thin QR. + /// + /// Matrix order. + [TestCase(1)] + [TestCase(10)] + [TestCase(100)] + public void CanFactorizeIdentityUsingThinQR(int order) + { + var matrixI = UserDefinedMatrix.Identity(order); + var factorQR = matrixI.QR(QRMethod.Thin); + var r = factorQR.R; + + Assert.AreEqual(matrixI.RowCount, r.RowCount); + Assert.AreEqual(matrixI.ColumnCount, r.ColumnCount); + + for (var i = 0; i < r.RowCount; i++) + { + for (var j = 0; j < r.ColumnCount; j++) + { + if (i == j) + { + Assert.AreEqual(-Complex.One, r[i, j]); + } + else + { + Assert.AreEqual(Complex.Zero, r[i, j]); + } + } + } + } + /// /// Identity determinant is one. /// @@ -151,6 +185,55 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex.Factorization } } + /// + /// Can factorize a random matrix using thin QR. + /// + /// Matrix row number. + /// Matrix column number. + [TestCase(1, 1)] + [TestCase(2, 2)] + [TestCase(5, 5)] + [TestCase(10, 6)] + [TestCase(50, 48)] + [TestCase(100, 98)] + public void CanFactorizeRandomMatrixUsingThinQR(int row, int column) + { + var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(row, column); + var factorQR = matrixA.QR(QRMethod.Thin); + var q = factorQR.Q; + var r = factorQR.R; + + // Make sure the R has the right dimensions. + Assert.AreEqual(column, r.RowCount); + Assert.AreEqual(column, r.ColumnCount); + + // Make sure the Q has the right dimensions. + Assert.AreEqual(row, q.RowCount); + Assert.AreEqual(column, q.ColumnCount); + + // Make sure the R factor is upper triangular. + for (var i = 0; i < r.RowCount; i++) + { + for (var j = 0; j < r.ColumnCount; j++) + { + if (i > j) + { + Assert.AreEqual(Complex.Zero, r[i, j]); + } + } + } + + // Make sure the Q*R is the original matrix. + var matrixQfromR = q * r; + for (var i = 0; i < matrixQfromR.RowCount; i++) + { + for (var j = 0; j < matrixQfromR.ColumnCount; j++) + { + AssertHelpers.AlmostEqual(matrixA[i, j], matrixQfromR[i, j], 9); + } + } + } + /// /// Can solve a system of linear equations for a random vector (Ax=b). /// @@ -339,5 +422,194 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex.Factorization } } } + + /// + /// Can solve a system of linear equations for a random vector (Ax=b). + /// + /// Matrix order. + [TestCase(1)] + [TestCase(2)] + [TestCase(5)] + [TestCase(10)] + [TestCase(50)] + [TestCase(100)] + public void CanSolveForRandomVectorUsingThinQR(int order) + { + var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order); + var matrixACopy = matrixA.Clone(); + var factorQR = matrixA.QR(QRMethod.Thin); + + var vectorb = MatrixLoader.GenerateRandomUserDefinedVector(order); + var resultx = factorQR.Solve(vectorb); + + Assert.AreEqual(matrixA.ColumnCount, resultx.Count); + + var matrixBReconstruct = matrixA * resultx; + + // Check the reconstruction. + for (var i = 0; i < order; i++) + { + AssertHelpers.AlmostEqual(vectorb[i], matrixBReconstruct[i], 9); + } + + // Make sure A didn't change. + for (var i = 0; i < matrixA.RowCount; i++) + { + for (var j = 0; j < matrixA.ColumnCount; j++) + { + Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]); + } + } + } + + /// + /// Can solve a system of linear equations for a random matrix (AX=B). + /// + /// Matrix order. + [TestCase(1)] + [TestCase(2)] + [TestCase(5)] + [TestCase(10)] + [TestCase(50)] + [TestCase(100)] + public void CanSolveForRandomMatrixUsingThinQR(int order) + { + var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order); + var matrixACopy = matrixA.Clone(); + var factorQR = matrixA.QR(QRMethod.Thin); + + 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++) + { + AssertHelpers.AlmostEqual(matrixB[i, j], matrixBReconstruct[i, j], 9); + } + } + + // Make sure A didn't change. + for (var i = 0; i < matrixA.RowCount; i++) + { + for (var j = 0; j < matrixA.ColumnCount; j++) + { + Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]); + } + } + } + + /// + /// Can solve for a random vector into a result vector. + /// + /// Matrix order. + [TestCase(1)] + [TestCase(2)] + [TestCase(5)] + [TestCase(10)] + [TestCase(50)] + [TestCase(100)] + public void CanSolveForRandomVectorWhenResultVectorGivenUsingThinQR(int order) + { + var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order); + var matrixACopy = matrixA.Clone(); + var factorQR = matrixA.QR(QRMethod.Thin); + 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 matrixBReconstruct = matrixA * resultx; + + // Check the reconstruction. + for (var i = 0; i < vectorb.Count; i++) + { + AssertHelpers.AlmostEqual(vectorb[i], matrixBReconstruct[i], 9); + } + + // Make sure A didn't change. + for (var i = 0; i < matrixA.RowCount; i++) + { + for (var j = 0; j < matrixA.ColumnCount; j++) + { + Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]); + } + } + + // Make sure b didn't change. + for (var i = 0; i < vectorb.Count; i++) + { + Assert.AreEqual(vectorbCopy[i], vectorb[i]); + } + } + + /// + /// Can solve a system of linear equations for a random matrix (AX=B) into a result matrix. + /// + /// Matrix order. + [TestCase(1)] + [TestCase(2)] + [TestCase(5)] + [TestCase(10)] + [TestCase(50)] + [TestCase(100)] + public void CanSolveForRandomMatrixWhenResultMatrixGivenUsingThinQR(int order) + { + var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order); + var matrixACopy = matrixA.Clone(); + var factorQR = matrixA.QR(QRMethod.Thin); + + 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++) + { + AssertHelpers.AlmostEqual(matrixB[i, j], matrixBReconstruct[i, j], 9); + } + } + + // 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/Complex32/Factorization/QRTests.cs b/src/UnitTests/LinearAlgebraTests/Complex32/Factorization/QRTests.cs index 4e4b4ee0..029b42e1 100644 --- a/src/UnitTests/LinearAlgebraTests/Complex32/Factorization/QRTests.cs +++ b/src/UnitTests/LinearAlgebraTests/Complex32/Factorization/QRTests.cs @@ -88,6 +88,39 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex32.Factorization } } + /// + /// Can factorize identity matrix using thin QR. + /// + /// Matrix order. + [TestCase(1)] + [TestCase(10)] + [TestCase(100)] + public void CanFactorizeIdentityUsingThinQR(int order) + { + var matrixI = DenseMatrix.Identity(order); + var factorQR = matrixI.QR(QRMethod.Thin); + var r = factorQR.R; + + Assert.AreEqual(matrixI.ColumnCount, r.RowCount); + Assert.AreEqual(matrixI.ColumnCount, r.ColumnCount); + + for (var i = 0; i < r.RowCount; i++) + { + for (var j = 0; j < r.ColumnCount; j++) + { + if (i == j) + { + Assert.AreEqual(1.0, r[i, j].Magnitude); + } + else + { + Assert.AreEqual(Complex32.Zero, r[i, j]); + } + } + } + } + + /// /// Identity determinant is one. /// @@ -171,6 +204,64 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex32.Factorization } } + /// + /// Can factorize a random matrix using thin QR. + /// + /// Matrix row number. + /// Matrix column number. + [TestCase(1, 1)] + [TestCase(2, 2)] + [TestCase(5, 5)] + [TestCase(10, 6)] + [TestCase(50, 48)] + [TestCase(100, 98)] + public void CanFactorizeRandomMatrixUsingThinQR(int row, int column) + { + var matrixA = MatrixLoader.GenerateRandomDenseMatrix(row, column); + var factorQR = matrixA.QR(QRMethod.Thin); + var q = factorQR.Q; + var r = factorQR.R; + + // Make sure the R has the right dimensions. + Assert.AreEqual(column, r.RowCount); + Assert.AreEqual(column, r.ColumnCount); + + // Make sure the Q has the right dimensions. + Assert.AreEqual(row, q.RowCount); + Assert.AreEqual(column, q.ColumnCount); + + // Make sure the R factor is upper triangular. + for (var i = 0; i < r.RowCount; i++) + { + for (var j = 0; j < r.ColumnCount; j++) + { + if (i > j) + { + Assert.AreEqual(Complex32.Zero, r[i, j]); + } + } + } + + // Make sure the Q is unitary --> (Q*)x(Q) = I + var matrixQсtQ = q.ConjugateTranspose() * q; + for (var i = 0; i < matrixQсtQ.RowCount; i++) + { + for (var j = 0; j < matrixQсtQ.ColumnCount; j++) + { + if (i == j) + { + Assert.AreEqual(matrixQсtQ[i, j].Real, 1.0f, 1e-3f); + Assert.AreEqual(matrixQсtQ[i, j].Imaginary, 0.0f, 1e-3f); + } + else + { + Assert.AreEqual(matrixQсtQ[i, j].Real, 0.0f, 1e-3f); + Assert.AreEqual(matrixQсtQ[i, j].Imaginary, 0.0f, 1e-3f); + } + } + } + } + /// /// Can solve a system of linear equations for a random vector (Ax=b). /// diff --git a/src/UnitTests/LinearAlgebraTests/Complex32/Factorization/UserQRTests.cs b/src/UnitTests/LinearAlgebraTests/Complex32/Factorization/UserQRTests.cs index 04956678..ecff2b28 100644 --- a/src/UnitTests/LinearAlgebraTests/Complex32/Factorization/UserQRTests.cs +++ b/src/UnitTests/LinearAlgebraTests/Complex32/Factorization/UserQRTests.cs @@ -24,6 +24,8 @@ // OTHER DEALINGS IN THE SOFTWARE. // +using MathNet.Numerics.LinearAlgebra.Generic.Factorization; + namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex32.Factorization { using System; @@ -87,6 +89,38 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex32.Factorization } } + /// + /// Can factorize identity matrix using thin QR. + /// + /// Matrix order. + [TestCase(1)] + [TestCase(10)] + [TestCase(100)] + public void CanFactorizeIdentityUsingThinQR(int order) + { + var matrixI = UserDefinedMatrix.Identity(order); + var factorQR = matrixI.QR(QRMethod.Thin); + var r = factorQR.R; + + Assert.AreEqual(matrixI.RowCount, r.RowCount); + Assert.AreEqual(matrixI.ColumnCount, r.ColumnCount); + + for (var i = 0; i < r.RowCount; i++) + { + for (var j = 0; j < r.ColumnCount; j++) + { + if (i == j) + { + Assert.AreEqual(-Complex32.One, r[i, j]); + } + else + { + Assert.AreEqual(Complex32.Zero, r[i, j]); + } + } + } + } + /// /// Identity determinant is one. /// @@ -170,6 +204,56 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex32.Factorization } } + /// + /// Can factorize a random matrix using thin QR. + /// + /// Matrix row number. + /// Matrix column number. + [TestCase(1, 1)] + [TestCase(2, 2)] + [TestCase(5, 5)] + [TestCase(10, 6)] + [TestCase(50, 48)] + [TestCase(100, 98)] + public void CanFactorizeRandomMatrixUsingThinQR(int row, int column) + { + var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(row, column); + var factorQR = matrixA.QR(QRMethod.Thin); + var q = factorQR.Q; + var r = factorQR.R; + + // Make sure the R has the right dimensions. + Assert.AreEqual(column, r.RowCount); + Assert.AreEqual(column, r.ColumnCount); + + // Make sure the Q has the right dimensions. + Assert.AreEqual(row, q.RowCount); + Assert.AreEqual(column, q.ColumnCount); + + // Make sure the R factor is upper triangular. + for (var i = 0; i < r.RowCount; i++) + { + for (var j = 0; j < r.ColumnCount; j++) + { + if (i > j) + { + Assert.AreEqual(Complex32.Zero, r[i, j]); + } + } + } + + // Make sure the Q*R is the original matrix. + var matrixQfromR = q * r; + for (var i = 0; i < matrixQfromR.RowCount; i++) + { + for (var j = 0; j < matrixQfromR.ColumnCount; j++) + { + Assert.AreEqual(matrixA[i, j].Real, matrixQfromR[i, j].Real, 1e-3f); + Assert.AreEqual(matrixA[i, j].Imaginary, matrixQfromR[i, j].Imaginary, 1e-3f); + } + } + } + /// /// Can solve a system of linear equations for a random vector (Ax=b). /// @@ -362,5 +446,198 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex32.Factorization } } } + + /// + /// Can solve a system of linear equations for a random vector (Ax=b). + /// + /// Matrix order. + [TestCase(1)] + [TestCase(2)] + [TestCase(5)] + [TestCase(10)] + [TestCase(50)] + [TestCase(100)] + public void CanSolveForRandomVectorUsingThinQR(int order) + { + var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order); + var matrixACopy = matrixA.Clone(); + var factorQR = matrixA.QR(QRMethod.Thin); + + var vectorb = MatrixLoader.GenerateRandomUserDefinedVector(order); + var resultx = factorQR.Solve(vectorb); + + Assert.AreEqual(matrixA.ColumnCount, resultx.Count); + + var matrixBReconstruct = matrixA * resultx; + + // Check the reconstruction. + for (var i = 0; i < order; i++) + { + Assert.AreEqual(vectorb[i].Real, matrixBReconstruct[i].Real, 1e-3f); + Assert.AreEqual(vectorb[i].Imaginary, matrixBReconstruct[i].Imaginary, 1e-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]); + } + } + } + + /// + /// Can solve a system of linear equations for a random matrix (AX=B). + /// + /// Matrix order. + [TestCase(1)] + [TestCase(2)] + [TestCase(5)] + [TestCase(10)] + [TestCase(50)] + [TestCase(100)] + public void CanSolveForRandomMatrixUsingThinQR(int order) + { + var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order); + var matrixACopy = matrixA.Clone(); + var factorQR = matrixA.QR(QRMethod.Thin); + + 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.AreEqual(matrixB[i, j].Real, matrixBReconstruct[i, j].Real, 1e-3f); + Assert.AreEqual(matrixB[i, j].Imaginary, matrixBReconstruct[i, j].Imaginary, 1e-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]); + } + } + } + + /// + /// Can solve for a random vector into a result vector. + /// + /// Matrix order. + [TestCase(1)] + [TestCase(2)] + [TestCase(5)] + [TestCase(10)] + [TestCase(50)] + [TestCase(100)] + public void CanSolveForRandomVectorWhenResultVectorGivenUsingThinQR(int order) + { + var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order); + var matrixACopy = matrixA.Clone(); + var factorQR = matrixA.QR(QRMethod.Thin); + 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 matrixBReconstruct = matrixA * resultx; + + // Check the reconstruction. + for (var i = 0; i < vectorb.Count; i++) + { + Assert.AreEqual(vectorb[i].Real, matrixBReconstruct[i].Real, 1e-3f); + Assert.AreEqual(vectorb[i].Imaginary, matrixBReconstruct[i].Imaginary, 1e-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 < vectorb.Count; i++) + { + Assert.AreEqual(vectorbCopy[i], vectorb[i]); + } + } + + /// + /// Can solve a system of linear equations for a random matrix (AX=B) into a result matrix. + /// + /// Matrix order. + [TestCase(1)] + [TestCase(2)] + [TestCase(5)] + [TestCase(10)] + [TestCase(50)] + [TestCase(100)] + public void CanSolveForRandomMatrixWhenResultMatrixGivenUsingThinQR(int order) + { + var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order); + var matrixACopy = matrixA.Clone(); + var factorQR = matrixA.QR(QRMethod.Thin); + + 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.AreEqual(matrixB[i, j].Real, matrixBReconstruct[i, j].Real, 1e-3f); + Assert.AreEqual(matrixB[i, j].Imaginary, matrixBReconstruct[i, j].Imaginary, 1e-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/Double/Factorization/QRTests.cs b/src/UnitTests/LinearAlgebraTests/Double/Factorization/QRTests.cs index e8edb44b..03fb1f4b 100644 --- a/src/UnitTests/LinearAlgebraTests/Double/Factorization/QRTests.cs +++ b/src/UnitTests/LinearAlgebraTests/Double/Factorization/QRTests.cs @@ -87,6 +87,38 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double.Factorization } } + /// + /// Can factorize identity matrix using thin QR. + /// + /// Matrix order. + [TestCase(1)] + [TestCase(10)] + [TestCase(100)] + public void CanFactorizeIdentityUsingThinQR(int order) + { + var matrixI = DenseMatrix.Identity(order); + var factorQR = matrixI.QR(QRMethod.Thin); + var r = factorQR.R; + + Assert.AreEqual(matrixI.ColumnCount, r.RowCount); + Assert.AreEqual(matrixI.ColumnCount, r.ColumnCount); + + for (var i = 0; i < r.RowCount; i++) + { + for (var j = 0; j < r.ColumnCount; j++) + { + if (i == j) + { + Assert.AreEqual(1.0, Math.Abs(r[i, j])); + } + else + { + Assert.AreEqual(0.0, r[i, j]); + } + } + } + } + /// /// Identity determinant is one. /// @@ -150,6 +182,55 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double.Factorization } } + /// + /// Can factorize a random matrix using thin QR. + /// + /// Matrix row number. + /// Matrix column number. + [TestCase(1, 1)] + [TestCase(2, 2)] + [TestCase(5, 5)] + [TestCase(10, 6)] + [TestCase(50, 48)] + [TestCase(100, 98)] + public void CanFactorizeRandomMatrixUsingThinQR(int row, int column) + { + var matrixA = MatrixLoader.GenerateRandomDenseMatrix(row, column); + var factorQR = matrixA.QR(QRMethod.Thin); + var q = factorQR.Q; + var r = factorQR.R; + + // Make sure the R has the right dimensions. + Assert.AreEqual(column, r.RowCount); + Assert.AreEqual(column, r.ColumnCount); + + // Make sure the Q has the right dimensions. + Assert.AreEqual(row, q.RowCount); + Assert.AreEqual(column, q.ColumnCount); + + // Make sure the R factor is upper triangular. + for (var i = 0; i < r.RowCount; i++) + { + for (var j = 0; j < r.ColumnCount; j++) + { + if (i > j) + { + Assert.AreEqual(0.0, r[i, j]); + } + } + } + + // Make sure the Q*R is the original matrix. + var matrixQfromR = q * r; + for (var i = 0; i < matrixQfromR.RowCount; i++) + { + for (var j = 0; j < matrixQfromR.ColumnCount; j++) + { + Assert.AreEqual(matrixA[i, j], matrixQfromR[i, j], 1.0e-11); + } + } + } + /// /// Can solve a system of linear equations for a random vector (Ax=b). /// diff --git a/src/UnitTests/LinearAlgebraTests/Double/Factorization/UserQRTests.cs b/src/UnitTests/LinearAlgebraTests/Double/Factorization/UserQRTests.cs index a3404a92..c59039eb 100644 --- a/src/UnitTests/LinearAlgebraTests/Double/Factorization/UserQRTests.cs +++ b/src/UnitTests/LinearAlgebraTests/Double/Factorization/UserQRTests.cs @@ -24,6 +24,8 @@ // OTHER DEALINGS IN THE SOFTWARE. // +using MathNet.Numerics.LinearAlgebra.Generic.Factorization; + namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double.Factorization { using System; @@ -86,6 +88,38 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double.Factorization } } + /// + /// Can factorize identity matrix using thin QR. + /// + /// Matrix order. + [TestCase(1)] + [TestCase(10)] + [TestCase(100)] + public void CanFactorizeIdentityUsingThinQR(int order) + { + var matrixI = UserDefinedMatrix.Identity(order); + var factorQR = matrixI.QR(QRMethod.Thin); + var r = factorQR.R; + + Assert.AreEqual(matrixI.RowCount, r.RowCount); + Assert.AreEqual(matrixI.ColumnCount, r.ColumnCount); + + for (var i = 0; i < r.RowCount; i++) + { + for (var j = 0; j < r.ColumnCount; j++) + { + if (i == j) + { + Assert.AreEqual(-1.0, r[i, j]); + } + else + { + Assert.AreEqual(0.0, r[i, j]); + } + } + } + } + /// /// Identity determinant is one. /// @@ -149,6 +183,55 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double.Factorization } } + /// + /// Can factorize a random matrix using thin QR. + /// + /// Matrix row number. + /// Matrix column number. + [TestCase(1, 1)] + [TestCase(2, 2)] + [TestCase(5, 5)] + [TestCase(10, 6)] + [TestCase(50, 48)] + [TestCase(100, 98)] + public void CanFactorizeRandomMatrixUsingThinQR(int row, int column) + { + var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(row, column); + var factorQR = matrixA.QR(QRMethod.Thin); + var q = factorQR.Q; + var r = factorQR.R; + + // Make sure the R has the right dimensions. + Assert.AreEqual(column, r.RowCount); + Assert.AreEqual(column, r.ColumnCount); + + // Make sure the Q has the right dimensions. + Assert.AreEqual(row, q.RowCount); + Assert.AreEqual(column, q.ColumnCount); + + // Make sure the R factor is upper triangular. + for (var i = 0; i < r.RowCount; i++) + { + for (var j = 0; j < r.ColumnCount; j++) + { + if (i > j) + { + Assert.AreEqual(0.0, r[i, j]); + } + } + } + + // Make sure the Q*R is the original matrix. + var matrixQfromR = q * r; + for (var i = 0; i < matrixQfromR.RowCount; i++) + { + for (var j = 0; j < matrixQfromR.ColumnCount; j++) + { + Assert.AreEqual(matrixA[i, j], matrixQfromR[i, j], 1.0e-11); + } + } + } + /// /// Can solve a system of linear equations for a random vector (Ax=b). /// @@ -337,5 +420,194 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double.Factorization } } } + + /// + /// Can solve a system of linear equations for a random vector (Ax=b). + /// + /// Matrix order. + [TestCase(1)] + [TestCase(2)] + [TestCase(5)] + [TestCase(10)] + [TestCase(50)] + [TestCase(100)] + public void CanSolveForRandomVectorUsingThinQR(int order) + { + var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order); + var matrixACopy = matrixA.Clone(); + var factorQR = matrixA.QR(QRMethod.Thin); + + var vectorb = MatrixLoader.GenerateRandomUserDefinedVector(order); + var resultx = factorQR.Solve(vectorb); + + Assert.AreEqual(matrixA.ColumnCount, resultx.Count); + + var matrixBReconstruct = matrixA * resultx; + + // Check the reconstruction. + for (var i = 0; i < order; i++) + { + Assert.AreEqual(vectorb[i], matrixBReconstruct[i], 1.0e-11); + } + + // Make sure A didn't change. + for (var i = 0; i < matrixA.RowCount; i++) + { + for (var j = 0; j < matrixA.ColumnCount; j++) + { + Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]); + } + } + } + + /// + /// Can solve a system of linear equations for a random matrix (AX=B). + /// + /// Matrix order. + [TestCase(1)] + [TestCase(2)] + [TestCase(5)] + [TestCase(10)] + [TestCase(50)] + [TestCase(100)] + public void CanSolveForRandomMatrixUsingThinQR(int order) + { + var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order); + var matrixACopy = matrixA.Clone(); + var factorQR = matrixA.QR(QRMethod.Thin); + + 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.AreEqual(matrixB[i, j], matrixBReconstruct[i, j], 1.0e-11); + } + } + + // Make sure A didn't change. + for (var i = 0; i < matrixA.RowCount; i++) + { + for (var j = 0; j < matrixA.ColumnCount; j++) + { + Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]); + } + } + } + + /// + /// Can solve for a random vector into a result vector. + /// + /// Matrix order. + [TestCase(1)] + [TestCase(2)] + [TestCase(5)] + [TestCase(10)] + [TestCase(50)] + [TestCase(100)] + public void CanSolveForRandomVectorWhenResultVectorGivenUsingThinQR(int order) + { + var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order); + var matrixACopy = matrixA.Clone(); + var factorQR = matrixA.QR(QRMethod.Thin); + 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 matrixBReconstruct = matrixA * resultx; + + // Check the reconstruction. + for (var i = 0; i < vectorb.Count; i++) + { + Assert.AreEqual(vectorb[i], matrixBReconstruct[i], 1.0e-11); + } + + // Make sure A didn't change. + for (var i = 0; i < matrixA.RowCount; i++) + { + for (var j = 0; j < matrixA.ColumnCount; j++) + { + Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]); + } + } + + // Make sure b didn't change. + for (var i = 0; i < vectorb.Count; i++) + { + Assert.AreEqual(vectorbCopy[i], vectorb[i]); + } + } + + /// + /// Can solve a system of linear equations for a random matrix (AX=B) into a result matrix. + /// + /// Matrix order. + [TestCase(1)] + [TestCase(2)] + [TestCase(5)] + [TestCase(10)] + [TestCase(50)] + [TestCase(100)] + public void CanSolveForRandomMatrixWhenResultMatrixGivenUsingThinQR(int order) + { + var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order); + var matrixACopy = matrixA.Clone(); + var factorQR = matrixA.QR(QRMethod.Thin); + + 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.AreEqual(matrixB[i, j], matrixBReconstruct[i, j], 1.0e-11); + } + } + + // Make sure A didn't change. + for (var i = 0; i < matrixA.RowCount; i++) + { + for (var j = 0; j < matrixA.ColumnCount; j++) + { + Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]); + } + } + + // Make sure B didn't change. + for (var i = 0; i < matrixB.RowCount; i++) + { + for (var j = 0; j < matrixB.ColumnCount; j++) + { + Assert.AreEqual(matrixBCopy[i, j], matrixB[i, j]); + } + } + } } } diff --git a/src/UnitTests/LinearAlgebraTests/Single/Factorization/QRTests.cs b/src/UnitTests/LinearAlgebraTests/Single/Factorization/QRTests.cs index 29980350..05069d08 100644 --- a/src/UnitTests/LinearAlgebraTests/Single/Factorization/QRTests.cs +++ b/src/UnitTests/LinearAlgebraTests/Single/Factorization/QRTests.cs @@ -87,6 +87,39 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single.Factorization } } + /// + /// Can factorize identity matrix using thin QR. + /// + /// Matrix order. + [TestCase(1)] + [TestCase(10)] + [TestCase(100)] + public void CanFactorizeIdentityUsingThinQR(int order) + { + var matrixI = DenseMatrix.Identity(order); + var factorQR = matrixI.QR(QRMethod.Thin); + var r = factorQR.R; + + Assert.AreEqual(matrixI.ColumnCount, r.RowCount); + Assert.AreEqual(matrixI.ColumnCount, r.ColumnCount); + + for (var i = 0; i < r.RowCount; i++) + { + for (var j = 0; j < r.ColumnCount; j++) + { + if (i == j) + { + Assert.AreEqual(1.0, Math.Abs(r[i, j])); + } + else + { + Assert.AreEqual(0.0, r[i, j]); + } + } + } + } + + /// /// Identity determinant is one. /// @@ -150,6 +183,55 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single.Factorization } } + /// + /// Can factorize a random matrix using thin QR. + /// + /// Matrix row number. + /// Matrix column number. + [TestCase(1, 1)] + [TestCase(2, 2)] + [TestCase(5, 5)] + [TestCase(10, 6)] + [TestCase(50, 48)] + [TestCase(100, 98)] + public void CanFactorizeRandomMatrixUsingThinQR(int row, int column) + { + var matrixA = MatrixLoader.GenerateRandomDenseMatrix(row, column); + var factorQR = matrixA.QR(QRMethod.Thin); + var q = factorQR.Q; + var r = factorQR.R; + + // Make sure the R has the right dimensions. + Assert.AreEqual(column, r.RowCount); + Assert.AreEqual(column, r.ColumnCount); + + // Make sure the Q has the right dimensions. + Assert.AreEqual(row, q.RowCount); + Assert.AreEqual(column, q.ColumnCount); + + // Make sure the R factor is upper triangular. + for (var i = 0; i < r.RowCount; i++) + { + for (var j = 0; j < r.ColumnCount; j++) + { + if (i > j) + { + Assert.AreEqual(0.0, r[i, j]); + } + } + } + + // Make sure the Q*R is the original matrix. + var matrixQfromR = q * r; + for (var i = 0; i < matrixQfromR.RowCount; i++) + { + for (var j = 0; j < matrixQfromR.ColumnCount; j++) + { + Assert.AreEqual(matrixA[i, j], matrixQfromR[i, j], 1.0e-4); + } + } + } + /// /// Can solve a system of linear equations for a random vector (Ax=b). /// diff --git a/src/UnitTests/LinearAlgebraTests/Single/Factorization/UserQRTests.cs b/src/UnitTests/LinearAlgebraTests/Single/Factorization/UserQRTests.cs index 9340c2fe..b418829b 100644 --- a/src/UnitTests/LinearAlgebraTests/Single/Factorization/UserQRTests.cs +++ b/src/UnitTests/LinearAlgebraTests/Single/Factorization/UserQRTests.cs @@ -24,6 +24,8 @@ // OTHER DEALINGS IN THE SOFTWARE. // +using MathNet.Numerics.LinearAlgebra.Generic.Factorization; + namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single.Factorization { using System; @@ -86,6 +88,38 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single.Factorization } } + /// + /// Can factorize identity matrix using thin QR. + /// + /// Matrix order. + [TestCase(1)] + [TestCase(10)] + [TestCase(100)] + public void CanFactorizeIdentityUsingThinQR(int order) + { + var matrixI = UserDefinedMatrix.Identity(order); + var factorQR = matrixI.QR(QRMethod.Thin); + var r = factorQR.R; + + Assert.AreEqual(matrixI.RowCount, r.RowCount); + Assert.AreEqual(matrixI.ColumnCount, r.ColumnCount); + + for (var i = 0; i < r.RowCount; i++) + { + for (var j = 0; j < r.ColumnCount; j++) + { + if (i == j) + { + Assert.AreEqual(-1.0, r[i, j]); + } + else + { + Assert.AreEqual(0.0, r[i, j]); + } + } + } + } + /// /// Identity determinant is one. /// @@ -149,6 +183,55 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single.Factorization } } + /// + /// Can factorize a random matrix using thin QR. + /// + /// Matrix row number. + /// Matrix column number. + [TestCase(1, 1)] + [TestCase(2, 2)] + [TestCase(5, 5)] + [TestCase(10, 6)] + [TestCase(50, 48)] + [TestCase(100, 98)] + public void CanFactorizeRandomMatrixUsingThinQR(int row, int column) + { + var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(row, column); + var factorQR = matrixA.QR(QRMethod.Thin); + var q = factorQR.Q; + var r = factorQR.R; + + // Make sure the R has the right dimensions. + Assert.AreEqual(column, r.RowCount); + Assert.AreEqual(column, r.ColumnCount); + + // Make sure the Q has the right dimensions. + Assert.AreEqual(row, q.RowCount); + Assert.AreEqual(column, q.ColumnCount); + + // Make sure the R factor is upper triangular. + for (var i = 0; i < r.RowCount; i++) + { + for (var j = 0; j < r.ColumnCount; j++) + { + if (i > j) + { + Assert.AreEqual(0.0, r[i, j]); + } + } + } + + // Make sure the Q*R is the original matrix. + var matrixQfromR = q * r; + for (var i = 0; i < matrixQfromR.RowCount; i++) + { + for (var j = 0; j < matrixQfromR.ColumnCount; j++) + { + Assert.AreEqual(matrixA[i, j], matrixQfromR[i, j], 1.0e-4); + } + } + } + /// /// Can solve a system of linear equations for a random vector (Ax=b). /// @@ -337,5 +420,194 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single.Factorization } } } + + /// + /// Can solve a system of linear equations for a random vector (Ax=b). + /// + /// Matrix order. + [TestCase(1)] + [TestCase(2)] + [TestCase(5)] + [TestCase(10)] + [TestCase(50)] + [TestCase(100)] + public void CanSolveForRandomVectorUsingThinQR(int order) + { + var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order); + var matrixACopy = matrixA.Clone(); + var factorQR = matrixA.QR(QRMethod.Thin); + + var vectorb = MatrixLoader.GenerateRandomUserDefinedVector(order); + var resultx = factorQR.Solve(vectorb); + + Assert.AreEqual(matrixA.ColumnCount, resultx.Count); + + var matrixBReconstruct = matrixA * resultx; + + // Check the reconstruction. + for (var i = 0; i < order; i++) + { + Assert.AreEqual(vectorb[i], matrixBReconstruct[i], 1e-4); + } + + // Make sure A didn't change. + for (var i = 0; i < matrixA.RowCount; i++) + { + for (var j = 0; j < matrixA.ColumnCount; j++) + { + Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]); + } + } + } + + /// + /// Can solve a system of linear equations for a random matrix (AX=B). + /// + /// Matrix order. + [TestCase(1)] + [TestCase(2)] + [TestCase(5)] + [TestCase(10)] + [TestCase(50)] + [TestCase(100)] + public void CanSolveForRandomMatrixUsingThinQR(int order) + { + var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order); + var matrixACopy = matrixA.Clone(); + var factorQR = matrixA.QR(QRMethod.Thin); + + 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.AreEqual(matrixB[i, j], matrixBReconstruct[i, j], 1e-4); + } + } + + // Make sure A didn't change. + for (var i = 0; i < matrixA.RowCount; i++) + { + for (var j = 0; j < matrixA.ColumnCount; j++) + { + Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]); + } + } + } + + /// + /// Can solve for a random vector into a result vector. + /// + /// Matrix order. + [TestCase(1)] + [TestCase(2)] + [TestCase(5)] + [TestCase(10)] + [TestCase(50)] + [TestCase(100)] + public void CanSolveForRandomVectorWhenResultVectorGivenUsingThinQR(int order) + { + var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order); + var matrixACopy = matrixA.Clone(); + var factorQR = matrixA.QR(QRMethod.Thin); + 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 matrixBReconstruct = matrixA * resultx; + + // Check the reconstruction. + for (var i = 0; i < vectorb.Count; i++) + { + Assert.AreEqual(vectorb[i], matrixBReconstruct[i], 1e-4); + } + + // Make sure A didn't change. + for (var i = 0; i < matrixA.RowCount; i++) + { + for (var j = 0; j < matrixA.ColumnCount; j++) + { + Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]); + } + } + + // Make sure b didn't change. + for (var i = 0; i < vectorb.Count; i++) + { + Assert.AreEqual(vectorbCopy[i], vectorb[i]); + } + } + + /// + /// Can solve a system of linear equations for a random matrix (AX=B) into a result matrix. + /// + /// Matrix order. + [TestCase(1)] + [TestCase(2)] + [TestCase(5)] + [TestCase(10)] + [TestCase(50)] + [TestCase(100)] + public void CanSolveForRandomMatrixWhenResultMatrixGivenUsingThinAR(int order) + { + var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order); + var matrixACopy = matrixA.Clone(); + var factorQR = matrixA.QR(QRMethod.Thin); + + 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.AreEqual(matrixB[i, j], matrixBReconstruct[i, j], 1e-4); + } + } + + // Make sure A didn't change. + for (var i = 0; i < matrixA.RowCount; i++) + { + for (var j = 0; j < matrixA.ColumnCount; j++) + { + Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]); + } + } + + // Make sure B didn't change. + for (var i = 0; i < matrixB.RowCount; i++) + { + for (var j = 0; j < matrixB.ColumnCount; j++) + { + Assert.AreEqual(matrixBCopy[i, j], matrixB[i, j]); + } + } + } } } diff --git a/src/UnitTests/Setup.cs b/src/UnitTests/Setup.cs index 18231463..6b51861a 100644 --- a/src/UnitTests/Setup.cs +++ b/src/UnitTests/Setup.cs @@ -45,13 +45,5 @@ public class Setup { MathNet.Numerics.Control.LinearAlgebraProvider = new MathNet.Numerics.Algorithms.LinearAlgebra.Mkl.MklLinearAlgebraProvider(); } - else if (provider.Contains("gotoblas")) - { - MathNet.Numerics.Control.LinearAlgebraProvider = new MathNet.Numerics.Algorithms.LinearAlgebra.GotoBlas.GotoBlasLinearAlgebraProvider(); - } - else if (provider.Contains("acml")) - { - MathNet.Numerics.Control.LinearAlgebraProvider = new MathNet.Numerics.Algorithms.LinearAlgebra.Acml.AcmlLinearAlgebraProvider(); - } } }