packages feed

accelerate-examples-0.2.0.0: tests/simple/SMVM/MatrixMarket.hs

{-# LANGUAGE OverloadedStrings, GADTs, StandaloneDeriving #-}

module SMVM.MatrixMarket (Matrix(..), readMatrix) where

import Control.Applicative                      hiding (many)

import Data.Complex
import Data.Attoparsec.Char8                    hiding (parse, Result(..))
import Data.Attoparsec.Lazy                     (parse, Result(..))
import Data.ByteString.Lex.Double
import qualified Data.ByteString.Lazy           as L


-- | Specifies the element type.  Pattern matrices do not have any elements,
-- only indices, and only make sense for coordinate matrices and vectors.
--
data Field  = Real | Complex | Integer | Pattern
    deriving (Eq, Show)

-- | Specifies either sparse or dense storage.  In sparse (\"coordinate\")
-- storage, elements are given in (i,j,x) triplets for matrices (or (i,x) for
-- vectors).  Indices are 1-based, so that A(1,1) is the first element of a
-- matrix, and x(1) is the first element of a vector.
--
-- In dense (\"array\") storage, elements are given in column-major order.
--
-- In both cases, each element is given on a separate line.
--
data Format = Coordinate | Array 
    deriving (Eq, Show)

-- | Specifies any special structure in the matrix.  For symmetric and hermition
-- matrices, only the lower-triangular part of the matrix is given. For skew
-- matrices, only the entries below the diagonal are stored.
--
data Structure = General | Symmetric | Hermitian | Skew 
    deriving (Eq, Show)


-- We really want a type parameter to Matrix, but I think that requires some
-- kind of dynamic typing so that we can determine (a ~ Integral) or (a ~
-- RealFloat), and so forth, depending on the file being read. This will do for
-- our purposes...
--
-- Format is: (rows,columns) nnz [(row,column,value)]
--
data Matrix where
  PatternMatrix :: (Int,Int) -> Int -> [(Int,Int)]               -> Matrix
  IntMatrix     :: (Int,Int) -> Int -> [(Int,Int,Int)]           -> Matrix
  RealMatrix    :: (Int,Int) -> Int -> [(Int,Int,Float)]         -> Matrix
  ComplexMatrix :: (Int,Int) -> Int -> [(Int,Int,Complex Float)] -> Matrix

deriving instance Show Matrix


--------------------------------------------------------------------------------
-- Combinators
--------------------------------------------------------------------------------

comment :: Parser ()
comment = char '%' *> skipWhile (not . eol) *> endOfLine
  where eol w = w `elem` "\n\r"

floating :: Fractional a => Parser a
floating = do
  mv <- readDouble <$> (skipSpace *> takeTill isSpace)  -- readDouble does the fancy stuff
  case mv of
       Just (v,_) -> return . realToFrac $ v
       Nothing    -> fail "floating-point number"

integral :: Integral a => Parser a
integral = skipSpace *> decimal

format :: Parser Format
format =  string "coordinate" *> pure Coordinate
      <|> string "array"      *> pure Array
      <?> "matrix format"

field :: Parser Field
field =  string "real"    *> pure Real
     <|> string "complex" *> pure Complex
     <|> string "integer" *> pure Integer
     <|> string "pattern" *> pure Pattern
     <?> "matrix field"

structure :: Parser Structure
structure =  string "general"        *> pure General
         <|> string "symmetric"      *> pure Symmetric
         <|> string "hermitian"      *> pure Hermitian
         <|> string "skew-symmetric" *> pure Skew
         <?> "matrix structure"

header :: Parser (Format,Field,Structure)
header =  string "%%MatrixMarket matrix"
       >> (,,) <$> (skipSpace *> format)
               <*> (skipSpace *> field)
               <*> (skipSpace *> structure)
               <*  endOfLine
               <?> "MatrixMarket header"

extent :: Parser (Int,Int,Int)
extent = do
  [m,n,l] <- skipWhile isSpace *> count 3 integral <* endOfLine
  return (m,n,l)

line :: Parser a -> Parser (Int,Int,a)
line f = (,,) <$> integral
              <*> integral
              <*> f
              <*  endOfLine

--------------------------------------------------------------------------------
-- Matrix Market
--------------------------------------------------------------------------------

matrix :: Parser Matrix
matrix = do
  (_,t,_) <- header
  (m,n,l) <- skipMany comment *> extent
  case t of
    Real    -> RealMatrix    (m,n) l `fmap` many (line floating)
    Complex -> ComplexMatrix (m,n) l `fmap` many (line ((:+) <$> floating <*> floating))
    Integer -> IntMatrix     (m,n) l `fmap` many (line integral)
    Pattern -> PatternMatrix (m,n) l `fmap` many ((,) <$> integral <*> integral)


readMatrix :: FilePath -> IO Matrix
readMatrix file = do
  chunks <- L.readFile file
  case parse matrix chunks of
       Fail _ _ msg -> error $ file ++ ": " ++ msg
       Done _ mtx   -> return mtx