22

I'm currently trying to develop a small matrix-oriented math library (I'm using Eigen 3 for matrix data structures and operations) and I wanted to implement some handy MATLAB functions, such as the widely used backslash operator (which is equivalent to mldivide ) in order to compute the solution of linear systems (expressed in matrix form).

Is there any good detailed explanation on how this could be achieved? (I've already implemented the Moore-Penrose pseudoinverse pinv function with a classical SVD decomposition, but I've read somewhere that A\b isn't always pinv(A)*b , at least MATLAB doesn't simply do that)

Adriaan
  • 17,741
  • 7
  • 42
  • 75
maddouri
  • 3,737
  • 5
  • 29
  • 51
  • 3
    It's not simple and I would highly recommend against trying. Simply interface it to LAPACK's DGEMV. A lot of smart people have spent a lot of time fine tuning `mldivide`. –  Aug 31 '13 at 22:22
  • similar question: [How does the MATLAB backslash operator solve Ax=b for square matrices?](http://scicomp.stackexchange.com/questions/1001/how-does-the-matlab-backslash-operator-solve-ax-b-for-square-matrices) – Amro Aug 31 '13 at 23:50
  • [My testing](https://stackoverflow.com/q/58171528/4486071) indicates the QR method used for rectangular is LAPACK dgels. – Tyson Hilmer Oct 01 '19 at 06:35

1 Answers1

44

For x = A\b, the backslash operator encompasses a number of algorithms to handle different kinds of input matrices. So the matrix A is diagnosed and an execution path is selected according to its characteristics.

The following page describes in pseudo-code when A is a full matrix:

if size(A,1) == size(A,2)         % A is square
    if isequal(A,tril(A))         % A is lower triangular
        x = A \ b;                % This is a simple forward substitution on b
    elseif isequal(A,triu(A))     % A is upper triangular
        x = A \ b;                % This is a simple backward substitution on b
    else
        if isequal(A,A')          % A is symmetric
            [R,p] = chol(A);
            if (p == 0)           % A is symmetric positive definite
                x = R \ (R' \ b); % a forward and a backward substitution
                return
            end
        end
        [L,U,P] = lu(A);          % general, square A
        x = U \ (L \ (P*b));      % a forward and a backward substitution
    end
else                              % A is rectangular
    [Q,R] = qr(A);
    x = R \ (Q' * b);
end

For non-square matrices, QR decomposition is used. For square triangular matrices, it performs a simple forward/backward substitution. For square symmetric positive-definite matrices, Cholesky decomposition is used. Otherwise LU decomposition is used for general square matrices.

Update: MathWorks has updated the algorithm section in the doc page of mldivide with some nice flow charts. See here and here (full and sparse cases).

mldivide_full

All of these algorithms have corresponding methods in LAPACK, and in fact it's probably what MATLAB is doing (note that recent versions of MATLAB ship with the optimized Intel MKL implementation).

The reason for having different methods is that it tries to use the most specific algorithm to solve the system of equations that takes advantage of all the characteristics of the coefficient matrix (either because it would be faster or more numerically stable). So you could certainly use a general solver, but it wont be the most efficient.

In fact if you know what A is like beforehand, you could skip the extra testing process by calling linsolve and specifying the options directly.

if A is rectangular or singular, you could also use PINV to find a minimal norm least-squares solution (implemented using SVD decomposition):

x = pinv(A)*b

All of the above applies to dense matrices, sparse matrices are a whole different story. Usually iterative solvers are used in such cases. I believe MATLAB uses UMFPACK and other related libraries from the SuiteSpase package for direct solvers.

When working with sparse matrices, you can turn on diagnostic information and see the tests performed and algorithms chosen using spparms:

spparms('spumoni',2)
x = A\b;

What's more, the backslash operator also works on gpuArray's, in which case it relies on cuBLAS and MAGMA to execute on the GPU.

It is also implemented for distributed arrays which works in a distributed computing environment (work divided among a cluster of computers where each worker has only part of the array, possibly where the entire matrix cannot be stored in memory all at once). The underlying implementation is using ScaLAPACK.

That's a pretty tall order if you want to implement all of that yourself :)

Community
  • 1
  • 1
Amro
  • 123,847
  • 25
  • 243
  • 454
  • Thank you Amro, that was a very constructive answer. "A" is most likely to be a non-square matrix, so, if I understood correctly, the most suitable method to use is QR decomposition. And yes, I'm willing to (at least try to) implement what is needed in order to make a somewhat self-contained library, so I don't really want to use other math packages (the algorithms' efficiency is, for the moment, not my primary objective ;) ). It's a personal project, nothing serious for now... – maddouri Sep 01 '13 at 16:01
  • 2
    @M4XMX: Yes, you could use QR decomposition: `Ax=b --> QRx=b --> Rx=Q'b --> x=R\(Q'b)` (where the last step is a simple backward substitution process). See [here](https://inst.eecs.berkeley.edu/~ee127a/book/login/l_lineqs_solving.html) for an explanation. Here is also a related [question](http://stackoverflow.com/q/7949229/97160) showing how to solve `Ax=b` using various libraries: GSL, Armadillo, Eigen. No need to reinvent the wheel :) – Amro Sep 01 '13 at 21:08
  • 2
    I think MATLAB might also be using [pivoting](https://en.wikipedia.org/wiki/QR_decomposition#Column_pivoting) to improve numerical accuracy. That is the decomposition is: `AP=QR` where `P` is a permutation matrix – Amro Sep 01 '13 at 21:19
  • What a beautiful answer. I've always wondered what algorithms `\\` employed. + 1. – rayryeng Feb 24 '15 at 19:23
  • In the non-square case, how is `x = R \ (Q' * b)` done? If `size(A,1) – tommsch Aug 21 '18 at 11:33
  • 1
    @tommsch we use QR factorization of `A'` and solve `(QR)'*x=b`: https://en.wikipedia.org/wiki/QR_decomposition#Using_for_solution_to_linear_inverse_problems – Amro Aug 21 '18 at 20:09