Question

I am trying to Learn Me a Haskell, and I decided to practice by writing a simple function for inverting 3x3 matrices. It should be easy, yet nothing I try will compile successfully.

Here is my code:

matInv3x3 :: [[Double]] -> [[Double]]
matInv3x3 m
    | length m /= 3         = error "wrong number of rows"
    | length (m !! 0) /= 3  = error "wrong number of elements in row 0"
    | length (m !! 1) /= 3  = error "wrong number of elements in row 1"
    | length (m !! 2) /= 3  = error "wrong number of elements in row 2"
    | det == 0              = error "zero determinant"
    | otherwise = mInv
    where   a = m !! 0 !! 0
            b = m !! 0 !! 1
            c = m !! 0 !! 2
            d = m !! 1 !! 0
            e = m !! 1 !! 1
            f = m !! 1 !! 2
            g = m !! 2 !! 0
            h = m !! 2 !! 1
            i = m !! 2 !! 2
            det = a*(e*i - f*h) - b*(i*d - f*g) + c*(d*h - e*g)
            A = (e*i - f*h) / det
            B = -(d*i - f*g) / det
            C = (d*h - e*g) / det
            D = -(b*i - c*h) / det
            E = (a*i - c*g) / det
            F = -(a*h - b*g) / det
            G = (b*f - c*e) / det
            H = -(a*f - c*d) / det
            I = (a*e - b*d) / det
            mInv = [[A,B,C],[D,E,F],[G,H,I]]

I am trying to guard against everything which can go wrong: bad list dimensions, and zero determinant. I modeled it after examples in the 'Learn You A...' book. I am trying to rely on the lazy evaluation in case the matrix has zero determinant.

GHCi won't compile it, citing a parse error for the '=' on line 10 (where b is defined). I'm sure there is some simple, fundamental thing I am missing. Can someone point out what I did wrong?

UPDATE:

I implemented the fixes proposed in the comments, and also corrected the swapped indices mistake I made (didn't spot it before, as the code wouldn't compile). Here is the fixed code, which inverts 3x3 matrices correctly:

matInv3x3 :: [[Double]] -> [[Double]]
matInv3x3 m
    | length m /= 3         = error "wrong number of rows"
    | length (m !! 0) /= 3  = error "wrong number of elements in row 0"
    | length (m !! 1) /= 3  = error "wrong number of elements in row 1"
    | length (m !! 2) /= 3  = error "wrong number of elements in row 2"
    | abs det < 1.0e-15     = error "zero or near-zero determinant"
    | otherwise = mInv
    where   [[a,d,g],[b,e,h],[c,f,i]] = m
            det = a*(e*i - f*h) - b*(i*d - f*g) + c*(d*h - e*g)
            a' = (e*i - f*h) / det
            b' = -(d*i - f*g) / det
            c' = (d*h - e*g) / det
            d' = -(b*i - c*h) / det
            e' = (a*i - c*g) / det
            f' = -(a*h - b*g) / det
            g' = (b*f - c*e) / det
            h' = -(a*f - c*d) / det
            i' = (a*e - b*d) / det
            mInv = [[a',b',c'],[d',e',f'],[g',h',i']]
Was it helpful?

Solution

A good exercise would be to generalize this function to arbitrary nxn matrices. Here is one way to calculate the determinant of an nxn as a start in case you are interested.

-- Remove the nth element from a list
remove :: Int -> [a] -> [a]
remove n xs = ys ++ (tail zs)
  where
    (ys, zs) = splitAt n xs

-- Minor matrix of cofactor C(i,j)
minor :: Int -> Int -> [[a]] -> [[a]]
minor i j xs = remove j $ map (remove i) xs

-- The determinant of a square matrix represented as a list of lists
-- representing column vectors, that is [column].
det :: Num a => [[a]] -> a
det (a:[]) = head a
det m = sum [(-1)^i * (c1 !! i) * det (minor i 0 m) | i <- [0 .. (n-1)]]
  where
    c1 = head m
    n = length m
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top