-- | Reading and writing vectors as ASCII files. -- We use ASCII so we can generate and check simple test data by hand, -- and we don't want to fool around with byte order issues. -- -- Vector file format is like: -- -- VECTOR -- header -- 100 -- length of vector -- 1.23 1.56 1.23 ... -- data, separated by whitespace -- .... -- -- module Vector ( readVectorFromTextFile , writeVectorAsTextFile , genRandomVector , readValues , hWriteValues , genRandomUArray) where import Data.List as L import Data.Array.Repa as A import qualified Data.Array.Parallel.Unlifted as U import Prelude as P import System.IO import Control.Monad import Data.Char import System.Random -- | Read a vector from a text file. readVectorFromTextFile :: (U.Elt a, Num a, Read a) => FilePath -- ^ File name of vector file. -> IO (Array DIM1 a) readVectorFromTextFile fileName = do handle <- openFile fileName ReadMode "VECTOR" <- hGetLine handle [len] <- liftM (P.map readInt . words) $ hGetLine handle str <- hGetContents handle let vals = readValues str let dims = Z :. len let vec = fromList dims vals return vec readInt :: String -> Int readInt str | and $ P.map isDigit str = read str -- | Write a vector as a text file. writeVectorAsTextFile :: (U.Elt a, Show a) => Array DIM1 a -- ^ Vector to write. -> FilePath -- ^ File name of output file. -> IO () writeVectorAsTextFile arr fileName = do file <- openFile fileName WriteMode hPutStrLn file "VECTOR" let Z :. len = extent arr hPutStrLn file $ show len hWriteValues file $ toList arr hClose file -- | Generate a random(ish) vector. genRandomVector :: DIM2 -> IO (Array DIM2 Double) genRandomVector sh = do uarr <- genRandomUArray (A.size sh) return $ fromUArray sh uarr -- Stuff shared with Matrix module ------------------------------------------------------------- -- | Write out values to a file. hWriteValues :: Show a => Handle -> [a] -- ^ Data values. -> IO () hWriteValues handle xx = go xx where go [] = return () go (x:xs) = do hPutStr handle $ show x hPutStr handle $ "\n" go xs -- | Read a string containing ints separated by whitespace. readValues :: (Num a, Read a) => String -> [a] readValues cs = readValues' [] cs where readValues' _ [] = [] readValues' acc (c : rest) | isSpace c = if null acc then readValues' [] rest else read (reverse acc) : readValues' [] rest | isDigit c || c == '.' || c == 'e' || c == '-' = readValues' (c : acc) rest | otherwise = error $ "unexpected char in Matrix file " ++ show (ord c) -- | Generate a random(ish) UArray of doubles. -- The std random function is too slow to generate really big vectors -- with. Instead, we generate a short random vector and repeat that. genRandomUArray :: Int -> IO (U.Array Double) genRandomUArray n = do let k = 1000 rg <- newStdGen let randvec = U.randomRs k (-100, 100) rg let vec = U.map (\i -> randvec U.!: (i `mod` k)) (U.enumFromTo 0 (n-1)) return vec