packages feed

lapack-0.1: src/Numeric/LAPACK/Matrix/Shape/Private.hs

{-# LANGUAGE TypeFamilies #-}
module Numeric.LAPACK.Matrix.Shape.Private where

import qualified Data.Array.Comfort.Shape as Shape

import Control.Applicative (Const(Const, getConst))

import Data.Functor.Identity (Identity(Identity), runIdentity)
import Data.List (tails)
import Data.Tuple.HT (swap)


data Order = RowMajor | ColumnMajor
   deriving (Eq, Show)

flipOrder :: Order -> Order
flipOrder RowMajor = ColumnMajor
flipOrder ColumnMajor = RowMajor

transposeFromOrder :: Order -> Char
transposeFromOrder RowMajor = 'T'
transposeFromOrder ColumnMajor = 'N'


type family HeightOf shape
type family WidthOf shape


data General height width =
   General {
      generalOrder :: Order,
      generalHeight :: height,
      generalWidth :: width
   } deriving (Eq, Show)

type instance HeightOf (General height width) = height
type instance WidthOf (General height width) = width

instance (Shape.C height, Shape.C width) => Shape.C (General height width) where
   type Index (General height width) = (Shape.Index height, Shape.Index width)
   indices (General _ height width) = Shape.indices (height,width)

   offset (General RowMajor height width) =
      Shape.offset (height,width)
   offset (General ColumnMajor height width) =
      Shape.offset (width,height) . swap
   uncheckedOffset (General RowMajor height width) =
      Shape.uncheckedOffset (height,width)
   uncheckedOffset (General ColumnMajor height width) =
      Shape.uncheckedOffset (width,height) . swap

   sizeOffset (General RowMajor height width) =
      Shape.sizeOffset (height,width)
   sizeOffset (General ColumnMajor height width) =
      Shape.sizeOffset (width,height) . swap
   uncheckedSizeOffset (General RowMajor height width) =
      Shape.uncheckedSizeOffset (height,width)
   uncheckedSizeOffset (General ColumnMajor height width) =
      Shape.uncheckedSizeOffset (width,height) . swap

   inBounds (General _ height width) = Shape.inBounds (height,width)
   size (General _ height width) = Shape.size (height,width)
   uncheckedSize (General _ height width) = Shape.uncheckedSize (height,width)


transpose :: General height width -> General width height
transpose (General order height width) = General (flipOrder order) width height

dimensions ::
   (Shape.C height, Shape.C width) => General height width -> (Int, Int)
dimensions (General order height width) =
   case order of
      RowMajor -> (Shape.size width, Shape.size height)
      ColumnMajor -> (Shape.size height, Shape.size width)


data Square size =
   Square {
      squareOrder :: Order,
      squareSize :: size
   } deriving (Eq, Show)

type instance HeightOf (Square size) = size
type instance WidthOf (Square size) = size

generalFromSquare :: Square size -> General size size
generalFromSquare (Square order sh) = General order sh sh

transposeSquare :: Square sh -> Square sh
transposeSquare (Square order size) = Square (flipOrder order) size

instance (Shape.C size) => Shape.C (Square size) where
   type Index (Square size) = (Shape.Index size, Shape.Index size)
   indices (Square _ size) = Shape.indices (size,size)

   offset (Square RowMajor size) =
      Shape.offset (size,size)
   offset (Square ColumnMajor size) =
      Shape.offset (size,size) . swap
   uncheckedOffset (Square RowMajor size) =
      Shape.uncheckedOffset (size,size)
   uncheckedOffset (Square ColumnMajor size) =
      Shape.uncheckedOffset (size,size) . swap

   sizeOffset (Square RowMajor size) =
      Shape.sizeOffset (size,size)
   sizeOffset (Square ColumnMajor size) =
      Shape.sizeOffset (size,size) . swap
   uncheckedSizeOffset (Square RowMajor size) =
      Shape.uncheckedSizeOffset (size,size)
   uncheckedSizeOffset (Square ColumnMajor size) =
      Shape.uncheckedSizeOffset (size,size) . swap

   inBounds (Square _ size) = Shape.inBounds (size,size)
   size (Square _ size) = Shape.size (size,size)
   uncheckedSize (Square _ size) = Shape.uncheckedSize (size,size)


data Householder height width =
   Householder {
      householderOrder :: Order,
      householderHeight :: height,
      householderWidth :: width
   } deriving (Eq, Show)

type instance HeightOf (Householder height width) = height
type instance WidthOf (Householder height width) = width

data Reflector = Reflector deriving (Eq)
data Triangle = Triangle deriving (Eq)

householderPart ::
   (Shape.C height, Shape.C width) =>
   Householder height width ->
   (Shape.Index height, Shape.Index width) -> Either Reflector Triangle
householderPart (Householder _ height width) (r,c) =
   if Shape.offset height r > Shape.offset width c
     then Left Reflector
     else Right Triangle

instance
   (Shape.C height, Shape.C width) =>
      Shape.C (Householder height width) where

   type Index (Householder height width) =
            (Either Reflector Triangle,
             (Shape.Index height, Shape.Index width))

   indices sh@(Householder _ height width) =
      map (\ix -> (householderPart sh ix, ix)) $
      Shape.indices (height,width)

   offset sh@(Householder order height width) (part,ix) =
      if part == householderPart sh ix
        then
            case order of
               RowMajor -> Shape.offset (height,width) ix
               ColumnMajor -> Shape.offset (width,height) (swap ix)
        else error "Shape.Householder.offset: wrong matrix part"
   uncheckedOffset (Householder RowMajor height width) =
      Shape.uncheckedOffset (height,width) . snd
   uncheckedOffset (Householder ColumnMajor height width) =
      Shape.uncheckedOffset (width,height) . swap . snd

   sizeOffset sh@(Householder order height width) (part,ix) =
      if part == householderPart sh ix
        then
            case order of
               RowMajor -> Shape.sizeOffset (height,width) ix
               ColumnMajor -> Shape.sizeOffset (width,height) (swap ix)
        else error "Shape.Householder.sizeOffset: wrong matrix part"
   uncheckedSizeOffset (Householder RowMajor height width) =
      Shape.uncheckedSizeOffset (height,width) . snd
   uncheckedSizeOffset (Householder ColumnMajor height width) =
      Shape.uncheckedSizeOffset (width,height) . swap . snd

   size (Householder _ height width) = Shape.size (height,width)
   uncheckedSize (Householder _ height width) =
      Shape.uncheckedSize (height,width)
   inBounds sh@(Householder _ height width) (part,ix) =
      Shape.inBounds (height,width) ix
      &&
      part == householderPart sh ix


{- |
Store the upper triangular half of a real symmetric or complex Hermitian matrix.
-}
data Hermitian size =
   Hermitian {
      hermitianOrder :: Order,
      hermitianSize :: size
   } deriving (Eq, Show)

type instance HeightOf (Hermitian size) = size
type instance WidthOf (Hermitian size) = size

uploFromOrder :: Order -> Char
uploFromOrder RowMajor = 'L'
uploFromOrder ColumnMajor = 'U'

instance (Shape.C size) => Shape.C (Hermitian size) where
   type Index (Hermitian size) = (Shape.Index size, Shape.Index size)

   indices (Hermitian _ size) =
      let ixs = Shape.indices size
      in  concat $ zipWith (\r cs -> map ((,) r) cs) ixs $ tails ixs

   uncheckedOffset sh ix =
      snd $ Shape.uncheckedSizeOffset sh ix

   sizeOffset sh ix =
      if Shape.inBounds sh ix
        then Shape.uncheckedSizeOffset sh ix
        else error "Shape.Hermitian.sizeOffset: wrong matrix part"

   uncheckedSizeOffset (Hermitian RowMajor size) (rs,cs) =
      let (s,r) = Shape.uncheckedSizeOffset size rs
          c = Shape.uncheckedOffset size cs
      in  (s, triangleSize s - triangleSize (s-r) + c-r)
   uncheckedSizeOffset (Hermitian ColumnMajor size) (rs,cs) =
      let (s,r) = Shape.uncheckedSizeOffset size rs
          c = Shape.uncheckedOffset size cs
      in  (s, triangleSize c + r)

   size (Hermitian _ size) = triangleSize $ Shape.size size
   uncheckedSize (Hermitian _ size) = triangleSize $ Shape.uncheckedSize size
   inBounds (Hermitian _ size) ix@(r,c) =
      Shape.inBounds (size,size) ix
      &&
      Shape.offset size r <= Shape.offset size c


data Triangular uplo size =
   Triangular {
      triangularUplo :: uplo,
      triangularOrder :: Order,
      triangularSize :: size
   } deriving (Eq, Show)

type instance HeightOf (Triangular uplo size) = size
type instance WidthOf (Triangular uplo size) = size

data Lower = Lower deriving (Eq, Show)
data Upper = Upper deriving (Eq, Show)

type LowerTriangular = Triangular Lower
type UpperTriangular = Triangular Upper

triangularTransposeUp :: LowerTriangular sh -> UpperTriangular sh
triangularTransposeUp (Triangular Lower order size) =
   Triangular Upper (flipOrder order) size

triangularTransposeDown :: UpperTriangular sh -> LowerTriangular sh
triangularTransposeDown (Triangular Upper order size) =
   Triangular Lower (flipOrder order) size


class Uplo uplo where switchUplo :: f Lower -> f Upper -> f uplo
instance Uplo Lower where switchUplo f _ = f
instance Uplo Upper where switchUplo _ f = f

autoUplo :: Uplo uplo => uplo
autoUplo = runIdentity $ switchUplo (Identity Lower) (Identity Upper)

uploOrder :: Uplo uplo => uplo -> Order -> Order
uploOrder uplo order = caseUplo uplo (flipOrder order) order

getUploConst :: uplo -> Const a uplo -> a
getUploConst _ = getConst

caseUplo :: Uplo uplo => uplo -> a -> a -> a
caseUplo uplo lo up =
   getUploConst uplo $ switchUplo (Const lo) (Const up)

instance (Uplo uplo, Shape.C size) => Shape.C (Triangular uplo size) where
   type Index (Triangular uplo size) = (Shape.Index size, Shape.Index size)

   indices (Triangular uplo _ size) =
      let ixs = Shape.indices size
          rcs = concat $ zipWith (\r cs -> map ((,) r) cs) ixs $ tails ixs
      in  caseUplo uplo (map swap rcs) rcs

   uncheckedOffset sh ix =
      snd $ Shape.uncheckedSizeOffset sh ix

   sizeOffset sh ix =
      if Shape.inBounds sh ix
        then Shape.uncheckedSizeOffset sh ix
        else error "Shape.Triangular.sizeOffset: wrong matrix part"

   uncheckedSizeOffset (Triangular uplo RowMajor size) (rs,cs) =
      let (s,r) = Shape.uncheckedSizeOffset size rs
          c = Shape.uncheckedOffset size cs
      in  (s,
           caseUplo uplo
               (triangleSize r + c)
               (triangleSize s - triangleSize (s-r) + c-r))
   uncheckedSizeOffset (Triangular uplo ColumnMajor size) (rs,cs) =
      let (s,r) = Shape.uncheckedSizeOffset size rs
          c = Shape.uncheckedOffset size cs
      in  (s,
           caseUplo uplo
               (triangleSize s - triangleSize (s-c) + r-c)
               (triangleSize c + r))

   size (Triangular _ _ size) = triangleSize $ Shape.size size
   uncheckedSize (Triangular _ _ size) = triangleSize $ Shape.uncheckedSize size
   inBounds (Triangular uplo _ size) ix@(r,c) =
      Shape.inBounds (size,size) ix
      &&
      caseUplo uplo
         (Shape.offset size r >= Shape.offset size c)
         (Shape.offset size r <= Shape.offset size c)

triangleSize :: Int -> Int
triangleSize n = div (n*(n+1)) 2

triangleRoot :: Floating a => a -> a
triangleRoot size = (sqrt (8*size+1)-1)/2

triangleExtent :: String -> Int -> Int
triangleExtent name size =
   let n = round (triangleRoot (fromIntegral size :: Double))
   in if size == triangleSize n
        then n
        else error (name ++ ": no triangular number of elements")