/// <summary> /// Solve the minimum norm problem A * x = b => transpose(Q) * inv(L) * b. The minimum nrom problem is defined as: /// from the infinite solutions of A * x = b, find the one with the minimum norm2(x). Warning: the rows of the original /// matrix must be independent for this to work: If A (m-by-n, with m <= n) has full row rank, r = m => the column /// space of A is an m dimensional subspace of R^m (every vector A*z is m-by-1) => the column space of A is the whole /// R^m and A*x=b, always has at least one solution. The nullspace of A is an (n-m) dimensional subspace of R^n. Thus /// there are infinite solutions to A*x=b. /// </summary> /// <param name="rhsVector">The right hand side vector b. It may lie outside the column space of the original matrix. Its /// <see cref="IIndexable1D.Length"/> must be equal to this.<see cref="NumRows"/>.</param> /// <exception cref="Exceptions.LapackException">Thrown if tha call to LAPACK fails due to <paramref name="rhsVector"/> /// having a different <see cref="IIndexable1D.Length"/> than this.<see cref="NumRows"/>.</exception> public Vector SolveMinNorm(Vector rhsVector) //TODO: perhaps I should use the driver routines of LAPACKE { if (NumRows > NumColumns) { throw new NotImplementedException("For now, the number of rows must be <= the number of columns"); } Preconditions.CheckSystemSolutionDimensions(NumRows, rhsVector.Length); // Min norm: A * x = b => L * Q * x = b, where b is the right hand side vector. // Step 1: L * c = b. L is m-by-n, b is m-by-1 => c is n-by-1. Reminder n>=m. // Decomposing L: [L1 0] * [c1; c2] = b => L1*c1 = b, where: // L1 is m-by-m, lower triangular and stored in the factorized col major dat // c1 is m-by-1 and can be found by forward substitution // c2 is (n-m)-by-1 and can be any vector whatsoever. int leadingDimA = NumRows; // Also k = min(NumRows, NumColumns) = NumRows = ldA int incC = 1; // step in rhs array double[] c = new double[NumColumns]; rhsVector.CopyToArray(0, c, 0, NumRows); Blas.Dtrsv(StoredTriangle.Lower, TransposeMatrix.NoTranspose, DiagonalValues.NonUnit, NumRows, reflectorsAndL, 0, leadingDimA, c, 0, incC); // TODO: Check output of BLAS somehow. E.g. Zero diagonal entries will result in NaN in the result vector. // Step 2: x = Q^T * c. Q is n-by-n, c is n-by-1, x is n-by-1 int numRowsC = NumColumns; // c has been grown past the limits of rhs. int numColsC = 1; // c is m-by-1 int k = reflectorScalars.Length; int leadingDimC = numRowsC; LapackLeastSquares.Dormlq(MultiplicationSide.Left, TransposeMatrix.Transpose, numRowsC, numColsC, k, reflectorsAndL, 0, leadingDimA, reflectorScalars, 0, c, 0, leadingDimC); return(Vector.CreateFromArray(c, false)); }
/// <summary> /// Solve the linear least squares problem A * x = b => x = inv(R) * transpose(Q) * b. /// Warning: the columns of the original matrix A must be independent for this to work. /// </summary> /// <param name="rhsVector">The right hand side vector b. It may lie outside the column space of the original matrix. Its /// <see cref="IIndexable1D.Length"/> must be equal to this.<see cref="NumRows"/>.</param> /// <exception cref="Exceptions.LapackException">Thrown if the call to LAPACK fails due to <paramref name="rhsVector"/> /// having a different <see cref="IIndexable1D.Length"/> than this.<see cref="NumRows"/>.</exception> public Vector SolveLeastSquares(Vector rhsVector) //TODO: perhaps I should use the driver routines of LAPACKE { if (NumRows < NumColumns) { throw new NotImplementedException("For now, the number of rows must be >= the number of columns"); } Preconditions.CheckSystemSolutionDimensions(NumRows, rhsVector.Length); // Least squares: x = inv(A^T * A) * A^T * b = inv(R) * Q^T * b, where b is the right hand side vector. // Step 1: c = Q^T * b. Q is m-by-m, b is m-by-1 => c is m-by-1 double[] c = rhsVector.CopyToArray(); int numRowsC = rhsVector.Length; int numColsC = 1; // rhs = m-by-1 int numReflectors = reflectorScalars.Length; int leadingDimA = numRowsC; int leadingDimC = numRowsC; LapackLeastSquares.Dormqr(MultiplicationSide.Left, TransposeMatrix.Transpose, numRowsC, numColsC, numReflectors, reflectorsAndR, 0, leadingDimA, reflectorScalars, 0, c, 0, leadingDimC); // Step 2: R * x = c, with R being m-by-n and upper trapezoidal (because m >= n). // Decomposing R: [R1; 0] * x = [c1 ; c2 ] => R1 * x = c1 => R1 * x = c1, with R1 being n-by-n, upper triangular // and stored in the factorized col major data. c1 and x are both n-by-1. The information stored in c2 is lost due to // the least squares approximation. // TODO: I do not really need to discard the extra m-n terms of c2, but I think it is unsafe to carry them around and // risk some method of Vector using the length of the internal buffer, instead of its Length propert. if (NumRows > NumColumns) { Array.Resize <double>(ref c, NumColumns); } int n = NumColumns; // Order of matrix R1 int ldR = NumRows; // R1 is stored in the upper trapezoid of a NumRows * NumColumns col major array. int incC = 1; // step in rhs array, which is the same c used for Q^T * b Blas.Dtrsv(StoredTriangle.Upper, TransposeMatrix.NoTranspose, DiagonalValues.NonUnit, n, reflectorsAndR, 0, ldR, c, 0, incC); // TODO: Check output of BLAS somehow. E.g. Zero diagonal entries will result in NaN in the result vector. return(Vector.CreateFromArray(c, false)); }
/// <summary> /// See https://software.intel.com/en-us/mkl-developer-reference-fortran-trsv#D8733073-F041-4AA1-B82C-123DFA993AD7 /// </summary> public void Dtrsv(StoredTriangle uplo, TransposeMatrix transA, DiagonalValues diag, int n, double[] a, int offsetA, int ldA, double[] x, int offsetX, int incX) => Blas.Dtrsv(uplo.Translate(), transA.Translate(), diag.Translate(), ref n, ref a[offsetA], ref ldA, ref x[offsetX], ref incX);