3

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']]
12
  • There is no error on the line you mentioned when I compile it - perhaps your indentation in your editor is different than here? On another note, you can't give variables names starting with capital letters - A,B .. I - are all invalid. You can do _A. Commented Feb 13, 2014 at 3:15
  • Haskell is white-space sensitive. The where clause looks suspicious to me, I'd put it in at least three more spaces to be sure. Commented Feb 13, 2014 at 3:42
  • 2
    The conventional names for those values would be a' through i'. GHC treats values starting with _ specially - it doesn't warn if they're unused. I wouldn't recommend that naming. Commented Feb 13, 2014 at 3:43
  • 4
    you can assign all matrix elements in single line [[a,d,g],[b,e,h],[c,f,i]] = m Commented Feb 13, 2014 at 4:23
  • 1
    @user2790167 Could you put your answer in the answer box, instead of in the question, please? Commented Feb 13, 2014 at 22:45

1 Answer 1

1

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
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.