From aba8c988cb08ef2032605a8d8259310b71dfc905 Mon Sep 17 00:00:00 2001 From: Marcus Cuda Date: Mon, 28 Feb 2011 22:23:45 +0800 Subject: [PATCH] native: added QR factor --- src/NativeWrappers/MKL/lapack.cpp | 12 ++ .../ILinearAlgebraProviderOfT.cs | 10 +- .../ManagedLinearAlgebraProvider.Complex.cs | 32 ++-- .../ManagedLinearAlgebraProvider.Complex32.cs | 32 ++-- .../ManagedLinearAlgebraProvider.Double.cs | 32 ++-- .../ManagedLinearAlgebraProvider.Single.cs | 32 ++-- .../LinearAlgebra/native.generic.include | 74 ++++++++- .../LinearAlgebra/safe.native.common.include | 28 +++- src/Numerics/Control.cs | 28 +++- .../Complex/Factorization/DenseQR.cs | 12 +- .../Complex32/Factorization/DenseQR.cs | 12 +- .../Double/Factorization/DenseQR.cs | 17 +- .../Double/Factorization/UserQR.cs | 2 +- .../Single/Factorization/DenseQR.cs | 12 +- src/Numerics/Properties/Resources.Designer.cs | 9 ++ src/Numerics/Properties/Resources.resx | 3 + .../Double/LinearAlgebraProviderTests.cs | 145 ++++++++++++++++++ 17 files changed, 430 insertions(+), 62 deletions(-) diff --git a/src/NativeWrappers/MKL/lapack.cpp b/src/NativeWrappers/MKL/lapack.cpp index 33ab37e3..3f9e62c0 100644 --- a/src/NativeWrappers/MKL/lapack.cpp +++ b/src/NativeWrappers/MKL/lapack.cpp @@ -523,6 +523,7 @@ extern "C" { if (i > j) { q[j * m + i] = r[j * m + i]; + r[j * m + i] = 0.0f; } } } @@ -552,6 +553,7 @@ extern "C" { if (i > j) { q[j * m + i] = r[j * m + i]; + r[j * m + i] = 0.0; } } } @@ -573,6 +575,10 @@ extern "C" { { int info = 0; CGEQRF(&m, &n, r, &m, tau, work, &len, &info); + + MKL_Complex8 zero; + zero.real = 0.0f; + zero.imag = 0.0f; for (int i = 0; i < m; ++i) { @@ -581,6 +587,7 @@ extern "C" { if (i > j) { q[j * m + i] = r[j * m + i]; + r[j * m + i] = zero; } } } @@ -603,6 +610,10 @@ extern "C" { int info = 0; ZGEQRF(&m, &n, r, &m, tau, work, &len, &info); + MKL_Complex16 zero; + zero.real = 0.0; + zero.imag = 0.0; + for (int i = 0; i < m; ++i) { for (int j = 0; j < m && j < n; ++j) @@ -610,6 +621,7 @@ extern "C" { if (i > j) { q[j * m + i] = r[j * m + i]; + r[j * m + i] = zero; } } } diff --git a/src/Numerics/Algorithms/LinearAlgebra/ILinearAlgebraProviderOfT.cs b/src/Numerics/Algorithms/LinearAlgebra/ILinearAlgebraProviderOfT.cs index e0a9b4c6..7ff780e7 100644 --- a/src/Numerics/Algorithms/LinearAlgebra/ILinearAlgebraProviderOfT.cs +++ b/src/Numerics/Algorithms/LinearAlgebra/ILinearAlgebraProviderOfT.cs @@ -316,13 +316,15 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra /// Computes the QR factorization of A. /// /// On entry, it is the M by N A matrix to factor. On exit, - /// it is overwritten with the R matrix of the QR factorization. + /// 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. /// This is similar to the GEQRF and ORGQR LAPACK routines. - void QRFactor(T[] r, int rowsR, int columnsR, T[] q); + void QRFactor(T[] r, int rowsR, int columnsR, T[] q, T[] tau); /// /// Computes the QR factorization of A. @@ -333,11 +335,13 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra /// 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[] r, int rowsR, int columnsR, T[] q, T[] work); + void QRFactor(T[] r, int rowsR, int columnsR, T[] q, T[] tau, T[] work); /// /// Solves A*X=B for X using QR factorization of A. diff --git a/src/Numerics/Algorithms/LinearAlgebra/ManagedLinearAlgebraProvider.Complex.cs b/src/Numerics/Algorithms/LinearAlgebra/ManagedLinearAlgebraProvider.Complex.cs index 06e497a9..5ca428dd 100644 --- a/src/Numerics/Algorithms/LinearAlgebra/ManagedLinearAlgebraProvider.Complex.cs +++ b/src/Numerics/Algorithms/LinearAlgebra/ManagedLinearAlgebraProvider.Complex.cs @@ -1359,8 +1359,10 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra /// 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. - public virtual void QRFactor(Complex[] r, int rowsR, int columnsR, Complex[] q) + public virtual void QRFactor(Complex[] r, int rowsR, int columnsR, Complex[] q, Complex[] tau) { if (r == null) { @@ -1374,16 +1376,21 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra if (r.Length != rowsR * columnsR) { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "r"); + throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, "rowsR * columnsR"), "r"); + } + + if (tau.Length < Math.Min(rowsR, columnsR)) + { + throw new ArgumentException(string.Format(Resources.ArrayTooSmall, "min(m,n)"), "tau"); } if (q.Length != rowsR * rowsR) { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "q"); + throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, "rowsR * rowsR"), "q"); } var work = new Complex[rowsR * rowsR]; - QRFactor(r, rowsR, columnsR, q, work); + QRFactor(r, rowsR, columnsR, q, tau, work); } /// @@ -1395,11 +1402,13 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra /// 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. - public virtual void QRFactor(Complex[] r, int rowsR, int columnsR, Complex[] q, Complex[] work) + public virtual void QRFactor(Complex[] r, int rowsR, int columnsR, Complex[] q, Complex[] tau, Complex[] work) { if (r == null) { @@ -1418,12 +1427,17 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra if (r.Length != rowsR * columnsR) { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "r"); + throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, "rowsR * columnsR"), "r"); + } + + if (tau.Length < Math.Min(rowsR, columnsR)) + { + throw new ArgumentException(string.Format(Resources.ArrayTooSmall, "min(m,n)"), "tau"); } if (q.Length != rowsR * rowsR) { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "q"); + throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, "rowsR * rowsR"), "q"); } if (work.Length < rowsR * rowsR) @@ -1681,8 +1695,8 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra /// /// 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. /// The B matrix. diff --git a/src/Numerics/Algorithms/LinearAlgebra/ManagedLinearAlgebraProvider.Complex32.cs b/src/Numerics/Algorithms/LinearAlgebra/ManagedLinearAlgebraProvider.Complex32.cs index 8a8f6b99..89e4b98f 100644 --- a/src/Numerics/Algorithms/LinearAlgebra/ManagedLinearAlgebraProvider.Complex32.cs +++ b/src/Numerics/Algorithms/LinearAlgebra/ManagedLinearAlgebraProvider.Complex32.cs @@ -1359,8 +1359,10 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra /// 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. - public virtual void QRFactor(Complex32[] r, int rowsR, int columnsR, Complex32[] q) + public virtual void QRFactor(Complex32[] r, int rowsR, int columnsR, Complex32[] q, Complex32[] tau) { if (r == null) { @@ -1374,16 +1376,21 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra if (r.Length != rowsR * columnsR) { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "r"); + throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, "rowsR * columnsR"), "r"); + } + + if (tau.Length < Math.Min(rowsR, columnsR)) + { + throw new ArgumentException(string.Format(Resources.ArrayTooSmall, "min(m,n)"), "tau"); } if (q.Length != rowsR * rowsR) { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "q"); + throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, "rowsR * rowsR"), "q"); } var work = new Complex32[rowsR * rowsR]; - QRFactor(r, rowsR, columnsR, q, work); + QRFactor(r, rowsR, columnsR, q, tau, work); } /// @@ -1395,11 +1402,13 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra /// 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. - public virtual void QRFactor(Complex32[] r, int rowsR, int columnsR, Complex32[] q, Complex32[] work) + public virtual void QRFactor(Complex32[] r, int rowsR, int columnsR, Complex32[] q, Complex32[] tau, Complex32[] work) { if (r == null) { @@ -1418,12 +1427,17 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra if (r.Length != rowsR * columnsR) { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "r"); + throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, "rowsR * columnsR"), "r"); + } + + if (tau.Length < Math.Min(rowsR, columnsR)) + { + throw new ArgumentException(string.Format(Resources.ArrayTooSmall, "min(m,n)"), "tau"); } if (q.Length != rowsR * rowsR) { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "q"); + throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, "rowsR * rowsR"), "q"); } if (work.Length < rowsR * rowsR) @@ -1681,8 +1695,8 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra /// /// 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. /// The B matrix. diff --git a/src/Numerics/Algorithms/LinearAlgebra/ManagedLinearAlgebraProvider.Double.cs b/src/Numerics/Algorithms/LinearAlgebra/ManagedLinearAlgebraProvider.Double.cs index 10ec9bd3..25cc4b04 100644 --- a/src/Numerics/Algorithms/LinearAlgebra/ManagedLinearAlgebraProvider.Double.cs +++ b/src/Numerics/Algorithms/LinearAlgebra/ManagedLinearAlgebraProvider.Double.cs @@ -1353,8 +1353,10 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra /// 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. - public virtual void QRFactor(double[] r, int rowsR, int columnsR, double[] q) + public virtual void QRFactor(double[] r, int rowsR, int columnsR, double[] q, double[] tau) { if (r == null) { @@ -1368,16 +1370,21 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra if (r.Length != rowsR * columnsR) { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "r"); + throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, "rowsR * columnsR"), "r"); + } + + if (tau.Length < Math.Min(rowsR, columnsR)) + { + throw new ArgumentException(string.Format(Resources.ArrayTooSmall, "min(m,n)"), "tau"); } if (q.Length != rowsR * rowsR) { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "q"); + throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, "rowsR * rowsR"), "q"); } var work = new double[rowsR * rowsR]; - QRFactor(r, rowsR, columnsR, q, work); + QRFactor(r, rowsR, columnsR, q, tau, work); } /// @@ -1389,11 +1396,13 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra /// 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. - public virtual void QRFactor(double[] r, int rowsR, int columnsR, double[] q, double[] work) + public virtual void QRFactor(double[] r, int rowsR, int columnsR, double[] q, double[] tau, double[] work) { if (r == null) { @@ -1412,12 +1421,17 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra if (r.Length != rowsR * columnsR) { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "r"); + throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, "rowsR * columnsR"), "r"); + } + + if (tau.Length < Math.Min(rowsR, columnsR)) + { + throw new ArgumentException(string.Format(Resources.ArrayTooSmall, "min(m,n)"), "tau"); } if (q.Length != rowsR * rowsR) { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "q"); + throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, "rowsR * rowsR"), "q"); } if (work.Length < rowsR * rowsR) @@ -1676,8 +1690,8 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra /// /// 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. /// The B matrix. diff --git a/src/Numerics/Algorithms/LinearAlgebra/ManagedLinearAlgebraProvider.Single.cs b/src/Numerics/Algorithms/LinearAlgebra/ManagedLinearAlgebraProvider.Single.cs index d9f99f7d..317beeac 100644 --- a/src/Numerics/Algorithms/LinearAlgebra/ManagedLinearAlgebraProvider.Single.cs +++ b/src/Numerics/Algorithms/LinearAlgebra/ManagedLinearAlgebraProvider.Single.cs @@ -1354,8 +1354,10 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra /// 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. - public virtual void QRFactor(float[] r, int rowsR, int columnsR, float[] q) + public virtual void QRFactor(float[] r, int rowsR, int columnsR, float[] q, float[] tau) { if (r == null) { @@ -1369,16 +1371,21 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra if (r.Length != rowsR * columnsR) { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "r"); + throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, "rowsR * columnsR"), "r"); + } + + if (tau.Length < Math.Min(rowsR, columnsR)) + { + throw new ArgumentException(string.Format(Resources.ArrayTooSmall, "min(m,n)"), "tau"); } if (q.Length != rowsR * rowsR) { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "q"); + throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, "rowsR * rowsR"), "q"); } var work = new float[rowsR * rowsR]; - QRFactor(r, rowsR, columnsR, q, work); + QRFactor(r, rowsR, columnsR, q, tau, work); } /// @@ -1390,11 +1397,13 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra /// 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. - public virtual void QRFactor(float[] r, int rowsR, int columnsR, float[] q, float[] work) + public virtual void QRFactor(float[] r, int rowsR, int columnsR, float[] q, float[] tau, float[] work) { if (r == null) { @@ -1413,12 +1422,17 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra if (r.Length != rowsR * columnsR) { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "r"); + throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, "rowsR * columnsR"), "r"); + } + + if (tau.Length < Math.Min(rowsR, columnsR)) + { + throw new ArgumentException(string.Format(Resources.ArrayTooSmall, "min(m,n)"), "tau"); } if (q.Length != rowsR * rowsR) { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "q"); + throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, "rowsR * rowsR"), "q"); } if (work.Length < rowsR * rowsR) @@ -1677,8 +1691,8 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra /// /// 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. /// The B matrix. diff --git a/src/Numerics/Algorithms/LinearAlgebra/native.generic.include b/src/Numerics/Algorithms/LinearAlgebra/native.generic.include index 7da5fb3f..e3bc980e 100644 --- a/src/Numerics/Algorithms/LinearAlgebra/native.generic.include +++ b/src/Numerics/Algorithms/LinearAlgebra/native.generic.include @@ -517,11 +517,39 @@ /// 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. [SecuritySafeCritical] - public override void QRFactor(<#=dataType#>[] r, int rowsR, int columnsR, <#=dataType#>[] q) + public override void QRFactor(<#=dataType#>[] r, int rowsR, int columnsR, <#=dataType#>[] q, <#=dataType#>[] tau) { - throw new NotImplementedException(); + if (r == null) + { + throw new ArgumentNullException("r"); + } + + if (q == null) + { + throw new ArgumentNullException("q"); + } + + if (r.Length != rowsR * columnsR) + { + throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, "rowsR * columnsR"), "r"); + } + + if (tau.Length < Math.Min(rowsR, columnsR)) + { + throw new ArgumentException(string.Format(Resources.ArrayTooSmall, "min(m,n)"), "tau"); + } + + if (q.Length != rowsR * rowsR) + { + throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, "rowsR * rowsR"), "q"); + } + + var work = new <#=dataType#>[columnsR * Control.BlockSize]; + SafeNativeMethods.<#=prefix#>_qr_factor(rowsR, columnsR, r, tau, q, work, work.Length); } /// @@ -533,14 +561,52 @@ /// 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. [SecuritySafeCritical] - public override void QRFactor(<#=dataType#>[] r, int rowsR, int columnsR, <#=dataType#>[] q, <#=dataType#>[] work) + public override void QRFactor(<#=dataType#>[] r, int rowsR, int columnsR, <#=dataType#>[] q, <#=dataType#>[] tau, <#=dataType#>[] work) { - throw new NotImplementedException(); + if (r == null) + { + throw new ArgumentNullException("r"); + } + + if (q == null) + { + throw new ArgumentNullException("q"); + } + + if (work == null) + { + throw new ArgumentNullException("q"); + } + + if (r.Length != rowsR * columnsR) + { + throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, "rowsR * columnsR"), "r"); + } + + if (tau.Length < Math.Min(rowsR, columnsR)) + { + throw new ArgumentException(string.Format(Resources.ArrayTooSmall, "min(m,n)"), "tau"); + } + + if (q.Length != rowsR * rowsR) + { + throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, "rowsR * rowsR"), "q"); + } + + if (work.Length < columnsR * Control.BlockSize) + { + work[0] = columnsR * Control.BlockSize; + throw new ArgumentException(Resources.WorkArrayTooSmall, "work"); + } + + SafeNativeMethods.<#=prefix#>_qr_factor(rowsR, columnsR, r, tau, q, work, work.Length); } /// diff --git a/src/Numerics/Algorithms/LinearAlgebra/safe.native.common.include b/src/Numerics/Algorithms/LinearAlgebra/safe.native.common.include index adae155b..59af2294 100644 --- a/src/Numerics/Algorithms/LinearAlgebra/safe.native.common.include +++ b/src/Numerics/Algorithms/LinearAlgebra/safe.native.common.include @@ -187,27 +187,39 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra.<#= namespaceSuffix #> internal static extern int z_lu_solve(int n, int nrhs, Complex[] a, [In, Out] Complex[] b); [DllImport(DllName, ExactSpelling = true, SetLastError = false, CallingConvention = CallingConvention.Cdecl)] - internal static extern int s_cholesky_solve(int n, int nrhs, float[] a, [In, Out] float[] b); + internal static extern int s_cholesky_solve(int n, int nrhs, float[] a, [In, Out] float[] b); [DllImport(DllName, ExactSpelling = true, SetLastError = false, CallingConvention = CallingConvention.Cdecl)] - internal static extern int d_cholesky_solve(int n, int nrhs, double[] a, [In, Out] double[] b); + internal static extern int d_cholesky_solve(int n, int nrhs, double[] a, [In, Out] double[] b); [DllImport(DllName, ExactSpelling = true, SetLastError = false, CallingConvention = CallingConvention.Cdecl)] - internal static extern int c_cholesky_solve(int n, int nrhs, Complex32[] a, [In, Out] Complex32[] b); + internal static extern int c_cholesky_solve(int n, int nrhs, Complex32[] a, [In, Out] Complex32[] b); [DllImport(DllName, ExactSpelling = true, SetLastError = false, CallingConvention = CallingConvention.Cdecl)] - internal static extern int z_cholesky_solve(int n, int nrhs, Complex[] a, [In, Out] Complex[] b); + internal static extern int z_cholesky_solve(int n, int nrhs, Complex[] a, [In, Out] Complex[] b); [DllImport(DllName, ExactSpelling = true, SetLastError = false, CallingConvention = CallingConvention.Cdecl)] - internal static extern int s_cholesky_solve_factored(int n, int nrhs, float[] a, float[] b); + internal static extern int s_cholesky_solve_factored(int n, int nrhs, float[] a, [In, Out] float[] b); [DllImport(DllName, ExactSpelling = true, SetLastError = false, CallingConvention = CallingConvention.Cdecl)] - internal static extern int d_cholesky_solve_factored(int n, int nrhs, double[] a, double[] b); + internal static extern int d_cholesky_solve_factored(int n, int nrhs, double[] a, [In, Out] double[] b); [DllImport(DllName, ExactSpelling = true, SetLastError = false, CallingConvention = CallingConvention.Cdecl)] - internal static extern int c_cholesky_solve_factored(int n, int nrhs, Complex32[] a, Complex32[] b); + internal static extern int c_cholesky_solve_factored(int n, int nrhs, Complex32[] a, [In, Out] Complex32[] b); [DllImport(DllName, ExactSpelling = true, SetLastError = false, CallingConvention = CallingConvention.Cdecl)] - internal static extern int z_cholesky_solve_factored(int n, int nrhs, Complex[] a, Complex[] b); + internal static extern int z_cholesky_solve_factored(int n, int nrhs, Complex[] a, [In, Out] Complex[] b); + + [DllImport(DllName, ExactSpelling = true, SetLastError = false, CallingConvention = CallingConvention.Cdecl)] + internal static extern int s_qr_factor(int m, int n, [In, Out] float[] r, [In, Out] float[] tau, [In, Out] float[] q, [In, Out] float[] work, int len); + + [DllImport(DllName, ExactSpelling = true, SetLastError = false, CallingConvention = CallingConvention.Cdecl)] + internal static extern int d_qr_factor(int m, int n, [In, Out] double[] r, [In, Out] double[] tau, [In, Out] double[] q, [In, Out] double[] work, int len); + + [DllImport(DllName, ExactSpelling = true, SetLastError = false, CallingConvention = CallingConvention.Cdecl)] + internal static extern int c_qr_factor(int m, int n, [In, Out] Complex32[] r, [In, Out] Complex32[] tau, [In, Out] Complex32[] q, [In, Out] Complex32[] work, int len); + + [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); #endregion LAPACK diff --git a/src/Numerics/Control.cs b/src/Numerics/Control.cs index 13dceba7..abd698ac 100644 --- a/src/Numerics/Control.cs +++ b/src/Numerics/Control.cs @@ -46,6 +46,11 @@ namespace MathNet.Numerics private static int _numberOfThreads = Environment.ProcessorCount; #endif + /// + /// Initial block size for the native linear algebra provider. + /// + private static int _blockSize = 512; + /// /// Initializes static members of the Control class. /// @@ -60,7 +65,7 @@ namespace MathNet.Numerics /// /// Gets or sets a value indicating whether the distribution classes check validate each parameter. /// For the multivariate distributions this could involve an expensive matrix factorization. - /// The default setting of this property is true. + /// The default setting of this property is true. /// public static bool CheckDistributionParameters { get; set; } @@ -109,5 +114,26 @@ namespace MathNet.Numerics } #endif } + + /// + /// Gets or sets the the block size to use for the native linear + /// algebra provider. + /// + /// The block size. Must be at least 32. + public static int BlockSize + { + get + { + return _blockSize; + } + + set + { + if (_blockSize > 31) + { + _blockSize = value; + } + } + } } } diff --git a/src/Numerics/LinearAlgebra/Complex/Factorization/DenseQR.cs b/src/Numerics/LinearAlgebra/Complex/Factorization/DenseQR.cs index 22dcd05f..a5dd9321 100644 --- a/src/Numerics/LinearAlgebra/Complex/Factorization/DenseQR.cs +++ b/src/Numerics/LinearAlgebra/Complex/Factorization/DenseQR.cs @@ -46,6 +46,15 @@ namespace MathNet.Numerics.LinearAlgebra.Complex.Factorization /// public class DenseQR : QR { + /// + /// Gets or sets Tau vector. Contains additional information on Q - used for native solver. + /// + public Complex[] Tau + { + get; + set; + } + /// /// Initializes a new instance of the class. This object will compute the /// QR factorization when the constructor is called and cache it's factorization. @@ -67,7 +76,8 @@ namespace MathNet.Numerics.LinearAlgebra.Complex.Factorization MatrixR = matrix.Clone(); MatrixQ = new DenseMatrix(matrix.RowCount); - Control.LinearAlgebraProvider.QRFactor(((DenseMatrix)MatrixR).Data, matrix.RowCount, matrix.ColumnCount, ((DenseMatrix)MatrixQ).Data); + Tau = new Complex[Math.Min(matrix.RowCount, matrix.ColumnCount)]; + Control.LinearAlgebraProvider.QRFactor(((DenseMatrix)MatrixR).Data, matrix.RowCount, matrix.ColumnCount, ((DenseMatrix)MatrixQ).Data, Tau); } /// diff --git a/src/Numerics/LinearAlgebra/Complex32/Factorization/DenseQR.cs b/src/Numerics/LinearAlgebra/Complex32/Factorization/DenseQR.cs index 64ef6526..403c3840 100644 --- a/src/Numerics/LinearAlgebra/Complex32/Factorization/DenseQR.cs +++ b/src/Numerics/LinearAlgebra/Complex32/Factorization/DenseQR.cs @@ -46,6 +46,15 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32.Factorization /// public class DenseQR : QR { + /// + /// Gets or sets Tau vector. Contains additional information on Q - used for native solver. + /// + public Complex32[] Tau + { + get; + set; + } + /// /// Initializes a new instance of the class. This object will compute the /// QR factorization when the constructor is called and cache it's factorization. @@ -67,7 +76,8 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32.Factorization MatrixR = matrix.Clone(); MatrixQ = new DenseMatrix(matrix.RowCount); - Control.LinearAlgebraProvider.QRFactor(((DenseMatrix)MatrixR).Data, matrix.RowCount, matrix.ColumnCount, ((DenseMatrix)MatrixQ).Data); + Tau = new Complex32[Math.Min(matrix.RowCount, matrix.ColumnCount)]; + Control.LinearAlgebraProvider.QRFactor(((DenseMatrix)MatrixR).Data, matrix.RowCount, matrix.ColumnCount, ((DenseMatrix)MatrixQ).Data, Tau); } /// diff --git a/src/Numerics/LinearAlgebra/Double/Factorization/DenseQR.cs b/src/Numerics/LinearAlgebra/Double/Factorization/DenseQR.cs index 48017348..1e626da7 100644 --- a/src/Numerics/LinearAlgebra/Double/Factorization/DenseQR.cs +++ b/src/Numerics/LinearAlgebra/Double/Factorization/DenseQR.cs @@ -45,6 +45,15 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Factorization /// public class DenseQR : QR { + /// + /// Gets or sets Tau vector. Contains additional information on Q - used for native solver. + /// + public double[] Tau + { + get; + set; + } + /// /// Initializes a new instance of the class. This object will compute the /// QR factorization when the constructor is called and cache it's factorization. @@ -54,11 +63,6 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Factorization /// If row count is less then column count public DenseQR(DenseMatrix matrix) { - if (matrix == null) - { - throw new ArgumentNullException("matrix"); - } - if (matrix.RowCount < matrix.ColumnCount) { throw new ArgumentException(Resources.ArgumentMatrixDimensions); @@ -66,7 +70,8 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Factorization MatrixR = matrix.Clone(); MatrixQ = new DenseMatrix(matrix.RowCount); - Control.LinearAlgebraProvider.QRFactor(((DenseMatrix)MatrixR).Data, matrix.RowCount, matrix.ColumnCount, ((DenseMatrix)MatrixQ).Data); + Tau = new double[Math.Min(matrix.RowCount, matrix.ColumnCount)]; + Control.LinearAlgebraProvider.QRFactor(((DenseMatrix)MatrixR).Data, matrix.RowCount, matrix.ColumnCount, ((DenseMatrix)MatrixQ).Data, Tau); } /// diff --git a/src/Numerics/LinearAlgebra/Double/Factorization/UserQR.cs b/src/Numerics/LinearAlgebra/Double/Factorization/UserQR.cs index 44d3c8d1..f9fcdc6a 100644 --- a/src/Numerics/LinearAlgebra/Double/Factorization/UserQR.cs +++ b/src/Numerics/LinearAlgebra/Double/Factorization/UserQR.cs @@ -91,7 +91,7 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Factorization /// Generate column from initial matrix to work array /// /// Initial matrix - /// The firts row + /// The first row /// Column index /// Generated vector private static double[] GenerateColumn(Matrix a, int row, int column) diff --git a/src/Numerics/LinearAlgebra/Single/Factorization/DenseQR.cs b/src/Numerics/LinearAlgebra/Single/Factorization/DenseQR.cs index a0fabfc2..9aaa543b 100644 --- a/src/Numerics/LinearAlgebra/Single/Factorization/DenseQR.cs +++ b/src/Numerics/LinearAlgebra/Single/Factorization/DenseQR.cs @@ -45,6 +45,15 @@ namespace MathNet.Numerics.LinearAlgebra.Single.Factorization /// public class DenseQR : QR { + /// + /// Gets or sets Tau vector. Contains additional information on Q - used for native solver. + /// + internal float[] Tau + { + get; + set; + } + /// /// Initializes a new instance of the class. This object will compute the /// QR factorization when the constructor is called and cache it's factorization. @@ -66,7 +75,8 @@ namespace MathNet.Numerics.LinearAlgebra.Single.Factorization MatrixR = matrix.Clone(); MatrixQ = new DenseMatrix(matrix.RowCount); - Control.LinearAlgebraProvider.QRFactor(((DenseMatrix)MatrixR).Data, matrix.RowCount, matrix.ColumnCount, ((DenseMatrix)MatrixQ).Data); + Tau = new float[Math.Min(matrix.RowCount, matrix.ColumnCount)]; + Control.LinearAlgebraProvider.QRFactor(((DenseMatrix)MatrixR).Data, matrix.RowCount, matrix.ColumnCount, ((DenseMatrix)MatrixQ).Data, Tau); } /// diff --git a/src/Numerics/Properties/Resources.Designer.cs b/src/Numerics/Properties/Resources.Designer.cs index 5c7a637b..93877d3d 100644 --- a/src/Numerics/Properties/Resources.Designer.cs +++ b/src/Numerics/Properties/Resources.Designer.cs @@ -69,6 +69,15 @@ namespace MathNet.Numerics.Properties { } } + /// + /// Looks up a localized string similar to The given array is the wrong length. Should be {0}.. + /// + internal static string ArgumentArrayWrongLength { + get { + return ResourceManager.GetString("ArgumentArrayWrongLength", resourceCulture); + } + } + /// /// Looks up a localized string similar to The argument must be between 0 and 1.. /// diff --git a/src/Numerics/Properties/Resources.resx b/src/Numerics/Properties/Resources.resx index f653a30b..15a681a0 100644 --- a/src/Numerics/Properties/Resources.resx +++ b/src/Numerics/Properties/Resources.resx @@ -351,4 +351,7 @@ The given array is too small. It must be at least {0} long. + + The given array is the wrong length. Should be {0}. + \ No newline at end of file diff --git a/src/UnitTests/LinearAlgebraProviderTests/Double/LinearAlgebraProviderTests.cs b/src/UnitTests/LinearAlgebraProviderTests/Double/LinearAlgebraProviderTests.cs index 7aa38802..ca7a999d 100644 --- a/src/UnitTests/LinearAlgebraProviderTests/Double/LinearAlgebraProviderTests.cs +++ b/src/UnitTests/LinearAlgebraProviderTests/Double/LinearAlgebraProviderTests.cs @@ -641,7 +641,152 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraProviderTests.Double AssertHelpers.AlmostEqual(b[5], 0, 14); } + [Test] + public void CanComputeQRFactorSquareMatrix() + { + var matrix = _matrices["Square3x3"]; + var r = new double[matrix.RowCount * matrix.ColumnCount]; + Array.Copy(matrix.Data, r, r.Length); + + var tau = new double[3]; + var q = new double[matrix.RowCount * matrix.RowCount]; + Provider.QRFactor(r, matrix.RowCount, matrix.ColumnCount, q, tau); + + var mq = new DenseMatrix(matrix.RowCount, matrix.RowCount, q); + var mr = new DenseMatrix(matrix.RowCount, 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); + } + } + } + + [Test] + public void CanComputeQRFactorTallMatrix() + { + var matrix = _matrices["Tall3x2"]; + var r = new double[matrix.RowCount * matrix.ColumnCount]; + Array.Copy(matrix.Data, r, r.Length); + + var tau = new double[3]; + var q = new double[matrix.RowCount * matrix.RowCount]; + Provider.QRFactor(r, matrix.RowCount, matrix.ColumnCount, q, tau); + + var mr = new DenseMatrix(matrix.RowCount, matrix.ColumnCount, r); + var mq = new DenseMatrix(matrix.RowCount, matrix.RowCount, q); + 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); + } + } + } + + [Test] + public void CanComputeQRFactorWideMatrix() + { + var matrix = _matrices["Wide2x3"]; + var r = new double[matrix.RowCount * matrix.ColumnCount]; + Array.Copy(matrix.Data, r, r.Length); + + var tau = new double[3]; + var q = new double[matrix.RowCount * matrix.RowCount]; + Provider.QRFactor(r, matrix.RowCount, matrix.ColumnCount, q, tau); + + var mr = new DenseMatrix(matrix.RowCount, matrix.ColumnCount, r); + var mq = new DenseMatrix(matrix.RowCount, matrix.RowCount, q); + 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); + } + } + } + [Test] + public void CanComputeQRFactorSquareMatrixWithWorkArray() + { + var matrix = _matrices["Square3x3"]; + var r = new double[matrix.RowCount * matrix.ColumnCount]; + Array.Copy(matrix.Data, r, r.Length); + + var tau = new double[3]; + var q = new double[matrix.RowCount * matrix.RowCount]; + var work = new double[matrix.ColumnCount * Control.BlockSize]; + Provider.QRFactor(r, matrix.RowCount, matrix.ColumnCount, q, tau, work); + + var mq = new DenseMatrix(matrix.RowCount, matrix.RowCount, q); + var mr = new DenseMatrix(matrix.RowCount, 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); + } + } + } + + [Test] + public void CanComputeQRFactorTallMatrixWithWorkArray() + { + var matrix = _matrices["Tall3x2"]; + var r = new double[matrix.RowCount * matrix.ColumnCount]; + Array.Copy(matrix.Data, r, r.Length); + + var tau = new double[3]; + var q = new double[matrix.RowCount * matrix.RowCount]; + var work = new double[matrix.ColumnCount * Control.BlockSize]; + Provider.QRFactor(r, matrix.RowCount, matrix.ColumnCount, q, tau, work); + + var mr = new DenseMatrix(matrix.RowCount, matrix.ColumnCount, r); + var mq = new DenseMatrix(matrix.RowCount, matrix.RowCount, q); + 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); + } + } + } + + [Test] + public void CanComputeQRFactorWideMatrixWithWorkArray() + { + var matrix = _matrices["Wide2x3"]; + var r = new double[matrix.RowCount * matrix.ColumnCount]; + Array.Copy(matrix.Data, r, r.Length); + + var tau = new double[3]; + var q = new double[matrix.RowCount * matrix.RowCount]; + var work = new double[matrix.ColumnCount * Control.BlockSize]; + Provider.QRFactor(r, matrix.RowCount, matrix.ColumnCount, q, tau, work); + + var mr = new DenseMatrix(matrix.RowCount, matrix.ColumnCount, r); + var mq = new DenseMatrix(matrix.RowCount, matrix.RowCount, q); + 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); + } + } + } /// /// Checks to see if a matrix and array contain the same values.