Skip to content

matrix/inverse_of_matrix: 3x3 inverse returns the transpose of the true inverse (adjoint computed but unused) #14813

Description

@aimasteracc

Description

matrix/inverse_of_matrix.py::inverse_of_matrix returns the transpose of the true inverse for 3×3 matrices. It builds the adjoint (transpose of the cofactor matrix) into adjoint_matrix, then never uses it — the inverse is computed from the un-transposed cofactor_matrix instead:

# Transpose the cofactor matrix (Adjoint matrix)
adjoint_matrix = array(cofactor_matrix)
for i in range(3):
    for j in range(3):
        adjoint_matrix[i][j] = cofactor_matrix[j][i]      # adjoint computed correctly...

# Inverse of the matrix using the formula (1/determinant) * adjoint matrix
inverse_matrix = array(cofactor_matrix)                   # BUG: uses cofactor_matrix, not adjoint_matrix
for i in range(3):
    for j in range(3):
        inverse_matrix[i][j] /= d(determinant)

The correct formula is inverse = (1/det) * adjoint = (1/det) * transpose(cofactor). Because inverse_matrix is seeded from cofactor_matrix (line 148) and adjoint_matrix is discarded, the result is (1/det) * cofactor — the transpose of the real inverse. For any non-symmetric 3×3 matrix the returned matrix is wrong (it does not satisfy M @ M⁻¹ == I). The 2×2 branch is correct.

The existing 3×3 doctest enshrines the wrong (transposed) value, so the test suite does not catch it.

Steps to reproduce

import numpy as np
from matrix.inverse_of_matrix import inverse_of_matrix

M = [[2, 5, 7], [2, 0, 1], [1, 2, 3]]          # the repo's own doctest input
got = inverse_of_matrix(M)
print(got)                                      # [[2.0, 5.0, -4.0], [1.0, 1.0, -1.0], [-5.0, -12.0, 10.0]]
print(np.linalg.inv(np.array(M, float)).tolist())
#   correct inverse: [[2, 1, -5], [5, 1, -12], [-4, -1, 10]]  (the transpose of `got`)
print((np.array(M, float) @ np.array(got, float)))
#   M @ got = [[-26, -69, 57], [-1, -2, 2], [-11, -29, 24]]   -- NOT the identity

Expected behavior

inverse_of_matrix(M) returns M⁻¹ such that M @ M⁻¹ == I. For the example above that is [[2, 1, -5], [5, 1, -12], [-4, -1, 10]].

Actual behavior

It returns [[2, 5, -4], [1, 1, -1], [-5, -12, 10]] — the transpose of M⁻¹. M @ result is not the identity matrix, so the result is not an inverse.

Suggested fix

Use the adjoint when seeding the inverse (and correct the 3×3 doctests, which currently assert the transposed values):

inverse_matrix = array(adjoint_matrix)   # was: array(cofactor_matrix)
for i in range(3):
    for j in range(3):
        inverse_matrix[i][j] /= d(determinant)

After the fix, verify with numpy.allclose(M @ inverse_of_matrix(M), numpy.eye(3)) for several non-symmetric matrices.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions