-- Some simple Hugs programs for manipulating matrices. -- module Matrix where import List type Matrix k = [Row k] -- matrix represented by a list of its rows type Row k = [k] -- a row represented by a list of literals -- General utility functions: shapeMat :: Matrix k -> (Int, Int) shapeMat mat = (rows mat, cols mat) rows :: Matrix k -> Int rows mat = length mat cols :: Matrix k -> Int cols mat = length (head mat) idMat :: Int -> Matrix Int idMat 0 = [] idMat (n+1) = [1:replicate n 0] ++ map (0:) (idMat n) -- Matrix multiplication: multiplyMat :: Matrix Int -> Matrix Int -> Matrix Int multiplyMat a b | cols a==rows b = [[row `dot` col | col<-b'] | row<-a] | otherwise = error "incompatible matrices" where v `dot` w = sum (zipWith (*) v w) b' = transpose b -- An attempt to implement the standard algorithm for converting a matrix -- to echelon form... echelon :: Matrix Int -> Matrix Int echelon rs | null rs || null (head rs) = rs | null rs2 = map (0:) (echelon (map tail rs)) | otherwise = piv : map (0:) (echelon rs') where rs' = map (adjust piv) (rs1++rs3) (rs1,rs2) = span leadZero rs leadZero (n:_) = n==0 (piv:rs3) = rs2 -- To find the echelon form of a matrix represented by a list of rows rs: -- -- {first line in definition of echelon}: -- If either the number of rows or the number of columns in the matrix -- is zero (i.e. if null rs || null (head rs)), then the matrix is -- already in echelon form. -- -- {definition of rs1, rs2, leadZero in where clause}: -- Otherwise, split the matrix into two submatrices rs1 and rs2 such that -- rs1 ++ rs2 == rs and all of the rows in rs1 begin with a zero. -- -- {second line in definition of echelon}: -- If rs2 is empty (i.e. if null rs2) then every row begins with a zero -- and the echelon form of rs can be found by adding a zero on to the -- front of each row in the echelon form of (map tail rs). -- -- {Third line in definition of echelon, and definition of piv, rs3}: -- Otherwise, the first row of rs2 (denoted piv) contains a non-zero -- leading coefficient. After moving this row to the top of the matrix -- the original matrix becomes piv:(rs1++rs3). -- By subtracting suitable multiples of piv from (suitable multiples of) -- each row in (rs1++rs3) {see definition of adjust below}, we obtain a -- matrix of the form: -- -- <----- piv ------> -- __________________ -- 0 | -- . | -- . | rs' where rs' = map (adjust piv) (rs1++rs3) -- . | -- 0 | -- -- whose echelon form is piv : map (0:) (echelon rs'). -- adjust :: Num a => Row a -> Row a -> Row a adjust (m:ms) (n:ns) = zipWith (-) (map (n*) ms) (map (m*) ns) -- A more specialised version of this, for matrices of integers, uses the -- greatest common divisor function gcd in an attempt to try and avoid -- result matrices with very large coefficients: -- -- (I'm not sure this is really worth the trouble!) adjust' :: Row Int -> Row Int -> Row Int adjust' (m:ms) (n:ns) = if g==0 then ns else zipWith (\x y -> b*y - a*x) ms ns where g = gcd m n a = n `div` g b = m `div` g -- end!!