/// <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> /// Explicitly creates the matrix Q1, which consists of the first n columns of the orthogonal matrix Q that resulted /// from the factorization: A = Q * R = [Q1, Q2] * [R1; 0] (Matlab notation) = Q1 * R1, /// where A is m-by-n, Q is m-by-m, R is m-by-n, Q1 is m-by-n and R1 is (n-by-n). /// This method is safe to use as the factorization data are copied (if necessary). However, it is inefficient if the /// generated matrix is only used once. /// </summary> public Matrix GetEconomyFactorQ() { if (NumRows < NumColumns) { throw new NotImplementedException("For now, the number of rows must be >= the number of columns"); } int m = NumRows; int n = NumColumns; int k = NumColumns; int ldA = m; var matrixQ = new double[NumRows * NumColumns]; Array.Copy(reflectorsAndR, matrixQ, matrixQ.Length); // Call Lapack int numRowsQ = NumRows; int numColsQ = NumColumns; int numReflectors = NumColumns; int leadingDimQ = numRowsQ; LapackLeastSquares.Dorgqr(numRowsQ, numColsQ, numReflectors, matrixQ, 0, leadingDimQ, reflectorScalars, 0); return(Matrix.CreateFromArray(matrixQ, NumRows, NumColumns, false)); }
/// <summary> /// Calculates the LQ factorization of a matrix, such that A = L * Q. Requires an extra /// min(<paramref name="numRows"/>, <paramref name="numCols"/>) available memory. /// </summary> /// <param name="numRows">The number of rows of the matrix.</param> /// <param name="numCols">The number of columns of the matrix.</param> /// <param name="matrix">The internal buffer storing the matrix entries in column major order. It will /// be overwritten with the factorization data.</param> /// <exception cref="NotImplementedException">Thrown if <paramref name="numCols"/> < <paramref name="numRows"/>. /// </exception> /// <exception cref="Exceptions.LapackException">Thrown if tha call to LAPACK fails due to an invalid /// <paramref name="matrix"/>.</exception> public static LQFactorization Factorize(int numRows, int numCols, double[] matrix) { if (numRows > numCols) { throw new NotImplementedException("For now, the number of rows must be <= the number of columns"); } int leadingDimA = numRows; int minDim = Math.Min(numRows, numCols); //TODO: this is known to be numRows (though it may change in the future) double[] reflectorScalars = new double[minDim]; LapackLeastSquares.Dgelqf(numRows, numCols, matrix, 0, leadingDimA, reflectorScalars, 0); return(new LQFactorization(numRows, numCols, matrix, reflectorScalars)); }
/// <summary> /// Explicitly creates the orthogonal matrix Q that resulted from the factorization: A = L * Q, where A is m-by-n, /// L is m-by-n and Q is n-by-n. /// This method is safe to use as the factorization data are copied (if necessary). However, it is inefficient if the /// generated matrix is only used once. /// </summary> public Matrix GetFactorQ() { if (NumRows > NumColumns) { throw new NotImplementedException("For now, the number of rows must be <= the number of columns"); } // We need a larger buffer for Q (n-by-n) > reflectorsAndL (p-by-n) double[] matrixQ = ArrayColMajor.IncreaseRows(NumRows, NumColumns, NumColumns, reflectorsAndL); // Call LAPACK int numColsQ = NumColumns; int numRowsQ = numColsQ; int numReflectors = NumRows; int leadingDimQ = numRowsQ; LapackLeastSquares.Dorglq(numRowsQ, numColsQ, numReflectors, matrixQ, 0, leadingDimQ, reflectorScalars, 0); return(Matrix.CreateFromArray(matrixQ, NumColumns, NumColumns, false)); }
/// <summary> /// Explicitly creates the orthogonal matrix Q that resulted from the factorization: A = Q * R, where A is m-by-n, /// Q is m-by-m and R is m-by-n. /// This method is safe to use as the factorization data are copied (if necessary). However, it is inefficient if the /// generated matrix is only used once. /// </summary> public Matrix GetFactorQ() { if (NumRows < NumColumns) { throw new NotImplementedException("For now, the number of rows must be >= the number of columns"); } // We need a larger buffer for Q (m-by-m) > reflectorsAndR (m-by-p) double[] matrixQ = ArrayColMajor.ResizeCols(NumRows, NumColumns, NumRows, reflectorsAndR); // Call Lapack int numRowsQ = NumRows; int numColsQ = numRowsQ; int numReflectors = NumColumns; int leadingDimQ = numRowsQ; LapackLeastSquares.Dorgqr(numRowsQ, numColsQ, numReflectors, matrixQ, 0, leadingDimQ, reflectorScalars, 0); return(Matrix.CreateFromArray(matrixQ, NumRows, NumRows, 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)); }