Skip to content

Proposed: glm::cofactor() and glm::adjoint() #1406

@XplicitComputing

Description

@XplicitComputing

glm::determinant(glm::mat) is very useful...
However, it is a product of the cofactor matrix, which computes sub-dimensional determinants, eventually outputting a single scalar. (There are physical and photonic methods that require tensor intermediates).

Striving for modularity:
The cofactor matrix should be the basis of computing determinants, so that developers don't have to roll their own and the machinery used to compute cofactors can be optimized and reused for determinants.

Below is an unrolled implementation of 2d, 3d, 4d cofactors:


glm::dmat2 cofactor(const glm::dmat2& A)
{
    return {A[1][1], -A[0][1], -A[1][0], A[0][0]};
}

glm::dmat3 cofactor(const glm::dmat3& A)
{
    glm::dmat3 B;
    B[0][0] =  A[1][1]*A[2][2] - A[1][2]*A[2][1];                      // +M00
    B[0][1] = -(A[1][0]*A[2][2] - A[1][2]*A[2][0]);                    // -M01
    B[0][2] =  A[1][0]*A[2][1] - A[1][1]*A[2][0];                      // +M02
    B[1][0] = -(A[0][1]*A[2][2] - A[0][2]*A[2][1]);                    // -M10
    B[1][1] =  A[0][0]*A[2][2] - A[0][2]*A[2][0];                      // +M11
    B[1][2] = -(A[0][0]*A[2][1] - A[0][1]*A[2][0]);                    // -M12
    B[2][0] =  A[0][1]*A[1][2] - A[0][2]*A[1][1];                      // +M20
    B[2][1] = -(A[0][0]*A[1][2] - A[0][2]*A[1][0]);                    // -M21
    B[2][2] =  A[0][0]*A[1][1] - A[0][1]*A[1][0];                      // +M22

    return B;
}

glm::dmat4 cofactor(const glm::dmat4& A)
{
    glm::dmat4 C;
    // Cofactor matrix C[i][j] = (-1)^(i+j) * det(minor(i, j))
    // glm is column-major, so C[col][row] = C[j][i]
    // === Row 0 ===
    C[0][0] =  glm::determinant(glm::mat3(
        A[1][1], A[2][1], A[3][1],
        A[1][2], A[2][2], A[3][2],
        A[1][3], A[2][3], A[3][3]));
    C[1][0] = -glm::determinant(glm::mat3(
        A[0][1], A[2][1], A[3][1],
        A[0][2], A[2][2], A[3][2],
        A[0][3], A[2][3], A[3][3]));
    C[2][0] =  glm::determinant(glm::mat3(
        A[0][1], A[1][1], A[3][1],
        A[0][2], A[1][2], A[3][2],
        A[0][3], A[1][3], A[3][3]));
    C[3][0] = -glm::determinant(glm::mat3(
        A[0][1], A[1][1], A[2][1],
        A[0][2], A[1][2], A[2][2],
        A[0][3], A[1][3], A[2][3]));
    // === Row 1 ===
    C[0][1] = -glm::determinant(glm::mat3(
        A[1][0], A[2][0], A[3][0],
        A[1][2], A[2][2], A[3][2],
        A[1][3], A[2][3], A[3][3]));
    C[1][1] =  glm::determinant(glm::mat3(
        A[0][0], A[2][0], A[3][0],
        A[0][2], A[2][2], A[3][2],
        A[0][3], A[2][3], A[3][3]));
    C[2][1] = -glm::determinant(glm::mat3(
        A[0][0], A[1][0], A[3][0],
        A[0][2], A[1][2], A[3][2],
        A[0][3], A[1][3], A[3][3]));
    C[3][1] =  glm::determinant(glm::mat3(
        A[0][0], A[1][0], A[2][0],
        A[0][2], A[1][2], A[2][2],
        A[0][3], A[1][3], A[2][3]));
    // === Row 2 ===
    C[0][2] =  glm::determinant(glm::mat3(
        A[1][0], A[2][0], A[3][0],
        A[1][1], A[2][1], A[3][1],
        A[1][3], A[2][3], A[3][3]));
    C[1][2] = -glm::determinant(glm::mat3(
        A[0][0], A[2][0], A[3][0],
        A[0][1], A[2][1], A[3][1],
        A[0][3], A[2][3], A[3][3]));
    C[2][2] =  glm::determinant(glm::mat3(
        A[0][0], A[1][0], A[3][0],
        A[0][1], A[1][1], A[3][1],
        A[0][3], A[1][3], A[3][3]));
    C[3][2] = -glm::determinant(glm::mat3(
        A[0][0], A[1][0], A[2][0],
        A[0][1], A[1][1], A[2][1],
        A[0][3], A[1][3], A[2][3]));
    // === Row 3 ===
    C[0][3] = -glm::determinant(glm::mat3(
        A[1][0], A[2][0], A[3][0],
        A[1][1], A[2][1], A[3][1],
        A[1][2], A[2][2], A[3][2]));
    C[1][3] =  glm::determinant(glm::mat3(
        A[0][0], A[2][0], A[3][0],
        A[0][1], A[2][1], A[3][1],
        A[0][2], A[2][2], A[3][2]));
    C[2][3] = -glm::determinant(glm::mat3(
        A[0][0], A[1][0], A[3][0],
        A[0][1], A[1][1], A[3][1],
        A[0][2], A[1][2], A[3][2]));
    C[3][3] =  glm::determinant(glm::mat3(
        A[0][0], A[1][0], A[2][0],
        A[0][1], A[1][1], A[2][1],
        A[0][2], A[1][2], A[2][2]));

    return C;
}

On a separate note, for convenience the following adjoint functions could be added:

glm::dmat2 adjoint(const glm::dmat2& m) {return glm::transpose(cofactor(m));}
glm::dmat3 adjoint(const glm::dmat3& m) {return glm::transpose(cofactor(m));}
glm::dmat4 adjoint(const glm::dmat4& m) {return glm::transpose(cofactor(m));}

//or perhaps more generally:
template<typename Matrix>
Matrix adjoint(const Matrix& m){return glm::transpose(cofactor(m));};

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions