Should mldivide Return a Solution for Square, Rank-Deficient, but Consistent Set of Linear Equations?

6 Ansichten (letzte 30 Tage)
load lindata
A and b define a linear system of equations Ax = b.
A is 12 x 12 with rank
rank(A)
ans = 6
But the linear equations are consistent with an infinite number of solutions. One such solution is
x1 = repmat([0;0;1],4,1);
Another solution can be found with the sym version of mldivide
x2 = A\b;
Warning: Solution is not unique because the system is rank-deficient.
Verify
A*[x1,x2] - b
ans = 
But the numeric version of mldivide fails
x3 = double(A)\double(b)
Warning: Matrix is singular to working precision.
x3 = 12×1
NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
I guess this isn't terribly surprising. But the doc page mldivide states:
"
  • If A is a square n-by-n matrix and B is a matrix with n rows, then x = A\B is a solution to the equation A*x = B, if it exists."
My interpretation of that last clause is that it would be properly read as "if a solution exists."
Here, many solutions exist but mldivide didn't find a solution. Is this an oversight on the doc page, or am I misreading it, or would it be reasonable to expect mldivide to return a solution for this sort of problem?

Akzeptierte Antwort

Christine Tobler
Christine Tobler am 10 Mär. 2025
Hi Paul!
You can use
x = linsolve(A, b, struct('RECT', true));
to use the QR method of mldivide even if A is square.
To get the minimum-norm solution of x, instead of just any solution x like mldivide does, you can use
x = lsqminnorm(A, b);
To see what algorithm is chosen by mldivide, you can call
dA = decomposition(A);
and check the Type property of the returned object.
  3 Kommentare
Christine Tobler
Christine Tobler am 11 Mär. 2025
The QR approach in linsolve and mldivide (they are the same) is to do a permuted QR decomposition, estimate the rank r of A based on the diagonal of the R matrix, and then truncate the system to only use the first r rows and columns of R. See the Answer here for details.
For linsolve, setting RECT will have A treated like a rectangular matrix (meaning QR decomposition is used), which safe for a square matrix. If a non-upper triangular matrix is passed in, but A is not upper triangular, its lower triangle will just be assumed to be zero. The same is true for lower triangular and symmetric, where we just read one triangle of the matrix and assume the other is its conjugate transpose.
So the output is always "valid" in the sense that the operation is valid for how the input matrix is interpreted, but it could be confusing. I will let the documentation team know your suggestions.
For the statement in mldivide, perhaps that second bullet point should say "If A is a square n-by-n matrix and B is a matrix with n rows, then x = A\B is a solution to the equation A*x = B, if A is well-conditioned.".
On having mldivide detect this case: Performance is part of the problem, particularly because a majority of calls to mldivide are from applications that always use square matrices. But more importantly, the QR algorithm we use relies on a tolerance for detecting the rank of a matrix, which is fine for a least-squares problem, but may not give the expected result otherwise.
For example, if A was a diagonal matrix with very different scaling on the diagonal, the smallest elements on the diagonal would be cut off, with those elements of the output getting set to zero:
format shortg
linsolve(diag(10.^(0:-5:-30)), ones(7, 1), struct('RECT', true))
Warning: Rank deficient, rank = 3, tol = 1.554312e-15.
ans = 7×1
1.0e+00 * 1 1e+05 1e+10 0 0 0 0
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
linsolve(diag(10.^(0:-5:-30)), ones(7, 1))
Warning: Matrix is close to singular or badly scaled. Results may be inaccurate. RCOND = 1.000000e-30.
ans = 7×1
1.0e+00 * 1 1e+05 1e+10 1e+15 1e+20 1e+25 1e+30
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
This is because the QR-based algorithm assumes that any change of A that is less than eps * norm(A) (more-or-less) is irrelevant, so these small diagonal elements should have no impact and should be removed to avoid potential Inf/NaN/overflow. But if we're not looking for a least-squares solution, but just for a direct solution, these elements have massive impact, because the smaller the element is, the larger its inverse.

Melden Sie sich an, um zu kommentieren.

Weitere Antworten (1)

Jack
Jack am 8 Mär. 2025
Hey Paul,
Good question. mldivide (\) is supposed to find a solution when one exists, but when A is square and singular, MATLAB treats it as numerically unstable and returns NaNs instead of picking one solution.
The reason A\b works in sym but not in double is that the symbolic version does exact algebra, so it can return a valid solution even when there are infinitely many. The numeric version, on the other hand, works with floating-point numbers. Since A is singular, MATLAB can’t get a stable least-squares solution, so it just throws out NaNs.
If you still want a solution in double, you can use the pseudoinverse instead:
x3 = pinv(A) * b;
That should give you one possible solution.
As for the doc page, technically mldivide is doing what it says—it guarantees a solution when A is invertible or full-rank, but when it’s singular, it just gives a warning and returns NaNs instead of trying to pick one from infinitely many.
Follow me so you can message me anytime with future MATLAB questions. If this helps, please accept the answer as well.
  13 Kommentare
Jack
Jack am 10 Mär. 2025
Bearbeitet: Jack am 10 Mär. 2025

Pinv relies on computing the singular value decomposition (SVD), which has cubic complexity and can be significantly slower than LU or QR for large matrices. In a 2000×2000 matrix, LU takes roughly 0.03 seconds while SVD takes about 0.84 seconds—about 30 times slower. If mldivide were modified to return a solution via pinv for square, singular matrices, users might indeed notice a substantial performance penalty, especially on larger problems.

In essence, mldivide is designed to return a solution only when it can do so reliably using its standard LU (or QR, for non-square cases) methods. When the matrix is singular and the system has infinitely many solutions, returning a particular solution via pinv (which computes the minimum-norm solution) could be computationally intensive and might not be what a typical user expects, given that the solution isn’t unique.

Regarding returning diagnostic information about which algorithm was used, that could be useful for advanced users. However, mldivide’s simplicity as a one-line operator is one of its strengths, and changing its interface (for example, adding a second output to indicate the algorithm) could break backward compatibility or complicate usage for the majority of users. A more balanced approach might be to provide a separate diagnostic function that inspects the matrix condition and details the decomposition strategy used internally without altering mldivide’s behavior.

Walter Roberson
Walter Roberson am 12 Mär. 2025
The vast majority of mldivide use is through the \ operator, which is a syntax that does not permit returning multiple outputs. In order for it to be used in a syntax that permitted returning multiple outputs, it would pretty much have to be invoked as
[Result, ExtraInfo] = mldivide(A,b);
though I guess it would also work to invoke
[Result, ExtraInfo] = A\b;
Currently that syntax generates an error about "Too many output arguments."
So there cannot be any present use of returning multiple outputs from \ or mldivide() that does not generate an error message. Accordingly, the only possible interferance with backwards compatibility by adding a second output argument would be against people deliberately writing
try; [Result, Extrainfo] = A\b; report_that_forbidden_event_has_occurred(); end
the number of which is surely vanishingly small.

Melden Sie sich an, um zu kommentieren.

Kategorien

Mehr zu Linear Algebra finden Sie in Help Center und File Exchange

Produkte


Version

R2024b

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!

Translated by