X-Git-Url: http://gitweb.michael.orlitzky.com/?a=blobdiff_plain;f=src%2FLinear%2FMatrix.hs;h=66b22f9f9d964e843e596678c0f7ebe34e8e9a22;hb=af25e6f32f6787a747be63093adc10915ad60068;hp=dbd321fefc6e076ade9344495ddfd1c6bd26858c;hpb=c22f72ffaa4edec587b28a7c22aa07330349fd73;p=numerical-analysis.git diff --git a/src/Linear/Matrix.hs b/src/Linear/Matrix.hs index dbd321f..66b22f9 100644 --- a/src/Linear/Matrix.hs +++ b/src/Linear/Matrix.hs @@ -2,6 +2,7 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE NoMonomorphismRestriction #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE RebindableSyntax #-} @@ -17,6 +18,7 @@ where import Data.List (intercalate) import Data.Vector.Fixed ( + (!), N1, N2, N3, @@ -24,6 +26,7 @@ import Data.Vector.Fixed ( N5, S, Z, + generate, mk1, mk2, mk3, @@ -32,6 +35,7 @@ import Data.Vector.Fixed ( ) import qualified Data.Vector.Fixed as V ( and, + foldl, fromList, head, length, @@ -42,7 +46,7 @@ import qualified Data.Vector.Fixed as V ( zipWith ) import Data.Vector.Fixed.Boxed (Vec) -import Data.Vector.Fixed.Internal (Arity, arity) +import Data.Vector.Fixed.Cont (Arity, arity) import Linear.Vector import Normed @@ -197,8 +201,6 @@ symmetric m = -- entries in the matrix. The i,j entry of the resulting matrix will -- have the value returned by lambda i j. -- --- TODO: Don't cheat with fromList. --- -- Examples: -- -- >>> let lambda i j = i + j @@ -207,15 +209,25 @@ symmetric m = -- construct :: forall m n a. (Arity m, Arity n) => (Int -> Int -> a) -> Mat m n a -construct lambda = Mat rows +construct lambda = Mat $ generate make_row where - -- The arity trick is used in Data.Vector.Fixed.length. - imax = (arity (undefined :: m)) - 1 - jmax = (arity (undefined :: n)) - 1 - row' i = V.fromList [ lambda i j | j <- [0..jmax] ] - rows = V.fromList [ row' i | i <- [0..imax] ] + make_row :: Int -> Vec n a + make_row i = generate (lambda i) +-- | Create an identity matrix with the right dimensions. +-- +-- Examples: +-- +-- >>> identity_matrix :: Mat3 Int +-- ((1,0,0),(0,1,0),(0,0,1)) +-- >>> identity_matrix :: Mat3 Double +-- ((1.0,0.0,0.0),(0.0,1.0,0.0),(0.0,0.0,1.0)) +-- +identity_matrix :: (Arity m, Ring.C a) => Mat m m a +identity_matrix = + construct (\i j -> if i == j then (fromInteger 1) else (fromInteger 0)) + -- | Given a positive-definite matrix @m@, computes the -- upper-triangular matrix @r@ with (transpose r)*r == m and all -- values on the diagonal of @r@ positive. @@ -416,9 +428,8 @@ instance (Ring.C a, Arity m, Arity n) => Module.C a (Mat m n a) where instance (Algebraic.C a, ToRational.C a, - Arity m, - Arity n) - => Normed (Mat (S m) (S n) a) where + Arity m) + => Normed (Mat (S m) N1 a) where -- | Generic p-norms. The usual norm in R^n is (norm_p 2). We treat -- all matrices as big vectors. -- @@ -431,7 +442,7 @@ instance (Algebraic.C a, -- 5.0 -- norm_p p (Mat rows) = - (root p') $ sum [(fromRational' $ toRational x)^p' | x <- xs] + (root p') $ sum [fromRational' (toRational x)^p' | x <- xs] where p' = toInteger p xs = concat $ V.toList $ V.map V.toList rows @@ -448,7 +459,31 @@ instance (Algebraic.C a, fromRational' $ toRational $ V.maximum $ V.map V.maximum rows +-- | Compute the Frobenius norm of a matrix. This essentially treats +-- the matrix as one long vector containing all of its entries (in +-- any order, it doesn't matter). +-- +-- Examples: +-- +-- >>> let m = fromList [[1, 2, 3],[4,5,6],[7,8,9]] :: Mat3 Double +-- >>> frobenius_norm m == sqrt 285 +-- True +-- +-- >>> let m = fromList [[1, -1, 1],[-1,1,-1],[1,-1,1]] :: Mat3 Double +-- >>> frobenius_norm m == 3 +-- True +-- +frobenius_norm :: (Algebraic.C a, Ring.C a) => Mat m n a -> a +frobenius_norm (Mat rows) = + sqrt $ vsum $ V.map row_sum rows + where + -- | The \"sum\" function defined in fixed-vector requires a 'Num' + -- constraint whereas we want to use the classes from + -- numeric-prelude. + vsum = V.foldl (+) (fromInteger 0) + -- | Square and add up the entries of a row. + row_sum = vsum . V.map (^2) -- Vector helpers. We want it to be easy to create low-dimension @@ -520,3 +555,93 @@ angle v1 v2 = where theta = (recip norms) NP.* (v1 `dot` v2) norms = (norm v1) NP.* (norm v2) + + + +-- | Given a square @matrix@, return a new matrix of the same size +-- containing only the on-diagonal entries of @matrix@. The +-- off-diagonal entries are set to zero. +-- +-- Examples: +-- +-- >>> let m = fromList [[1,2,3],[4,5,6],[7,8,9]] :: Mat3 Int +-- >>> diagonal_part m +-- ((1,0,0),(0,5,0),(0,0,9)) +-- +diagonal_part :: (Arity m, Ring.C a) + => Mat m m a + -> Mat m m a +diagonal_part matrix = + construct lambda + where + lambda i j = if i == j then matrix !!! (i,j) else 0 + + +-- | Given a square @matrix@, return a new matrix of the same size +-- containing only the on-diagonal and below-diagonal entries of +-- @matrix@. The above-diagonal entries are set to zero. +-- +-- Examples: +-- +-- >>> let m = fromList [[1,2,3],[4,5,6],[7,8,9]] :: Mat3 Int +-- >>> lt_part m +-- ((1,0,0),(4,5,0),(7,8,9)) +-- +lt_part :: (Arity m, Ring.C a) + => Mat m m a + -> Mat m m a +lt_part matrix = + construct lambda + where + lambda i j = if i >= j then matrix !!! (i,j) else 0 + + +-- | Given a square @matrix@, return a new matrix of the same size +-- containing only the below-diagonal entries of @matrix@. The on- +-- and above-diagonal entries are set to zero. +-- +-- Examples: +-- +-- >>> let m = fromList [[1,2,3],[4,5,6],[7,8,9]] :: Mat3 Int +-- >>> lt_part_strict m +-- ((0,0,0),(4,0,0),(7,8,0)) +-- +lt_part_strict :: (Arity m, Ring.C a) + => Mat m m a + -> Mat m m a +lt_part_strict matrix = + construct lambda + where + lambda i j = if i > j then matrix !!! (i,j) else 0 + + +-- | Given a square @matrix@, return a new matrix of the same size +-- containing only the on-diagonal and above-diagonal entries of +-- @matrix@. The below-diagonal entries are set to zero. +-- +-- Examples: +-- +-- >>> let m = fromList [[1,2,3],[4,5,6],[7,8,9]] :: Mat3 Int +-- >>> ut_part m +-- ((1,2,3),(0,5,6),(0,0,9)) +-- +ut_part :: (Arity m, Ring.C a) + => Mat m m a + -> Mat m m a +ut_part = transpose . lt_part . transpose + + +-- | Given a square @matrix@, return a new matrix of the same size +-- containing only the above-diagonal entries of @matrix@. The on- +-- and below-diagonal entries are set to zero. +-- +-- Examples: +-- +-- >>> let m = fromList [[1,2,3],[4,5,6],[7,8,9]] :: Mat3 Int +-- >>> ut_part_strict m +-- ((0,2,3),(0,0,6),(0,0,0)) +-- +ut_part_strict :: (Arity m, Ring.C a) + => Mat m m a + -> Mat m m a +ut_part_strict = transpose . lt_part_strict . transpose