3d Matrix Inversion

Keith Vetter 2005-11-02 - today I needed to do some linear programming on equations of three unknowns and hence, wanted to invert some 3x3 matrices. For matrices this small, it's easiest just to compute the inverse as the adjoint matrix divided by the determinant .

Here's the code--it works fine for my toy application, but the code ignores all the thorny issues of numerical stability that plague general matrix code.


 proc Inverse3 {matrix} {
    if {[llength $matrix] != 3 ||
        [llength [lindex $matrix 0]] != 3 || 
        [llength [lindex $matrix 1]] != 3 || 
        [llength [lindex $matrix 2]] != 3} {
        error "wrong sized matrix"
    }
    set inv {{? ? ?} {? ? ?} {? ? ?}}
 
    # Get adjoint matrix : transpose of cofactor matrix
    for {set i 0} {$i < 3} {incr i} {
        for {set j 0} {$j < 3} {incr j} {
            lset inv $i $j [_Cofactor3 $matrix $i $j]
        }
    }
    # Now divide by the determinant
    set det [expr {double([lindex $matrix 0 0]   * [lindex $inv 0 0]
                   + [lindex $matrix 0 1] * [lindex $inv 1 0]
                   + [lindex $matrix 0 2] * [lindex $inv 2 0])}]
    if {$det == 0} {
        error "non-invertable matrix"
    }
    
    for {set i 0} {$i < 3} {incr i} {
        for {set j 0} {$j < 3} {incr j} {
            lset inv $i $j [expr {[lindex $inv $i $j] / $det}]
        }
    }
    return $inv
 }
 proc _Cofactor3 {matrix i j} {
    array set COLS {0 {1 2} 1 {0 2} 2 {0 1}}
    foreach {row1 row2} $COLS($j) break
    foreach {col1 col2} $COLS($i) break
    
    set a [lindex $matrix $row1 $col1]
    set b [lindex $matrix $row1 $col2]
    set c [lindex $matrix $row2 $col1]
    set d [lindex $matrix $row2 $col2]
 
    set det [expr {$a*$d - $b*$c}]
    if {($i+$j) & 1} { set det [expr {-$det}]}
    return $det
 }

DKF: Here's an alternate version of _Cofactor3 that should be a bit (~30%) faster (array setup costs are a little high and lassign is faster than foreach+break for the case it handles):

proc _Cofactor3 {matrix i j} {
    set COLS {{1 2} {0 2} {0 1}}
    lassign [lindex $COLS $j] row1 row2
    lassign [lindex $COLS $i] col1 col2
    
    set a [lindex $matrix $row1 $col1]
    set b [lindex $matrix $row1 $col2]
    set c [lindex $matrix $row2 $col1]
    set d [lindex $matrix $row2 $col2]
 
    set det [expr {$a*$d - $b*$c}]
    if {($i+$j) & 1} { set det [expr {-$det}]}
    return $det
}