lapack-0.0: src/Numeric/LAPACK/Vector.hs
module Numeric.LAPACK.Vector (
Vector,
fromList,
constant,
dot,
sum,
absSum,
norm1,
norm2,
argAbsMaximum,
scale,
add, sub ,
mac,
mul,
outer,
conjugate,
random, RandomDistribution(..),
) where
import qualified Numeric.LAPACK.Matrix.Shape.Private as MatrixShape
import qualified Numeric.LAPACK.Private as Private
import Numeric.LAPACK.Private (RealOf, zero, one, minusOne, fill)
import qualified Numeric.LAPACK.FFI.Generic as LapackGen
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 Foreign.ForeignPtr (withForeignPtr)
import Foreign.Ptr (Ptr)
import Foreign.Storable (Storable, peek, peekElemOff, pokeElemOff)
import Foreign.C.Types (CInt)
import System.IO.Unsafe (unsafePerformIO)
import Control.Monad.Trans.Cont (ContT(ContT), evalContT)
import Control.Monad.IO.Class (liftIO)
import Control.Applicative (Const(Const,getConst), (<$>))
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 Data.Complex (Complex)
import Data.Word (Word64)
import Data.Bits (shiftR, (.&.))
import Prelude hiding (sum)
type Vector = Array
fromList :: (Shape.C sh, Storable a) => sh -> [a] -> Vector sh a
fromList = Array.fromList
constant :: (Shape.C sh, Storable a, Class.Floating a) => sh -> a -> Vector sh a
constant sh a = Array.unsafeCreate sh $ fill a (Shape.size sh)
newtype Dot sh a = Dot {runDot :: Vector sh a -> Vector sh a -> a}
dot ::
(Shape.C sh, Eq sh, Class.Floating a) =>
Vector sh a -> Vector sh a -> a
dot =
runDot $
Class.switchFloating
(Dot dotReal)
(Dot dotReal)
(Dot dotComplex)
(Dot dotComplex)
dotReal ::
(Shape.C sh, Eq sh, Class.Real a) =>
Vector sh a -> Vector sh a -> a
dotReal (Array shX x) (Array shY y) = unsafePerformIO $ do
Call.assert "dot: shapes mismatch" (shX == shY)
evalContT $ do
nPtr <- Call.cint $ Shape.size shX
sxPtr <- ContT $ withForeignPtr x
incxPtr <- Call.cint 1
syPtr <- ContT $ withForeignPtr y
incyPtr <- Call.cint 1
liftIO $ BlasReal.dot nPtr sxPtr incxPtr syPtr incyPtr
{-
We cannot use 'cdot' because Haskell's FFI
does not support Complex numbers as return values.
-}
dotComplex ::
(Shape.C sh, Eq sh, Class.Real a) =>
Vector sh (Complex a) -> Vector sh (Complex a) -> Complex a
dotComplex (Array shX x) (Array shY y) = unsafePerformIO $ do
Call.assert "dot: shapes mismatch" (shX == shY)
evalContT $ do
transPtr <- Call.char 'N'
mPtr <- Call.cint 1
nPtr <- Call.cint $ Shape.size shX
alphaPtr <- Call.number one
xPtr <- ContT $ withForeignPtr x
ldxPtr <- Call.cint 1
yPtr <- ContT $ withForeignPtr y
incyPtr <- Call.cint 1
betaPtr <- Call.number zero
zPtr <- Call.alloca
inczPtr <- Call.cint 1
liftIO $
BlasGen.gemv
transPtr mPtr nPtr alphaPtr xPtr ldxPtr
yPtr incyPtr betaPtr zPtr inczPtr
liftIO $ peek zPtr
sum :: (Shape.C sh, Class.Floating a) => Vector sh a -> a
sum (Array sh x) = unsafePerformIO $
withForeignPtr x $ \xPtr -> Private.sum (Shape.size sh) xPtr 1
norm1 :: (Shape.C sh, Class.Real a) => Vector sh a -> a
norm1 (Array sh x) = unsafePerformIO $
evalContT $ do
nPtr <- Call.cint $ Shape.size sh
sxPtr <- ContT $ withForeignPtr x
incxPtr <- Call.cint 1
liftIO $ BlasReal.asum nPtr sxPtr incxPtr
{- |
Sum of the absolute values of real numbers or components of complex numbers.
For real numbers it is equivalent to 'norm1'.
-}
absSum :: (Shape.C sh, Class.Floating a) => Vector sh a -> RealOf a
absSum (Array sh x) = unsafePerformIO $
evalContT $ do
nPtr <- Call.cint $ Shape.size sh
sxPtr <- ContT $ withForeignPtr x
incxPtr <- Call.cint 1
liftIO $ asum nPtr sxPtr incxPtr
asum :: Class.Floating a => Ptr CInt -> Ptr a -> Ptr CInt -> IO (RealOf a)
asum =
getNorm $
Class.switchFloating
(Norm BlasReal.asum) (Norm BlasReal.asum)
(Norm BlasComplex.casum) (Norm BlasComplex.casum)
{- |
Euclidean norm of a vector or Frobenius norm of a matrix.
-}
norm2 :: (Shape.C sh, Class.Floating a) => Vector sh a -> RealOf a
norm2 (Array sh x) = unsafePerformIO $
evalContT $ do
nPtr <- Call.cint $ Shape.size sh
sxPtr <- ContT $ withForeignPtr x
incxPtr <- Call.cint 1
liftIO $ nrm2 nPtr sxPtr incxPtr
nrm2 :: Class.Floating a => Ptr CInt -> Ptr a -> Ptr CInt -> IO (RealOf a)
nrm2 =
getNorm $
Class.switchFloating
(Norm BlasReal.nrm2) (Norm BlasReal.nrm2)
(Norm BlasComplex.cnrm2) (Norm BlasComplex.cnrm2)
newtype Norm a =
Norm {getNorm :: Ptr CInt -> Ptr a -> Ptr CInt -> IO (RealOf a)}
{- |
Returns the index and value of the element with the maximal absolute value.
The function does not strictly compare the absolute value of a complex number
but the sum of the absolute complex components.
Caution: It actually returns the value of the element, not its absolute value!
-}
argAbsMaximum ::
(Shape.C sh, Storable a, Class.Floating a) =>
Vector sh a -> (Shape.Index sh, a)
argAbsMaximum (Array sh x) = unsafePerformIO $
evalContT $ do
nPtr <- Call.cint $ Shape.size sh
sxPtr <- ContT $ withForeignPtr x
incxPtr <- Call.cint 1
liftIO $ do
k <- fromIntegral . subtract 1 <$> BlasGen.iamax nPtr sxPtr incxPtr
xmax <- peekElemOff sxPtr k
return (Shape.indices sh !! k, xmax)
scale, _scale ::
(Shape.C sh, Storable a, Class.Floating a) =>
a -> Vector sh a -> Vector sh a
scale alpha (Array sh x) = Array.unsafeCreate sh $ \syPtr -> do
evalContT $ do
alphaPtr <- Call.number alpha
nPtr <- Call.cint $ Shape.size sh
sxPtr <- ContT $ withForeignPtr x
incxPtr <- Call.cint 1
incyPtr <- Call.cint 1
liftIO $ BlasGen.copy nPtr sxPtr incxPtr syPtr incyPtr
liftIO $ BlasGen.scal nPtr alphaPtr syPtr incyPtr
_scale a (Array sh b) = Array.unsafeCreate sh $ \cPtr -> do
let m = 1
let k = 1
let n = Shape.size sh
evalContT $ do
transaPtr <- Call.char 'N'
transbPtr <- Call.char 'N'
mPtr <- Call.cint m
kPtr <- Call.cint k
nPtr <- Call.cint n
alphaPtr <- Call.number one
aPtr <- Call.number a
ldaPtr <- Call.cint m
bPtr <- ContT $ withForeignPtr b
ldbPtr <- Call.cint k
betaPtr <- Call.number zero
ldcPtr <- Call.cint m
liftIO $
BlasGen.gemm
transaPtr transbPtr mPtr nPtr kPtr alphaPtr
aPtr ldaPtr bPtr ldbPtr betaPtr cPtr ldcPtr
add, sub ::
(Shape.C sh, Eq sh, Storable a, Class.Floating a) =>
Vector sh a -> Vector sh a -> Vector sh a
add = mac one
sub x y = mac minusOne y x
mac ::
(Shape.C sh, Eq sh, Storable a, Class.Floating a) =>
a -> Vector sh a -> Vector sh a -> Vector sh a
mac alpha (Array shX x) (Array shY y) = Array.unsafeCreate shX $ \szPtr -> do
Call.assert "mac: shapes mismatch" (shX == shY)
evalContT $ do
nPtr <- Call.cint $ Shape.size shX
saPtr <- Call.number alpha
sxPtr <- ContT $ withForeignPtr x
incxPtr <- Call.cint 1
syPtr <- ContT $ withForeignPtr y
incyPtr <- Call.cint 1
inczPtr <- Call.cint 1
liftIO $ BlasGen.copy nPtr syPtr incyPtr szPtr inczPtr
liftIO $ BlasGen.axpy nPtr saPtr sxPtr incxPtr szPtr inczPtr
mul ::
(Shape.C sh, Eq sh, Storable a, Class.Floating a) =>
Vector sh a -> Vector sh a -> Vector sh a
mul (Array shA a) (Array shX x) = Array.unsafeCreate shX $ \yPtr -> do
Call.assert "mul: shapes mismatch" (shA == shX)
let n = Shape.size shX
evalContT $ do
transPtr <- Call.char 'N'
mPtr <- Call.cint n
nPtr <- Call.cint n
klPtr <- Call.cint 0
kuPtr <- Call.cint 0
alphaPtr <- Call.number one
aPtr <- ContT $ withForeignPtr a
ldaPtr <- Call.cint 1
xPtr <- ContT $ withForeignPtr x
incxPtr <- Call.cint 1
betaPtr <- Call.number zero
incyPtr <- Call.cint 1
liftIO $
BlasGen.gbmv transPtr
mPtr nPtr klPtr kuPtr alphaPtr aPtr ldaPtr
xPtr incxPtr betaPtr yPtr incyPtr
outer ::
(Shape.C shx, Eq shx, Shape.C shy, Eq shy,
Storable a, Class.Floating a) =>
Vector shx a -> Vector shy a -> Array (MatrixShape.General shx shy) a
outer (Array shX x) (Array shY y) =
Array.unsafeCreate (MatrixShape.General MatrixShape.ColumnMajor shX shY) $
\cPtr -> do
let m = Shape.size shX
let n = Shape.size shY
evalContT $ do
transaPtr <- Call.char 'N'
transbPtr <- Call.char 'N'
mPtr <- Call.cint m
nPtr <- Call.cint n
kPtr <- Call.cint 1
alphaPtr <- Call.number one
aPtr <- ContT $ withForeignPtr x
ldaPtr <- Call.cint m
bPtr <- ContT $ withForeignPtr y
ldbPtr <- Call.cint 1
betaPtr <- Call.number zero
ldcPtr <- Call.cint m
liftIO $
BlasGen.gemm
transaPtr transbPtr mPtr nPtr kPtr alphaPtr
aPtr ldaPtr bPtr ldbPtr betaPtr cPtr ldcPtr
newtype Conjugate sh a = Conjugate {getConjugate :: Vector sh a -> Vector sh a}
conjugate ::
(Shape.C sh, Storable a, Class.Floating a) =>
Vector sh a -> Vector sh a
conjugate =
getConjugate $
Class.switchFloating
(Conjugate id)
(Conjugate id)
(Conjugate complexConjugate)
(Conjugate complexConjugate)
complexConjugate ::
(Shape.C sh, Storable a, Class.Real a) =>
Vector sh (Complex a) -> Vector sh (Complex a)
complexConjugate (Array sh x) = Array.unsafeCreate sh $ \syPtr ->
evalContT $ do
nPtr <- Call.cint $ Shape.size sh
sxPtr <- ContT $ withForeignPtr x
incxPtr <- Call.cint 1
incyPtr <- Call.cint 1
liftIO $ do
BlasGen.copy nPtr sxPtr incxPtr syPtr incyPtr
LapackComplex.lacgv nPtr syPtr incyPtr
data RandomDistribution =
UniformBox01
| UniformBoxPM1
| Normal
| UniformDisc
| UniformCircle
deriving (Eq, Ord, Show, Enum)
{-
@random distribution shape seed@
Only the least significant 47 bits of @seed@ are used.
-}
random ::
(Shape.C sh, Storable a, Class.Floating a) =>
RandomDistribution -> sh -> Word64 -> Vector sh a
random dist sh seed = Array.unsafeCreate sh $ \xPtr ->
evalContT $ do
nPtr <- Call.cint $ Shape.size sh
distPtr <-
Call.cint $
case (getConst $ isComplexInFunctor xPtr, dist) of
(_, UniformBox01) -> 1
(_, UniformBoxPM1) -> 2
(_, Normal) -> 3
(True, UniformDisc) -> 4
(True, UniformCircle) -> 5
(False, UniformDisc) -> 2
(False, UniformCircle) ->
error
"Vector.random: UniformCircle not supported for real numbers"
iseedPtr <- Call.allocaArray 4
liftIO $ do
pokeElemOff iseedPtr 0 $ fromIntegral ((seed `shiftR` 35) .&. 0xFFF)
pokeElemOff iseedPtr 1 $ fromIntegral ((seed `shiftR` 23) .&. 0xFFF)
pokeElemOff iseedPtr 2 $ fromIntegral ((seed `shiftR` 11) .&. 0xFFF)
pokeElemOff iseedPtr 3 $ fromIntegral ((seed.&.0x7FF)*2+1)
LapackGen.larnv distPtr iseedPtr nPtr xPtr
isComplexInFunctor :: (Class.Floating a) => f a -> Const Bool a
isComplexInFunctor _ =
Class.switchFloating (Const False) (Const False) (Const True) (Const True)