lapack-0.1: src/Numeric/LAPACK/Matrix/Hermitian.hs
{-# LANGUAGE TypeFamilies #-}
module Numeric.LAPACK.Matrix.Hermitian (
Hermitian,
fromList,
autoFromList,
identity,
diagonal,
getDiagonal,
multiplyVector,
square,
multiplySquareLeft,
multiplyGeneralLeft,
multiplySquareRight,
multiplyGeneralRight,
outer,
sumRank1,
sumRank2,
toSquare,
covariance,
addTransposed,
) where
import qualified Numeric.LAPACK.Matrix.Shape.Private as MatrixShape
import Numeric.LAPACK.Matrix.Triangular.Private
(forPointers, pack, unpack, unpackToTemp,
diagonalPointers, rowMajorPointers, columnMajorPointers)
import Numeric.LAPACK.Matrix.Shape.Private
(Order(RowMajor,ColumnMajor), flipOrder, uploFromOrder)
import Numeric.LAPACK.Matrix.Square (Square)
import Numeric.LAPACK.Matrix.Private (General, ZeroInt, zeroInt)
import Numeric.LAPACK.Vector (Vector)
import Numeric.LAPACK.Private
(RealOf, fill, zero, one, lacgv, fromReal, realPart, copyToTemp)
import qualified Numeric.LAPACK.FFI.Complex as LapackComplex
import qualified Numeric.BLAS.FFI.Generic as BlasGen
import qualified Numeric.BLAS.FFI.Complex as BlasComplex
import qualified Numeric.BLAS.FFI.Real as BlasReal
import qualified Numeric.Netlib.Utility as Call
import qualified Numeric.Netlib.Class as Class
import qualified Data.Array.Comfort.Storable.Internal as Array
import qualified Data.Array.Comfort.Shape as Shape
import Data.Array.Comfort.Storable.Internal (Array(Array))
import Foreign.C.Types (CInt, CChar)
import Foreign.ForeignPtr (ForeignPtr, withForeignPtr)
import Foreign.Ptr (Ptr)
import Foreign.Storable (Storable, poke, peek)
import Control.Monad.Trans.Cont (ContT(ContT), evalContT)
import Control.Monad.IO.Class (liftIO)
import Control.Monad (when)
import qualified Data.NonEmpty as NonEmpty
import Data.Foldable (forM_)
import Data.Complex (Complex)
type Hermitian sh = Array (MatrixShape.Hermitian sh)
fromList :: (Shape.C sh, Storable a) => Order -> sh -> [a] -> Hermitian sh a
fromList order sh =
Array.fromList (MatrixShape.Hermitian order sh)
autoFromList :: (Storable a) => Order -> [a] -> Hermitian ZeroInt a
autoFromList order xs =
fromList order
(zeroInt $ MatrixShape.triangleExtent "Hermitian.autoFromList" $
length xs)
xs
identity :: (Shape.C sh, Class.Floating a) => Order -> sh -> Hermitian sh a
identity order sh =
Array.unsafeCreateWithSize (MatrixShape.Hermitian order sh) $
\triSize aPtr -> do
fill zero triSize aPtr
mapM_ (flip poke one . snd) $
diagonalPointers order (Shape.size sh) aPtr aPtr
diagonal ::
(Shape.C sh, Class.Floating a) =>
Order -> Vector sh (RealOf a) -> Hermitian sh a
diagonal order =
runDiagonal $
Class.switchFloating
(Diagonal $ diagonalAux order) (Diagonal $ diagonalAux order)
(Diagonal $ diagonalAux order) (Diagonal $ diagonalAux order)
newtype Diagonal sh a =
Diagonal {runDiagonal :: Vector sh (RealOf a) -> Hermitian sh a}
diagonalAux ::
(Shape.C sh, Class.Floating a, RealOf a ~ ar, Storable ar) =>
Order -> Vector sh ar -> Hermitian sh a
diagonalAux order (Array sh x) =
Array.unsafeCreateWithSize (MatrixShape.Hermitian order sh) $
\triSize aPtr -> do
fill zero triSize aPtr
withForeignPtr x $ \xPtr ->
forM_ (diagonalPointers order (Shape.size sh) xPtr aPtr) $
\(srcPtr,dstPtr) -> poke dstPtr . fromReal =<< peek srcPtr
getDiagonal ::
(Shape.C sh, Class.Floating a) =>
Hermitian sh a -> Vector sh (RealOf a)
getDiagonal =
runGetDiagonal $
Class.switchFloating
(GetDiagonal $ getDiagonalAux) (GetDiagonal $ getDiagonalAux)
(GetDiagonal $ getDiagonalAux) (GetDiagonal $ getDiagonalAux)
newtype GetDiagonal sh a =
GetDiagonal {runGetDiagonal :: Hermitian sh a -> Vector sh (RealOf a)}
getDiagonalAux ::
(Shape.C sh, Class.Floating a, RealOf a ~ ar, Storable ar) =>
Hermitian sh a -> Vector sh ar
getDiagonalAux (Array (MatrixShape.Hermitian order sh) a) =
Array.unsafeCreateWithSize sh $ \n xPtr ->
withForeignPtr a $ \aPtr ->
forM_ (diagonalPointers order n xPtr aPtr) $
\(dstPtr,srcPtr) -> poke dstPtr . realPart =<< peek srcPtr
multiplyVector ::
(Shape.C sh, Eq sh, Class.Floating a) =>
Hermitian sh a -> Vector sh a -> Vector sh a
multiplyVector (Array (MatrixShape.Hermitian order shA) a) (Array shX x) =
Array.unsafeCreateWithSize shX $ \n yPtr -> do
Call.assert "Hermitian.multiplyVector: width shapes mismatch" (shA == shX)
evalContT $ do
uploPtr <- Call.char $ uploFromOrder order
nPtr <- Call.cint n
alphaPtr <- Call.number one
aPtr <- ContT $ withForeignPtr a
xPtr <- ContT $ withForeignPtr x
incxPtr <- Call.cint 1
betaPtr <- Call.number zero
incyPtr <- Call.cint 1
liftIO $
BlasGen.hpmv
uploPtr nPtr alphaPtr aPtr xPtr incxPtr betaPtr yPtr incyPtr
square ::
(Shape.C sh, Eq sh, Class.Floating a) =>
Hermitian sh a -> Hermitian sh a
square
(Array shape@(MatrixShape.Hermitian order sh) a) =
Array.unsafeCreate shape $ \cpPtr -> do
let n = Shape.size sh
evalContT $ do
sidePtr <- Call.char 'L'
uploPtr <- Call.char 'U'
nPtr <- Call.cint n
let ldPtr = nPtr
bPtr <- unpackToTemp (unpackFull order) n a
cPtr <- Call.allocaArray (n*n)
alphaPtr <- Call.number one
betaPtr <- Call.number zero
liftIO $ do
BlasGen.hemm sidePtr uploPtr
nPtr nPtr alphaPtr bPtr ldPtr
bPtr ldPtr betaPtr cPtr ldPtr
pack order n cPtr cpPtr
multiplySquareLeft ::
(Shape.C sh, Eq sh, Class.Floating a) =>
Square sh a -> Hermitian sh a -> Square sh a
multiplySquareLeft
(Array shapeB@(MatrixShape.Square orderB shB) b)
(Array (MatrixShape.Hermitian orderA shA) a) =
Array.unsafeCreate shapeB $ \cPtr -> do
Call.assert "Hermitian.multiplySquareLeft: shapes mismatch" (shA == shB)
let n = Shape.size shB
multiplyAux True orderA n a (flipOrder orderB) n b cPtr
multiplyGeneralLeft ::
(Shape.C height, Shape.C width, Eq width, Class.Floating a) =>
General height width a -> Hermitian width a -> General height width a
multiplyGeneralLeft
(Array shapeB@(MatrixShape.General orderB height width) b)
(Array (MatrixShape.Hermitian orderA shA) a) =
Array.unsafeCreate shapeB $ \cPtr -> do
Call.assert "Hermitian.multiplyGeneralLeft: shapes mismatch" (shA == width)
multiplyAux True
orderA (Shape.size width) a (flipOrder orderB) (Shape.size height) b cPtr
multiplySquareRight ::
(Shape.C sh, Eq sh, Class.Floating a) =>
Hermitian sh a -> Square sh a -> Square sh a
multiplySquareRight
(Array (MatrixShape.Hermitian orderA shA) a)
(Array shapeB@(MatrixShape.Square orderB shB) b) =
Array.unsafeCreate shapeB $ \cPtr -> do
Call.assert "Hermitian.multiplySquareRight: shapes mismatch" (shA == shB)
let n = Shape.size shB
multiplyAux False orderA n a orderB n b cPtr
multiplyGeneralRight ::
(Shape.C height, Eq height, Shape.C width, Class.Floating a) =>
Hermitian height a -> General height width a -> General height width a
multiplyGeneralRight
(Array (MatrixShape.Hermitian orderA shA) a)
(Array shapeB@(MatrixShape.General orderB height width) b) =
Array.unsafeCreate shapeB $ \cPtr -> do
Call.assert "Hermitian.multiplyGeneralRight: shapes mismatch" (shA == height)
multiplyAux False
orderA (Shape.size height) a orderB (Shape.size width) b cPtr
multiplyAux ::
Class.Floating a =>
Bool ->
Order -> Int -> ForeignPtr a ->
Order -> Int -> ForeignPtr a -> Ptr a -> IO ()
multiplyAux extraConjugate orderA m0 a orderB n0 b cPtr = do
let size = m0*m0
evalContT $ do
let (side,(m,n)) =
case orderB of
ColumnMajor -> ('L',(m0,n0))
RowMajor -> ('R',(n0,m0))
sidePtr <- Call.char side
uploPtr <- Call.char $ uploFromOrder orderA
mPtr <- Call.cint m
nPtr <- Call.cint n
alphaPtr <- Call.number one
aPtr <- unpackToTemp (unpack orderA) m0 a
ldaPtr <- Call.cint m0
incaPtr <- Call.cint 1
sizePtr <- Call.cint size
bPtr <- ContT $ withForeignPtr b
ldbPtr <- Call.cint m
betaPtr <- Call.number zero
ldcPtr <- Call.cint m
liftIO $ do
when ((orderA/=orderB) /= extraConjugate) $
lacgv sizePtr aPtr incaPtr
BlasGen.hemm sidePtr uploPtr
mPtr nPtr alphaPtr aPtr ldaPtr
bPtr ldbPtr betaPtr cPtr ldcPtr
outer :: (Shape.C sh, Class.Floating a) => Vector sh a -> Hermitian sh a
outer =
getMap $
Class.switchFloating
(Map outerAux) (Map outerAux)
(Map outerAux) (Map outerAux)
outerAux ::
(Shape.C sh, Class.Floating a, RealOf a ~ ar, Class.Real ar) =>
Vector sh a -> Hermitian sh a
outerAux (Array sh x) =
Array.unsafeCreateWithSize (MatrixShape.Hermitian ColumnMajor sh) $
\triSize aPtr -> do
let n = Shape.size sh
evalContT $ do
uploPtr <- Call.char $ uploFromOrder ColumnMajor
nPtr <- Call.cint n
alphaPtr <- Call.real one
xPtr <- ContT $ withForeignPtr x
incxPtr <- Call.cint 1
liftIO $ fill zero triSize aPtr
liftIO $ hpr uploPtr nPtr alphaPtr xPtr incxPtr aPtr
sumRank1 ::
(Shape.C sh, Eq sh, Class.Floating a) =>
NonEmpty.T [] (RealOf a, Vector sh a) -> Hermitian sh a
sumRank1 =
getSumRank1 $
Class.switchFloating
(SumRank1 sumRank1Aux) (SumRank1 sumRank1Aux)
(SumRank1 sumRank1Aux) (SumRank1 sumRank1Aux)
type SumRank1_ sh a = NonEmpty.T [] (RealOf a, Vector sh a) -> Hermitian sh a
newtype SumRank1 sh a = SumRank1 {getSumRank1 :: SumRank1_ sh a}
sumRank1Aux ::
(Shape.C sh, Eq sh, Class.Floating a, RealOf a ~ ar, Class.Real ar) =>
SumRank1_ sh a
sumRank1Aux xs@(NonEmpty.Cons (_, Array sh _) _) =
Array.unsafeCreateWithSize (MatrixShape.Hermitian ColumnMajor sh) $
\triSize aPtr -> do
let n = Shape.size sh
evalContT $ do
uploPtr <- Call.char $ uploFromOrder ColumnMajor
nPtr <- Call.cint n
alphaPtr <- Call.alloca
incxPtr <- Call.cint 1
liftIO $ do
fill zero triSize aPtr
forM_ xs $ \(alpha, Array shX x) ->
withForeignPtr x $ \xPtr -> do
Call.assert
"Hermitian.sumRank1: non-matching vector size" (sh==shX)
poke alphaPtr alpha
hpr uploPtr nPtr alphaPtr xPtr incxPtr aPtr
type HPR_ a =
Ptr CChar -> Ptr CInt ->
Ptr (RealOf a) -> Ptr a -> Ptr CInt -> Ptr a -> IO ()
newtype HPR a = HPR {getHPR :: HPR_ a}
hpr :: Class.Floating a => HPR_ a
hpr =
getHPR $
Class.switchFloating
(HPR BlasReal.spr) (HPR BlasReal.spr)
(HPR BlasComplex.hpr) (HPR BlasComplex.hpr)
sumRank2 ::
(Shape.C sh, Eq sh, Class.Floating a) =>
NonEmpty.T [] (a, (Vector sh a, Vector sh a)) -> Hermitian sh a
sumRank2 xys@(NonEmpty.Cons (_, (Array sh _, _)) _) =
Array.unsafeCreateWithSize (MatrixShape.Hermitian ColumnMajor sh) $
\triSize aPtr -> do
let n = Shape.size sh
evalContT $ do
uploPtr <- Call.char $ uploFromOrder ColumnMajor
nPtr <- Call.cint n
alphaPtr <- Call.alloca
incPtr <- Call.cint 1
liftIO $ do
fill zero triSize aPtr
forM_ xys $ \(alpha, (Array shX x, Array shY y)) ->
withForeignPtr x $ \xPtr ->
withForeignPtr y $ \yPtr -> do
Call.assert
"Hermitian.sumRank2: non-matching x vector size" (sh==shX)
Call.assert
"Hermitian.sumRank2: non-matching y vector size" (sh==shY)
poke alphaPtr alpha
BlasGen.hpr2 uploPtr nPtr alphaPtr xPtr incPtr yPtr incPtr aPtr
{-
It is not strictly necessary to keep the 'order'.
It would be neither more complicated nor less efficient
to change the order via the conversion.
-}
toSquare, _toSquare ::
(Shape.C sh, Class.Floating a) => Hermitian sh a -> Square sh a
_toSquare (Array (MatrixShape.Hermitian order sh) a) =
Array.unsafeCreate (MatrixShape.Square order sh) $ \bPtr ->
evalContT $ do
let n = Shape.size sh
aPtr <- ContT $ withForeignPtr a
conjPtr <- conjugateToTemp (MatrixShape.triangleSize n) a
liftIO $ do
unpack (flipOrder order) n conjPtr bPtr -- wrong
unpack order n aPtr bPtr
toSquare (Array (MatrixShape.Hermitian order sh) a) =
Array.unsafeCreate (MatrixShape.Square order sh) $ \bPtr ->
withForeignPtr a $ \aPtr ->
unpackFull order (Shape.size sh) aPtr bPtr
{- |
Make a temporary copy only for complex matrices.
-}
conjugateToTemp ::
(Class.Floating a) => Int -> ForeignPtr a -> ContT r IO (Ptr a)
conjugateToTemp n =
runCopyToTemp $
Class.switchFloating
(CopyToTemp $ ContT . withForeignPtr)
(CopyToTemp $ ContT . withForeignPtr)
(CopyToTemp $ complexConjugateToTemp n)
(CopyToTemp $ complexConjugateToTemp n)
newtype CopyToTemp r a =
CopyToTemp {runCopyToTemp :: ForeignPtr a -> ContT r IO (Ptr a)}
complexConjugateToTemp ::
Class.Real a =>
Int -> ForeignPtr (Complex a) -> ContT r IO (Ptr (Complex a))
complexConjugateToTemp n x = do
nPtr <- Call.cint n
xPtr <- copyToTemp n x
incxPtr <- Call.cint 1
liftIO $ LapackComplex.lacgv nPtr xPtr incxPtr
return xPtr
{- |
A^H * A
-}
covariance ::
(Shape.C height, Shape.C width, Class.Floating a) =>
General height width a -> Hermitian width a
covariance =
getMap $
Class.switchFloating
(Map covarianceAux) (Map covarianceAux)
(Map covarianceAux) (Map covarianceAux)
newtype Map f g a = Map {getMap :: f a -> g a}
covarianceAux ::
(Shape.C height, Shape.C width,
Class.Floating a, RealOf a ~ ar, Class.Real ar) =>
General height width a -> Hermitian width a
covarianceAux (Array (MatrixShape.General order height width) a) =
Array.unsafeCreate (MatrixShape.Hermitian order width) $ \bPtr -> do
let n = Shape.size width
let k = Shape.size height
evalContT $ do
nPtr <- Call.cint n
kPtr <- Call.cint k
alphaPtr <- Call.number one
aPtr <- ContT $ withForeignPtr a
betaPtr <- Call.number zero
cPtr <- Call.allocaArray (n*n)
ldcPtr <- Call.cint n
case order of
ColumnMajor -> do
uploPtr <- Call.char 'U'
transPtr <- Call.char 'C'
ldaPtr <- Call.cint k
liftIO $ do
herk uploPtr transPtr
nPtr kPtr alphaPtr aPtr ldaPtr betaPtr cPtr ldcPtr
pack ColumnMajor n cPtr bPtr
RowMajor -> do
uploPtr <- Call.char 'L'
transPtr <- Call.char 'N'
ldaPtr <- Call.cint n
liftIO $ do
herk uploPtr transPtr
nPtr kPtr alphaPtr aPtr ldaPtr betaPtr cPtr ldcPtr
pack RowMajor n cPtr bPtr
type HERK_ a =
Ptr CChar -> Ptr CChar -> Ptr CInt -> Ptr CInt -> Ptr (RealOf a) -> Ptr a ->
Ptr CInt -> Ptr (RealOf a) -> Ptr a -> Ptr CInt -> IO ()
newtype HERK a = HERK {getHERK :: HERK_ a}
herk :: Class.Floating a => HERK_ a
herk =
getHERK $
Class.switchFloating
(HERK BlasReal.syrk)
(HERK BlasReal.syrk)
(HERK BlasComplex.herk)
(HERK BlasComplex.herk)
{- |
A^H + A
-}
addTransposed, _addTransposed ::
(Shape.C sh, Class.Floating a) => Square sh a -> Hermitian sh a
_addTransposed (Array (MatrixShape.Square order sh) a) =
Array.unsafeCreateWithSize (MatrixShape.Hermitian order sh) $ \bSize bPtr -> do
let n = Shape.size sh
evalContT $ do
alphaPtr <- Call.number one
incxPtr <- Call.cint 1
aPtr <- ContT $ withForeignPtr a
sizePtr <- Call.cint bSize
conjPtr <- Call.allocaArray bSize
liftIO $ do
pack order n aPtr bPtr
pack (flipOrder order) n aPtr conjPtr -- wrong
lacgv sizePtr conjPtr incxPtr
BlasGen.axpy sizePtr alphaPtr conjPtr incxPtr bPtr incxPtr
addTransposed (Array (MatrixShape.Square order sh) a) =
Array.unsafeCreate (MatrixShape.Hermitian order sh) $ \bPtr -> do
let n = Shape.size sh
evalContT $ do
alphaPtr <- Call.number one
incxPtr <- Call.cint 1
incnPtr <- Call.cint n
aPtr <- ContT $ withForeignPtr a
liftIO $ case order of
RowMajor ->
forPointers (rowMajorPointers n aPtr bPtr) $
\nPtr (srcPtr,dstPtr) -> do
BlasGen.copy nPtr srcPtr incnPtr dstPtr incxPtr
lacgv nPtr dstPtr incxPtr
BlasGen.axpy nPtr alphaPtr srcPtr incxPtr dstPtr incxPtr
ColumnMajor ->
forPointers (columnMajorPointers n aPtr bPtr) $
\nPtr ((srcRowPtr,srcColumnPtr),dstPtr) -> do
BlasGen.copy nPtr srcRowPtr incnPtr dstPtr incxPtr
lacgv nPtr dstPtr incxPtr
BlasGen.axpy nPtr alphaPtr srcColumnPtr incxPtr dstPtr incxPtr
unpackFull :: Class.Floating a => Order -> Int -> Ptr a -> Ptr a -> IO ()
unpackFull order n packedPtr fullPtr = evalContT $ do
incxPtr <- Call.cint 1
incyPtr <- Call.cint n
liftIO $ case order of
RowMajor ->
forPointers (rowMajorPointers n fullPtr packedPtr) $
\nPtr (dstPtr,srcPtr) -> do
BlasGen.copy nPtr srcPtr incxPtr dstPtr incyPtr
lacgv nPtr dstPtr incyPtr
BlasGen.copy nPtr srcPtr incxPtr dstPtr incxPtr
ColumnMajor ->
forPointers (columnMajorPointers n fullPtr packedPtr) $
\nPtr ((dstRowPtr,dstColumnPtr),srcPtr) -> do
BlasGen.copy nPtr srcPtr incxPtr dstRowPtr incyPtr
lacgv nPtr dstRowPtr incyPtr
BlasGen.copy nPtr srcPtr incxPtr dstColumnPtr incxPtr
_pack :: Class.Floating a => Order -> Int -> Ptr a -> Ptr a -> IO ()
_pack order n fullPtr packedPtr =
evalContT $ do
incxPtr <- Call.cint 1
liftIO $
case order of
ColumnMajor ->
forPointers (columnMajorPointers n fullPtr packedPtr) $
\nPtr ((_,srcPtr),dstPtr) ->
BlasGen.copy nPtr srcPtr incxPtr dstPtr incxPtr
RowMajor ->
forPointers (rowMajorPointers n fullPtr packedPtr) $
\nPtr (srcPtr,dstPtr) ->
BlasGen.copy nPtr srcPtr incxPtr dstPtr incxPtr