PINV
Overview
The PINV function computes the Moore-Penrose pseudoinverse of a matrix using singular value decomposition (SVD). The pseudoinverse is a generalization of the matrix inverse that exists for any matrix, including non-square or singular matrices. It is widely used to solve linear systems that may not have a unique solution, and in applications such as least squares fitting and signal processing. The pseudoinverse of a matrix is the unique matrix that satisfies the four Moore-Penrose conditions:
where denotes the conjugate transpose. If is invertible then the Moore-Penrose pseudoinverse is exactly the inverse of . If is not invertible then the Moore-Penrose pseudoinverse computes the solution to such that is minimized. This wrapper uses scipy.linalg.pinv, returning only the effective rank (instead of the rank and pseudoinverse pair) when return_rank is TRUE, and it treats scalar inputs as matrices for Excel compatibility. For more details, see the SciPy documentation .
This example function is provided as-is without any representation of accuracy.
Usage
To use the function in Excel:
=PINV(matrix, [atol], [rtol], [return_rank], [check_finite])matrix(2D list or scalar, required): Matrix to pseudo-invert. Scalars are interpreted as matrices, and each row must have the same length.atol(float, optional, default=0.0): Absolute threshold for small singular values to be treated as zero.rtol(float, optional, default=None): Relative threshold for small singular values. If omitted, SciPy usesmax(M, N) * eps.return_rank(bool, optional, default=FALSE): WhenTRUE, the function returns a single-cell table containing the effective rank instead of the pseudoinverse.check_finite(bool, optional, default=TRUE): WhenTRUE, verifies the input matrix contains only finite values before computation.
The function returns a 2D list containing the pseudoinverse of the supplied matrix. If return_rank is TRUE, the function returns a 2D list with a single value representing the effective rank. Invalid inputs yield a 2D list containing an error message.
Examples
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.000 | 1.000 |
| 1.500 | -0.500 |
Example 2: Pseudoinverse of a 3x2 matrix
Inputs:
| matrix | |
|---|---|
| 1 | 2 |
| 3 | 4 |
| 5 | 6 |
Excel formula:
=PINV({1,2;3,4;5,6})Expected output:
| Result | ||
|---|---|---|
| -1.333 | -0.333 | 0.667 |
| 1.083 | 0.333 | -0.417 |
Example 3: Pseudoinverse with return_rank=TRUE
Inputs:
| matrix | return_rank | |
|---|---|---|
| 1 | 2 | TRUE |
| 3 | 4 |
Excel formula:
=PINV({1,2;3,4}, , , TRUE)Expected output:
| Result |
|---|
| 2.000 |
Example 4: Pseudoinverse with atol and rtol
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.000 | 1.000 |
| 1.500 | -0.500 |
Python Code
from typing import List, Optional, Union
import numpy as np
from scipy.linalg import pinv as scipy_pinv
MatrixValue = Union[float, bool, str, None]
MatrixInput = Union[MatrixValue, List[List[MatrixValue]]]
MatrixOutput = Union[List[List[float]], List[List[str]]]
def pinv(
matrix: MatrixInput,
atol: float = 0.0,
rtol: Optional[float] = None,
return_rank: bool = False,
check_finite: bool = True,
) -> MatrixOutput:
"""
Compute the Moore-Penrose pseudoinverse of a matrix using singular value decomposition (SVD).
Args:
matrix: 2D list (or scalar interpreted as a 1x1 matrix) containing numeric values to be pseudo-inverted.
atol: Absolute threshold for small singular values (default: 0.0).
rtol: Relative threshold for small singular values (default: None, meaning SciPy decides).
return_rank: If True, return the effective rank as a 2D list with a single value (default: False).
check_finite: If True, verify the input contains only finite numbers before computing (default: True).
Returns:
Pseudoinverse of the matrix as a 2D list, or [[rank]] if return_rank is True, or an error message as a 2D list of strings.
This example function is provided as-is without any representation of accuracy.
"""
# Normalize scalar input into a 2D list for consistent handling
if not isinstance(matrix, list):
if isinstance(matrix, (int, float, bool)):
matrix = [[float(matrix)]]
elif isinstance(matrix, str):
try:
matrix = [[float(matrix)]]
except Exception:
return [["Invalid input: matrix must be numeric."]]
else:
return [["Invalid input: matrix must be a 2D list or scalar numeric value."]]
# 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 [["Invalid input: matrix must be a 2D list with at least one row."]]
if any(len(row) == 0 for row in matrix):
return [["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 [["Invalid input: all rows in matrix must have the same length."]]
# Validate optional parameters
try:
atol_value = float(atol)
except Exception:
return [["Invalid input: atol must be a numeric value."]]
if rtol is not None:
try:
rtol_value: Optional[float] = float(rtol)
except Exception:
return [["Invalid input: rtol must be a numeric value or None."]]
else:
rtol_value = None
if not isinstance(return_rank, bool):
return [["Invalid input: return_rank must be a boolean."]]
if not isinstance(check_finite, bool):
return [["Invalid input: check_finite must be a boolean."]]
# Convert the matrix elements to floats, ensuring numeric values
numeric_rows: List[List[float]] = []
for row in matrix:
numeric_row: List[float] = []
for value in row:
try:
numeric_row.append(float(value))
except Exception:
return [["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
try:
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 [["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,
)
except Exception as exc:
return [[f"scipy.linalg.pinv error: {exc}"]]
# Ensure the result does not contain non-finite values before returning to Excel
if not np.all(np.isfinite(result)):
return [["scipy.linalg.pinv error: result contains non-finite values."]]
return result.tolist()