QR

Overview

The QR function computes the QR decomposition (also known as QR factorization) of a matrix, returning either the orthogonal matrix Q or the upper triangular matrix R. QR decomposition is a fundamental matrix factorization technique in linear algebra with wide applications in solving linear systems, least squares problems, and eigenvalue computations.

For any real m \times n matrix A, the QR decomposition expresses it as:

A = QR

where Q is an orthogonal matrix (meaning Q^T Q = I, so its columns form an orthonormal basis) and R is an upper triangular matrix (also called right triangular). For complex matrices, Q is unitary, satisfying Q^\dagger Q = I. When A is invertible and diagonal elements of R are constrained to be positive, this factorization is unique.

This implementation uses the scipy.linalg.qr function from SciPy, which interfaces with optimized LAPACK routines (dgeqrf, zgeqrf, dorgqr, zungqr) for numerical stability and performance. The function computes the full QR decomposition (mode=‘full’), producing a Q matrix of shape (M, M) and an R matrix of shape (M, N).

Several algorithms exist for computing QR decomposition, including the Gram–Schmidt process, Householder reflections, and Givens rotations. SciPy’s implementation uses Householder reflections, which offer superior numerical stability compared to the classical Gram–Schmidt process. For more theoretical background, see the Wikipedia article on QR decomposition.

Common applications include:

  • Solving linear least squares problems: QR provides a numerically stable approach to finding \hat{x} that minimizes \|Ax - b\|
  • Computing matrix determinants: \det(A) = \prod_i r_{ii} where r_{ii} are diagonal elements of R
  • Eigenvalue algorithms: The QR algorithm iteratively applies QR decomposition to compute eigenvalues
  • Orthonormalization: The columns of Q form an orthonormal basis for the column space of A

This example function is provided as-is without any representation of accuracy.

Excel Usage

=QR(a, return_type)
  • a (list[list], required): Matrix to decompose. Each row must have the same number of columns, and entries must be finite numeric values.
  • return_type (str, optional, default: “Q”): Which matrix factor to return from the QR decomposition.

Returns (list[list]): 2D Q or R matrix, or error message string.

Examples

Example 1: Orthogonal factor of a 2x2 matrix

Inputs:

a return_type
1 2 Q
3 4

Excel formula:

=QR({1,2;3,4}, "Q")

Expected output:

Result
-0.3162 -0.9487
-0.9487 0.3162

Example 2: Upper-triangular factor of a 2x2 matrix

Inputs:

a return_type
1 2 R
3 4

Excel formula:

=QR({1,2;3,4}, "R")

Expected output:

Result
-3.1623 -4.4272
0 -0.6325

Example 3: Orthogonal factor of a 3x2 rectangular matrix

Inputs:

a return_type
1 2 Q
3 4
5 6

Excel formula:

=QR({1,2;3,4;5,6}, "Q")

Expected output:

Result
-0.169 0.8971 0.4082
-0.5071 0.276 -0.8165
-0.8452 -0.345 0.4082

Example 4: Upper-triangular factor of a 3x2 rectangular matrix

Inputs:

a return_type
1 2 R
3 4
5 6

Excel formula:

=QR({1,2;3,4;5,6}, "R")

Expected output:

Result
-5.9161 -7.4374
0 0.8281
0 0

Python Code

import numpy as np
from scipy.linalg import qr as scipy_linalg_qr

def qr(a, return_type='Q'):
    """
    Compute the QR decomposition of a matrix and return either Q or R.

    See: https://docs.scipy.org/doc/scipy/reference/generated/scipy.linalg.qr.html

    This example function is provided as-is without any representation of accuracy.

    Args:
        a (list[list]): Matrix to decompose. Each row must have the same number of columns, and entries must be finite numeric values.
        return_type (str, optional): Which matrix factor to return from the QR decomposition. Valid options: Q, R. Default is 'Q'.

    Returns:
        list[list]: 2D Q or R matrix, or error message string.
    """
    def to2d(x):
        return [[x]] if not isinstance(x, list) else x

    # Normalize scalar inputs into a 2D list structure
    a = to2d(a)
    if not isinstance(a[0], list):
        # Handle case where input was a 1D list like [1, 2, 3]
        if all(not isinstance(item, list) for item in a):
            try:
                a = [[float(item)] for item in a]
            except (TypeError, ValueError):
                return "Invalid input: a must contain only numeric values."

    # Validate the 2D list structure and ensure consistent row lengths
    if not a or not all(isinstance(row, list) for row in a):
        return "Invalid input: a must be a 2D list with at least one row."
    if any(len(row) == 0 for row in a):
        return "Invalid input: rows in a must contain at least one value."
    column_count = len(a[0])
    if any(len(row) != column_count for row in a):
        return "Invalid input: all rows in a must have the same length."

    # Convert entries to floats and check for finite values
    numeric_rows = []
    for row in a:
        numeric_row = []
        for value in row:
            try:
                numeric_value = float(value)
            except Exception:
                return "Invalid input: a must contain only numeric values."
            if not np.isfinite(numeric_value):
                return "Invalid input: a must contain only finite numbers."
            numeric_row.append(numeric_value)
        numeric_rows.append(numeric_row)

    array = np.array(numeric_rows, dtype=float)

    # Validate return_type and normalize casing
    if not isinstance(return_type, str) or return_type.upper() not in ("Q", "R"):
        return "Invalid input: return_type must be 'Q' or 'R'."
    normalized_return_type = return_type.upper()

    # Compute the QR decomposition
    try:
        q_matrix, r_matrix = scipy_linalg_qr(array, mode="full")
    except Exception as exc:
        return f"QR decomposition error: {exc}"

    result = q_matrix if normalized_return_type == "Q" else r_matrix

    # Ensure the result is finite before returning
    if not np.all(np.isfinite(result)):
        return "QR decomposition error: result contains non-finite values."

    return result.tolist()

Online Calculator