PINV

The Moore–Penrose pseudoinverse generalizes the matrix inverse to non-square or singular matrices. It is widely used for computing least-squares solutions and analyzing the effective rank of a matrix.

Given a matrix A with singular value decomposition A = U\Sigma V^H, the pseudoinverse is defined as A^+ = V \Sigma^+ U^H, where \Sigma^+ replaces each nonzero singular value \sigma with 1/\sigma and transposes the diagonal.

Excel Usage

=PINV(matrix, atol, rtol, return_rank, check_finite)
  • matrix (list[list], required): Matrix to pseudo-invert. Scalars are treated as 1x1 matrices.
  • atol (float, optional, default: 0): Absolute threshold for small singular values to be treated as zero.
  • rtol (float, optional, default: null): Relative threshold for small singular values. If None, uses max(M, N) * eps.
  • return_rank (bool, optional, default: false): If True, return the effective rank instead of the pseudoinverse.
  • check_finite (bool, optional, default: true): If True, verify the input contains only finite numbers.

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

Example 1: Pseudoinverse of a 2x2 invertible matrix

Inputs:

matrix
1 2
3 4

Excel formula:

=PINV({1,2;3,4})

Expected output:

Result
-2 1
1.5 -0.5
Example 2: Pseudoinverse of a 3x2 rectangular matrix

Inputs:

matrix
1 2
3 4
5 6

Excel formula:

=PINV({1,2;3,4;5,6})

Expected output:

Result
-1.33333 -0.333333 0.666667
1.08333 0.333333 -0.416667
Example 3: Effective rank of a 2x2 matrix

Inputs:

matrix return_rank
1 2 true
3 4

Excel formula:

=PINV({1,2;3,4}, TRUE)

Expected output:

2

Example 4: Pseudoinverse with custom atol and rtol thresholds

Inputs:

matrix atol rtol
1 2 0.00001 0.00001
3 4

Excel formula:

=PINV({1,2;3,4}, 0.00001, 0.00001)

Expected output:

Result
-2 1
1.5 -0.5

Python Code

import numpy as np
from scipy.linalg import pinv as scipy_pinv

def pinv(matrix, atol=0, rtol=None, return_rank=False, check_finite=True):
    """
    Compute the Moore-Penrose pseudoinverse of a matrix using singular value decomposition (SVD).

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

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

    Args:
        matrix (list[list]): Matrix to pseudo-invert. Scalars are treated as 1x1 matrices.
        atol (float, optional): Absolute threshold for small singular values to be treated as zero. Default is 0.
        rtol (float, optional): Relative threshold for small singular values. If None, uses max(M, N) * eps. Default is None.
        return_rank (bool, optional): If True, return the effective rank instead of the pseudoinverse. Default is False.
        check_finite (bool, optional): If True, verify the input contains only finite numbers. Default is True.

    Returns:
        list[list]: 2D pseudoinverse matrix, or error message string.
    """
    try:
        # Helper function to normalize scalar/single-element input to 2D list
        def to2d(x):
            return [[x]] if not isinstance(x, list) else x

        # Normalize scalar input into a 2D list for consistent handling
        matrix = to2d(matrix)
        if not isinstance(matrix[0], list):
            # Handle case where input was a 1D list
            return "Error: Invalid input: matrix must be a 2D list or scalar numeric value."

        # Convert scalar values that came from Excel single-element arrays
        for i, row in enumerate(matrix):
            for j, val in enumerate(row):
                if isinstance(val, (int, float, bool)):
                    matrix[i][j] = float(val)
                elif isinstance(val, str):
                    try:
                        matrix[i][j] = float(val)
                    except Exception:
                        return "Error: Invalid input: matrix must contain only numeric values."

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

        # Validate optional parameters
        try:
            atol_value = float(atol)
        except Exception:
            return "Error: Invalid input: atol must be a numeric value."

        if rtol is not None:
            try:
                rtol_value = float(rtol)
            except Exception:
                return "Error: Invalid input: rtol must be a numeric value or None."
        else:
            rtol_value = None

        if not isinstance(return_rank, bool):
            return "Error: Invalid input: return_rank must be a boolean."
        if not isinstance(check_finite, bool):
            return "Error: Invalid input: check_finite must be a boolean."

        # Convert the matrix elements to floats, ensuring numeric values
        numeric_rows = []
        for row in matrix:
            numeric_row = []
            for value in row:
                try:
                    numeric_row.append(float(value))
                except Exception:
                    return "Error: Invalid input: matrix must contain only numeric values."
            numeric_rows.append(numeric_row)

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

        # Compute the pseudoinverse and optionally the rank
        if return_rank:
            _, rank = scipy_pinv(
                arr,
                atol=atol_value,
                rtol=rtol_value,
                return_rank=True,
                check_finite=check_finite,
            )
            if not np.isfinite(rank):
                return "Error: scipy.linalg.pinv error: effective rank is not finite."
            return float(rank)

        result = scipy_pinv(
            arr,
            atol=atol_value,
            rtol=rtol_value,
            return_rank=False,
            check_finite=check_finite,
        )

        # Ensure the result does not contain non-finite values before returning to Excel
        if not np.all(np.isfinite(result)):
            return "Error: scipy.linalg.pinv error: result contains non-finite values."

        return result.tolist()
    except Exception as e:
        return f"Error: {str(e)}"

Online Calculator

Matrix to pseudo-invert. Scalars are treated as 1x1 matrices.
Absolute threshold for small singular values to be treated as zero.
Relative threshold for small singular values. If None, uses max(M, N) * eps.
If True, return the effective rank instead of the pseudoinverse.
If True, verify the input contains only finite numbers.