Skip to content

qr_solve(): unexpected failure on 4x4 system #983

@ericjwhitney

Description

@ericjwhitney

Attempting to solve the following system of equations using qr_solve(A, b) results in an error, although it appears the solution should exist:

$$\mathbf{A} = \begin{bmatrix} 1 & -π/20 & (-π/20)^2 & (-π/20)^3 \\ 1 & 0 & 0 & 0 \\ 1 & π/20 & (π/20)^2 & (π/20)^3 \\ 1 & π/10 & (π/10)^2 & (π/10)^3 \\ \end{bmatrix}$$

$$\mathbf{x} = \begin{bmatrix} sin(-π/20) \\ 0 \\ sin(π/20) \\ sin(π/20) \end{bmatrix}$$

Reproducing code example, cpython version = 3.12.1 mpmath version = 1.3.0:

from mpmath import mp

A = mp.matrix([[mp.one, -mp.pi / 20, (-mp.pi / 20) ** 2, (-mp.pi / 20) ** 3],
               [mp.one, mp.zero, mp.zero, mp.zero],
               [mp.one, mp.pi / 20, (mp.pi / 20) ** 2, (mp.pi / 20) ** 3],
               [mp.one, mp.pi / 10, (mp.pi / 10) ** 2, (mp.pi / 10) ** 3]])

b = mp.matrix([[mp.sin(-mp.pi / 20)],
               [mp.zero],
               [mp.sin(mp.pi / 20)],
               [mp.sin(mp.pi / 20)]])

# Uncommenting the line below allows the QR householder method to work. I
# think this is because it allows a slight non-zero result to appear so
# execution can continue. Any 'dps' value greater than 10 causes
# failure.
#
# mp.dps = 10

x_qr, residual_qr = mp.qr_solve(A, b)  # <- Gives error.

print(f"\nSolution using QR decomposition x_qr:\n{x_qr}")
print(f"Residual Ax_qr - b:\n{residual_qr}")

Error message:

Traceback (most recent call last):
  File "...\mpmath_issue.py", line 48, in <module>
    x_qr, residual_qr = mp.qr_solve(A, b)
                        ^^^^^^^^^^^^^^^^^
  File "...\mpmath\matrices\linalg.py", line 407, in qr_solve
    H, p, x, r = ctx.householder(ctx.extend(A, b))
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "...\mpmath\matrices\linalg.py", line 341, in householder
    raise ValueError('matrix is numerically singular')
ValueError: matrix is numerically singular

By comparison, lu_solve(A, b) has no problem:

print(f"Condition number of A: {mp.cond(A)}")

# Solution using LU decomposition -> Works fine.
x_lu = mp.lu_solve(A, b)
residual_lu = A * x_lu - b
print(f"\nSolution using LU decomposition x_lu:\n{x_lu}")
print(f"\nResidual Ax_lu - b:\n{residual_lu}")

Gives result:

Condition number of A: 694.870840206284

Solution using LU decomposition x_lu:
[             0.0]
[1.16187485778415]
[             0.0]
[-6.7270020477122]

Residual Ax_lu - b:
[0.0]
[0.0]
[0.0]
[0.0]

Cross-checking with Wolfram|alpha confirms the system is invertible / solvable, and lines up with the lu_solve() solution:

$$\mathbf{A}^{-1} = \begin{bmatrix} 0 & 1 & 0 & 0 \\ -20/(3π) & -10/π & 20/π & -10/(3π) \\ 200/π^2 & -400/π^2 & 200/π^2 & 0 \\ -4000/(3π^3) & 4000/π^3 & -4000/π^3 & 4000/(3π^3) \end{bmatrix}$$

$$\mathbf{x} = \mathbf{A}^{-1} \mathbf{b} = \begin{bmatrix} 0 \\ 70 sin(π/20)/(3π) \\ 0 \\ -4000 sin(π/20)/(3π^3) \end{bmatrix}$$

I believe the problem is likely this line in householder(ctx, A) in mpmath/matrices/linalg.py:

if not abs(s) > ctx.eps:  # <- Problem area?
    raise ValueError('matrix is numerically singular')

I suspect this check might be in the wrong location... it may not be required here at all? From memory this should check that if the sum is zero, then nothing further needs to be done with that particular column?

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugan unexpected problem or unintended behaviorneed decision

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions